Every software working with data or databases has the aspect of system security. ACL is the term given to the part of the system which implements this security aspect and it stands for Access Control List. You can also call it: who can do what.
Through my years of working / implementing software systems with security aspects, I’ve never found any of the ACL implementations out there the way I wanted them to be. The list starts from operating systems like Windows and Linux to libraries like Spring and Zend Framework. They all lack something and please don’t get me wrong, I’m not saying that they are not secure enough. I mean, well how hard should it be to just say no when someone asks you for something? Because that’s all security is about! To explain what’s wrong with all the ACL implementations that I’ve seen, let me break down ACL for you.
In many of the implementations, ACL is composed of three ingredients; User, Action and Resource. In these implementations, ACL is responsible to know if the User is allowed to do the Action on the specified Resource. In these implementations, you have a three-dimensional matrix with Users on one axis and Actions and Resources on the other two. So when you specify the User, Action and Resource, the matrix element specified with those three coordinates is holding a boolean indicating whether the Action is granted or not.
The first problem with these implementations is exactly at this very spot. You see, because we crossed Actions with Resources, we are explicitly saying that the exact same actions can be done on all of the resources within the system. And this is not always true. Such implementations usually consider four actions: Create, Read, Update and Delete (a.k.a. CRUD) for all the resources. Even though this might be true in most of the times, but it is definitely not true as a statement. For instance, I might want to implement a Search action and it is only available for some of the resources within my system (not every resource can be searched) and I want to grant it to my users separately from the read access. Or there might be even resources that can not be Deleted at all (like Resource itself – yeah resource definition is a Resource too!). Anyways, I think crossing Actions with Resources and uniforming the matrix is certainly meaningless. That’s why I prefer to merge the two concepts into one and call them ActionResource. For example, you can have a CreateUser, ReadUser, UpdateUser and DeleteUser but no SearchUser ActionResource. But for some other entity within your system, you might want to have SearchEntity as well. Now we have a 2D matrix while each of our resources are free to have as many actions as they like.
Now our ACL implementation has only two dimensions, Users and ActionResources. It’s a 2D grid, easier to implement and more flexible (everything that a software engineer is after). But this implementation can be cumbersome for the user to work with. While the number of users in a system is usually manageable, ActionResources are not. Having only 10 entities in your system and considering an average of four actions for each, it makes a total of 40 ActionResources which means you have to set 200 checkboxes if you only have 5 users in your software! I think you would agree if I say; this is not acceptable. In computers, whenever a list becomes too long to handle, one way to deal with it is to group list items so we can deal with them in bulk. In this solution, the number of groups is smaller than the number of items so it makes sense to deal with them instead. But even in some cases, the number of groups could be too big to handle and even though features like searching them could help, one can’t help but think of grouping the groups! Of course, this can go on and on with arbitrary levels of grouping. In fact, this need is so popular in computer science that it has a name, it’s called a tree!
So what I am proposing here is that whenever we have a long list to work with, the best way to go about it is to group them using a tree. Each node in the tree would be a group that can hold other groups, as well as list items. This is a flexible design which is also easy for the user to interact with since we are already familiar with the concept of a tree in GUI (we’ve been using folder tree to organize our hard disks since Windows 3, at least that’s as far as my experience goes).On the other hand, trees are one of the major data structures in computer science and thoroughly studied. And they provide you with a ton of features.
Now we have a tree of ActionResources, that I like to call a Resource Tree, which we are going to cross it over with our list of users to grant or reject access to. Since the list of users in a software is usually manageable (unless we are dealing with a big organization) one would put a stop on it right here which is just fine.
But there’s another characteristic to a user list which it does not share with ActionResource list and I would like to expand on that. While resource tree is something static and can only be changed either by a developer or some administrator (if we implement an interface for it), a user list is not so by any means. User list has a dynamic characteristic to it which makes it different from resource tree. Users come and go, and each time a new user is introduced to the system, we need to reset the permissions for him/her. Since we came up with the tree thingie for ActionReources, this chore is not that tedious like it was before, also we are talking about security here and thus human error (in this case, user error) can lead to catastrophic consequences. This is why some ACL implementations came up with the idea of user groups or roles to eliminate this human error.
All the ACL implementations that I know have a grouping measure for users, while this for sure can be handy to handle long lists of users, but even if your system has a limited number of users, groups can still be helpful. Compared to the user list, user groups are more static over the time. Which means you can grant access to groups and revise them less frequently, resulting in fewer mistakes. Now all you need to do is to specify which groups the user belongs to. In operating systems, user groups are flat lists but as we’ve mentioned before trees have got more features than lists. On the other hand, roles (which are basically user groups) in most of ACL libraries are already implemented using a tree (like Zend Framework). The thing here for using a tree over a list is that it gives you the sense of supervision hierarchy. Roles with higher levels have power over their subordinates. In terms of ACL, roles with higher levels can do all the things their subordinated can do. This is intuitive, in any organization any boss should be able to overrule his/her subordinates. And a role tree implements that perfectly.
In conclusion, we ended up with a Resource Tree, a Role Tree and a User List. Resources are associated to roles (through permission grid) and roles are given to users (through role tree). I’ve personally implemented this design a couple of times by now and one of the criticisms that I’ve faced was that users sometimes would like to be able to grant some permission directly to a user regardless of which role he/she has. Personally, I managed not to give in to such a request. Even though it might sound like a valid requirement but believe me it isn’t! Of course, there could be more advanced implementations helping the user to achieve this specific request but all of them will still obey the exact same design – Resource Tree, Role Tree and User List. For instance, one can implement an auto role generator system where the user sets his/her expectations on who can do what and then the system comes up with a role tree that supports that. It might sound overkill but in a long run, it is worth it.