Fake binary trees
... rants, ramblings and occasional good idea ...

Access control based security

Natural question to ask after previous post is: that's all fine and dandy but how do you combine this with access control list (ACL) based security?

First, let's explain the issue here: What I refer to as 'ACL based security' is defining permissions (access rights) for individual resources, similar to the way operating systems allow access to file system. E.g. user 'xy' can see all tasks for projects he manages, but also all other tasks in other projects where their managers have allowed access to 'xy', or tasks which are assigned to 'xy'. This changes our imaginary security API from HasPermission(user, permission) to HasPermissionFor(user, permission, object)

Although security is usually not considered a business logic, the line starts to get blurry here. In my opinion, this is both a business and infrastructure concept.

One possible solution, which unfortunatelly can be seen too often, is to retrieve data as usual and then throw away resources which don't match the permissions. This approach fails miserably in many aspects: performance, filtering, paging, etc.

I am not sure if it is even possible to create a 'one size fits all' solution for this problem. However, in most systems that I had to deal with, following solution was able to get me quite far.

Note: I presume that the infrastructure is already set up: additional database tables store ACL entries which define who is allowed or denied access to individual resources, so that queries which retrieve items from the database can join on these tables. This is not a trivial thing and can become quite complex, especially when you take into account resource hierarchies (e.g. project-task), but it is out of scope of this post.

Anyway, suppose that we have ITaskManagementService which exposes following method:

[RequiresPermission(Permission.Edit)]
void GetTasks( ... parameters...)
{
    // m_taskRepository is an instance of ISecureRepository<T>
    m_taskRepository.GetAll(); 
}

Service has SecurityInterceptor implemented through Windsor/DP2, which checks for RequiresPermission attribute and do something like:

class SecurityInterceptor : IMethodInterceptor
{
    public object Intercept(IMethodInvocation invocation, params object[] args)
    {
            CallContext[Context.Security] = new SecurityContext(CurrentUser, attr.Permission);         
            return invocation.Proceed(args);
    }
}

than in the SecureRepository implementation, permissions are added to the query:

public IList<T> GetAll()
{
    ICriteria criteria = BuildCriteria();
    return criteria.List<T>();
}

public void BuildCriteria<T>()
{
    ICriteria criteria = Session.CreateCriteria(typeof(T));
    SecurityContext security = CallContext[Context.Security];
    // now we modify criteria to join entity tables with ACL tables...
    AddPermissions(criteria, security.UserId, security.RequiredPermissions);
}

This will make sure that GetAll() method returns only those tasks for which the caller has sufficient permissions. The drawback of the solution is that it only works if the resources and permissions are stored in tables in the same database so you can join them, but this usually isn't an issue for most small to medium solution.

Posted at 18:01 on February 6, 2008
Categories: .NET | Software Design   E-mail | del.icio.us | Permalink | Comments (0) | Post RSSRSS comment feed
Comments are closed