9

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.

6

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.

2

I think your option 3 is the closest, but instead of interrogating the user and subject about their permission sets you should pass the user and subject to the access controller.

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

The access controller should be responsible for both getting the permission sets and checking if the access is sufficient. That way you isolate both the storage logic and the checking logic in your access controller, separate from both user and subject.

The other element possibly missing from your example is what operation is being performed. Some users may have the right to read some data but not to update, delete, execute, etc. So you might have a checkAccess method on the access controller with three parameters: user, subject, operation. You might also like to provide some additional information back from checkAccess to return information about why access wasn't granted.

For example, delegating all this to the access controller later allows you to replace the way your permissions are represented. You might start with role-based access control and move to claims-based later. You might store permissions in a simple structure to begin with and later add hierarchical groups/roles and permissable operations on different types of subjects. Not putting your permission sets into the interface helps to enable this.

Whether you use AOP or some boilerplate code to plug this in is, in my opinion, less important.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.