Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NHibernate: Change query for authorization

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?

  • Interceptors (too late, retrieves 1 million objects when only 5 are required).
  • Events (IPostLoadEvent is the closest to what I need, but it's too late as well).
  • A custom NhQueryable and DefaultQueryProvider. This throws an exception: "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.
  • The OnPrepareStatement Interceptor. This is incredibly ugly. It allows me to edit the SQL, but all I get is a string. This would work, but I'm hoping there's a much more elegant way to do the same thing.

How do others handle their authorization code? How do you make sure nobody forgets to call the authorization checks?

like image 788
Sander Avatar asked Dec 04 '25 16:12

Sander


1 Answers

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.

like image 51
gurra777 Avatar answered Dec 06 '25 05:12

gurra777