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):
- Query the
subject
with a list of permissions that theuser
has –subject.allowAccess(user.getPermissionSet)
- Query the
user
with a list of permissions that thesubject
requires –user.hasPermissionTo(subject.getRequiredPermissions())
- Query a third-party to locate the intersections of permissions –
accessController.doPermissionSetsIntersect(subject.permissionSet, user.getPermissionSet())
- Query either the
subject
/user
, while delegating the "decision" to a third-party class - Have the
user
attempt to access thesubject
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 ofsubject.display()
ever know that access control is in effect? (wheresubject.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.