Access control design patterns

The Java EE model is pretty much on the lines of 2. Your code runs in a "Container", you tell the container about your interface entry points (URLs for servlets, methods for EJBs) and define the roles that can use these entry points. An adminstrator maps the authentication info (eg. LDAP user and groups) to specific roles and the container consults that mapping in granting access to the entry points.

The key here is that the Container "knows" about your code, it's effectively a quite clever proxy.

In the absence of a container I'd be looking at the proxy approach, perhaps using some kind of Aspect Oriented technique.

I think you're right that option 3 is very brittle,too much responsibility on the client programmers.


Another solution could be a little variant of your 1.

ex.

class Service
{
  var $ACL = //some hash map with acl
}

class Companies extends Service
{

  function getCompanyById($id)
  {
    //real code
  }
}

class SafeCompanies extends Companies
{
//If a method must be "protected" with an ACL, you must override them in this way
  function getCompanyById($id)
  {
    $this->check('read'); //raise an exception if current user haven't READ privilege
    parent::getCompanyById($id);    
  }  
} 

in this way you dont mix responsibilities and still can use polymorphism

my 2 cents


Ten years later... The world has evolved quite a bit since and in particular a whole new paradigm has come up: externalized authorization. To be fair, each and every development framework has its own version of it (e.g. CanCanCan in Ruby or Spring Security in Java or Claims-based authorization in C#). Externalized authorization aims to decouple authorization logic from business logic. The idea is that authorization needs might evolve independently of business logic. For instance your business logic provides access to bank accounts (view / edit / delete / transfer). The functionality is stable - it won't change in the near future. However, the authorization needs might evolve because of legislation (GDPR, Open Banking...) or different requirements (delegation, parent-child, VIP...) This is why you want to maintain authorization externally.

To do so, there is a model called attribute-based access control (abac) which is an evolution / extension to the more well-known role-based access control (rbac). In RBAC, access control is identity-centric. It is based on the user, the role, and the group(s) the user belongs to. That's not enough often times. In ABAC, you can use attributes of the user, resource, context (time), and action. ABAC also lets you write policies in plain old English using standardized policy languages (xacml or alfa or Rego). The cloud platforms (AWS and Google) have also implemented their own language (Called Google IAM and AWS IAM respectively).

Some of the benefits of ABAC are:

  • flexibility: you can change your authorization policies without touching your apps / APIs
  • reusability: you can apply the same policies to data, APIs, apps, or infrastructure.
  • visibility: express authorization as policies rather than hard-code the logic which means you can easily audit the policies and understand what is possible / what isn't
  • auditability: with ABAC you get a single log of all access that was granted or denied.

If you want to learn more, check out the Wikipedia pages for ABAC and ALFA as well as NIST's page on ABAC.