Design – Standard practices for access control (design pattern)

access-controldesignsingle-responsibility

I'm looking at my interface design and I am struggling to decide which is the most "correct" way to implement role-based access control, given a user and a subject that the user would like to access.


As far as I can see I have three core options (with a fourth being a bastardisation of the first three and a fifth being a tweak of the fourth):

  1. Query the subject with a list of permissions that the user has – subject.allowAccess(user.getPermissionSet)
  2. Query the user with a list of permissions that the subject requires – user.hasPermissionTo(subject.getRequiredPermissions())
  3. Query a third-party to locate the intersections of permissions – accessController.doPermissionSetsIntersect(subject.permissionSet, user.getPermissionSet())
  4. Query either the subject/user, while delegating the "decision" to a third-party class
  5. Have the user attempt to access the subject and throw an error if access is not permitted

I am leaning toward option four – Have the subject contain an accessController field, where calls to subject.userMayAccess(User user) delegate the operation a la:

class Subject {
    public function display(user) {
        if(!accessController.doPermissionSetsIntersect(this.permissionSet, user.getPermissionSet())) {
            display403(); //Or other.. eg, throw an error..
        }
    }
}

.. but then this raises further questions:

  • should the accessController be a field vs a static class..?
  • Should a subject know what permissions are required to be able to view it?
  • where does the principle of least knowledge come into play here, with respect to calling subject.display()? Should callers of subject.display() ever know that access control is in effect? (where subject.display() is a final "template method")
  • have subject.display() manage access control, throwing an exception where the user does not have the required permission?

What would be considered "best practice" in this situation? Where should responsibility for performing the checks actually occur?

As this is somewhat both an academic excercise which will then progress into implementation, references to design patterns would be appreciated.

Best Answer

The best practice is to use something known as the Interceptor pattern to intercept calls to protected areas.

This can be achieved by use of AOP or cross cutting concerns applied to your access entry points.

The subject should never know about who is able to view it. This complicates the subject code unnecessarily and there is no reason for it to be required, unless you inadvertently provide a direct access mechanism to the same function.

Preferably the caller and the callee should not know about access, apart from handling rejections. However, the issue will be dependant on the system you are implementing on and how you gain access to the security credentials/principal for the caller. For example, in SOAP systems this information is added onto the header of a SOAP message, whereas in a windows system it would be available via the windows authentication mechanism.

If you use the AOP or interceptor pattern approach it would throw any necessary exceptions, and it would be up to the client (caller) to handle any exceptions thrown.

In this way you keep your client, service, and authentication code separate with no intermingling of knowledge or functionality.

Related Topic