We are completely rewriting an existing product (and in the process changing from PHP to C#). One of the problems we encountered in our existing product is that of authorization: unless you make sure to check it always and everywhere, people get to see stuff they're not allowed to see. What's the best place to make sure it's always checked? I think the data layer is a great place to do this, but other suggestions are welcome.
We use NHibernate, so we've built a class that provides our entities. It looks like this:
class DataProvider {
TEntity Get<TEntity>(int id) {
return GetSession().Get<TEntity>(id);
}
IQueryable<TEntity> Query<TEntity>()
{
return GetSession().Query<TEntity>();
}
// ...
}
This allows me to call DataProvider.Get<Person>(1) or do LINQ queries using from person in DataProvider.Query<Person>() select person. Using Interceptors would allow me to deny access to any single object a user has no authorization for (though arguably I could insert that code in DataProvider.Get). It becomes troublesome when I try to use LINQ. Suppose I have a table with 1 million entities, but I only have access to 5 entities. Using Interceptors would retrieve the entire table and test each of the returned entities, but I only need 5 of them. The best thing I can imagine is injecting some custom SQL to tell NHibernate which objects to retrieve and ignore all others. We already have a system in place that can determine whether or not we have access to an object, let's assume we know how to make a query out of that as well.
What have I tried?
The constant for 'MyQueryable<Person>' is not supported", I think this is due to casting somewhere deep inside NHibernate. I doubt NHibernate was designed to allow a custom query provider or queryable.How do others handle their authorization code? How do you make sure nobody forgets to call the authorization checks?
I think you should take a look at NHibernate filters.
Nice intro by Ayende here: http://ayende.com/blog/3993/nhibernate-filters
We successfully used NH filters for a scenario, similar to yours, a few years ago. I've attached the essence of the filter below. In our case, all classes that we wanted to control access to inherited from a base class, "SecuredObject". We also had a notion of "Documents" in "Folders". It's kind of taken out of context here but perhaps it can give you some ideas.
<filter name="GrantedSecuredObjectsOnly" condition="
...
Id IN
(SELECT SecurityGrant.SecuredObjectId FROM SecurityGrant WHERE SecurityGrant.SecuredObjectConsumerId=:userId)) OR
(Id IN (SELECT Document.Id FROM Document WHERE Document.FolderId IN (SELECT SecurityGrant.SecuredObjectId FROM SecurityGrant WHERE SecurityGrant.SecuredObjectConsumerId=:userId))
...
"/>
userId is of course set to the currently logged on users ID.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With