When discussing security, it's usually about confidentiality, authorization and a tiny bit of access controls (usually declarative). Things like logging (to detect when things go wrong), authenticating transactions instead of sessions (for extra confirmation that the users is who (s)he says (s)he is), etc. are usually left out. I'm going to discuss access controls here.
For access controls, most documentation you'll find (even today in 2008) is role based, and declarative. Declarative means you don't have to program it, and that it can be enforced by your application server. This is a huge gain, as the same code is used for all access controls, and thus the code is likely to be bug free (or very nearly so).
However, these role based access controls also have a big part missing: the roles are global for the entire application. This means that it is not possible to allow access only to your objects without some serious undermining of the security design.
You see, the only way to circumvent the fact that roles are global, is to define roles like
read dossiers of your organization,
read dossiers for which you are responsible,
etc. This effectively adds an extra security layer for each relationship there is between a user and access
to a record. You can see where this quickly gets out of hand, and how easy it is to introduce bugs.
But wait: hasn't this problem been solved before? Of course it has! You get the same problems when viewing files on a multi-user computer/server. So, you devise structures for Access Control Lists (ACLs), and... you'll quickly find even they are inadequate for the task. The main reasons for this are:
- The authorization required to create a record usually depends on the object type. It may also depend on the parent object, but usually there is none.
- Authorizations must also be applied when searching: search results may not include records you don't have access to.
Using Access Control Lists effectively
The solution lies in implementing a dual structure: an ACL that protects objects, and that also doubles as a home interface (or rather a modern equivalent as EJB3 has come out).
Step one is to name ACL's, preferably using an Enum that defines which actions the code supports. This is not a bad thing, because an ACL is an abstract class anyway: the relationships that the ACL recognizes depend on the data model (code) already. So you have an abstract ACL, and subclasses to handle for example users and dossiers.
Step two is to name the relationships I just mentioned. Also for this, an Enum is best, as the relationships are not free-form. They depend on the referenced objects in the data model. With these two additions/clarifications on basic ACL's, we've defined a structure that can determine if access to a specific (collection of) object(s) is allowed, and whether a specific type of object can be created.
The trick at this point is the aforementioned home interface: it decides how objects are searched, and is also used to create them.
You see, it's not that smart to implement searching outside the model. If you do, you only make searching easier initially. You have also removed the possibility of seamlessly enforce access controls – you'll have to duplicate this functionality everywhere you use the model. This dramatically increases the possible locations for bugs, and you must remember to implement it again when a new access path to the data is implemented. And on top of that, the searching code becomes more difficult to maintain. So for a short, initial speed gain you've sentenced yourself to a lot more work. This is the reason people say programming is like sex: one mistake and you have to support it for life.
Implementing searching and object creation inside the model can be done in (stateless) session beans. This scales quite well, but it's main advantage is that the model itself can enforce security now. Especially if you use the EJB3 interceptor feature to enforce ACL's on the business logic, because you can then name the ACL to be applied in an annotation.
Using Access Control Lists in the real world
For those of you who shake their head wondering if I'm really that far removed from reality, don't. I realize that in the real world it frequently occurs that the model is not in complete control. Especially at the start of a project, managers want to see something quickly. Combine that with the way agile methodologies are applied these days, and you'll likely find yourself implementing and expanding a prototype. You'll even put it into production.
In this evolved prototype, you've probably built pages with functionality more than a good data model. The latter is after all often seen as an acadmic exercise. So while officially you'll use a Model-View-Controller design with a controller as gateway between the model and the page, in reality the model is so closely tied to the controller that the controller is actually a part of the model. This is because the controllers does the searching, applies business logic, etc. Not that strange once you realize that instead of specifying business logic, a customer usually specifies what they want the program to do and how they want to interact with it.
pragmatic (i.e. sloppy) cases like this, your goal is to ensure that all access controls are
evaluated consistently and preferably in the same place. Some pointers:
- If your controller framework supports interceptors, like for example Stripes does, you can use this instead of using EJB3 interceptors to enforce access controls. This at least ensures that there is less code to do this, and it's more likely to be (nearly) bug free.
- If not, you can at least formalize the way you retrieve the ACL to apply. The only tricky bit is that you (and your colleages!) must consistently use it and enforce it's access control decisions.
- For searching, you can let the ACL class define a filter method to filter out record the user may not access. It's not as efficient as a session bean that can decide on specialized queries, but it still works. And, it's a whole lot less bug prone and costly during maintenance than doing these specialized searches in your controller – see item 2 of formalizing how you apply ACL's. Spreading functionality around is a sure way to introduce bugs, as even geniusses have their bad days.
Of course, you can only apply ACL's when you have a user. Using the existing structures however, is easy as pie:
There already is a user object. In Java, this is a Principal (obtained via
A user is already associated with roles. In Java, you can test membership via
- By interpreting the roles as user groups, you can apply ACL's based on the user, it's group memberships and it's relationship to the protected object.