Question

I'm working on a PHP application, and I'd like to add access control to some of my objects. I didn't tag this question as PHP, as I feel this question is not language specific.

Say I have a 'Service class'

abstract class Service {


}

Many services use this as a baseclass. One pseudo example would be:

class Companies extends Service {

  function getCompanyInfo($id) {
      //...
  }

}

Later down the road I want to add access control. The example 'getCompanyInfoById' method is a 'read' operation, so this would require a 'read' privilege.

At this point I can implement this in the following way:

  1. Add accesscontrol to the Service class. Every method (such as getCompanyInfoById) must call the 'hasPrivilege' method internally before completing the operation and returning the result.
  2. Wrap all Service objects in a some kind of Proxy object that will checks privileges before calling the method in the inner-object.
  3. Completely separate access control, and force the 'caller' to check privileges before calling the method.

Cons for every option:

  1. This requires changing all Services, and requires them to be aware of Access control. I feel this goes against Separation of concerns.
  2. This breaks OOP features, such as Polymorphism. The caller no longer knows what interfaces any service supports.
  3. This is the most flexible, but the big drawback is that checking permission is now implicit. Developers can 'forget' or complex codepaths can cause unauthorized services to be called.

Are there better ways to approach this altogether?

Was it helpful?

Solution

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

OTHER TIPS

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.

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 () which is an evolution / extension to the more well-known role-based access control (). 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 ( or 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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top