Authorization with Zend_Acl in Zend Framework
We now have a way to check if a user is who he/she says he/she is. Next we need to stop certain users from accessing certain parts of the application. To do this, we are going to use the Zend_Acl component
ACL (Access Control List) lets us create a list that contains all the rules for accessing our system. In Zend_Acl, this list works like a tree enabling us to inherit from rule to rule, building up a fi ne-grained access control system.
There are two main concepts at work in Zend_Acl—Resources and Roles. A Resource is something that needs to be accessed and a Role is the thing that is trying to access the Resource. To have access to a resource, you need to have the correct Role.
To start us off, let’s fi rst look at a basic example. In this example, we are going to use the scenario of a data centre. In the data centre, we need to control access to the server room. Only people with the correct permissions will be able to access the server room.
To start, we need to create some Roles and Resources.
$visitor = new Zend_Acl_Role('Visitor');
$admin = new Zend_Acl_Role('Admin');
$serverRoom = new Zend_Acl_Resource('ServerRoom');
Here we have created two Roles—Visitor and Admin and one Resource—ServerRoom. Next, we need to create the Access Control List.
$acl = new Zend_Acl();
Here we instantiate a new Zend_Acl instance and add the two Roles and one new access rule. When we add the Roles, we make the Admin Role inherit from the Visitor Role. This means that Admin inherits all the access rules of the Visitor. We also add one new Rule containing the ServerRoom resource.
At this point, access to the server room is denied for both Visitors and Admins. We can change this by adding allow or deny rules:
- Allow all to all resources: $acl->allow();
- Deny all to all resources: $acl->deny();
- Allow Admin and Deny Visitor to all resources: $acl->allow($admin);
- Allow Admin and Deny Visitor to ServerRoom resource: $acl->allow($admin, $serverRoom);
When adding rules, we can also set permissions. These can be used to deny/allow access to parts of a Resource. For example, we may allow visitors to view the server room but not access the cabinets. To do this, we can add extra permission options to our rules.
Allow Visitor and Admin to view the ServerRoom, Deny Visitor cabinet access:
$acl->allow(visitor, $serverRoom, array('view'));
$acl->deny($visitor, $serverRoom, array('cabinet'));
Here we simply add the new permissions as an array containing the strings of the permissions we want to add to the ServerRoom resource.
Next we need to query the ACL. This is done through the isAllowed() method.
$acl->isAllowed($admin, $serverRoom, 'view');
// returns true
$acl->isAllowed($visitor, $serverRoom, 'view');
// returns true
$acl->isAllowed($visitor, $serverRoom, 'cabinet');
// returns false
As we can see, Zend_Acl provides us with an easy, lightweight way of controlling access to our systems resources. Next we will look at the ways in which we can use the ACL component in our MVC application.
ACL in MVC
When looking to implement ACL in MVC, we need to first think about how and where we implement the ACL in the MVC layers. The ACL by nature is centralized, meaning that all rules, permissions, and so on are kept in a central place from which we query them. However, do we really want this? What about when we introduce more than one module, do all modules use the same ACL? Also we need to think about where access control happens—is it in the Controller layer or the Model/Domain layer?
Using a centralized global ACL
A common way to implement the ACL is to use a centralized ACL with access controlled at the application level or outside the domain layer. To do this, we first create a centralized ACL. Typically, this would be done during the bootstrap process and the full ACL would be created including all rules, resources, and roles. This can then be placed within the Registry or passed as an invoke argument to the Front Controller. We would then intercept each request using a Front Controller plugin (preDispatch). This would check whether the request was authorized or not using the ACL. If the request was not valid, we would then redirect the request to an access denied controller/action.
This approach would base its rules on the controller/action being requested, so a rule using this may look something like:
$acl->allow('Customer', 'user', 'edit');
Here we would allow access for a Customer Role to the User Resource and the Edit permission. This would map to the user Controller, and the edit action or user/edit
The advantages of using centralized global ACL are as follows:
- Centralized place to access and manage ACL rules, resources, and roles
- Maps nicely to the MVC controller/action architecture
The disadvantages are as follows:
- Centralized ACL could become large and hard to manage
- No means to handle modules
- We would need to re-implement access controls in order to use our Domain in a web service, as they are based on action/controller
Using module specific ACL’s
The next logical step is to split the ACL so that we have one ACL per module. To do this, we would still create our ACL during bootstrap but this time we would create a separate ACL for each module, and then we would use an Action Helper instead of Front Controller plugin to intercept the request (preDispatch).
- Fixes our module handling problem with the previous approach
- Keeps things modular and smaller
- We still have the problem of having to re-implement access control if we use our Domain away from the controller/action context.
ACL in the Domain layer
To deal with our last concern about what if we need to use the Domain in another context outside the controller/action architecture, we have the option to move all the Access Control into the Domain itself. To do this, we would have one ACL per module but would push the management of this into the Model. The Models would then be responsible for handling their own access rules. This in effect will give us a e-centralized ACL, as the Models will add all rules to the ACL.
- We can use the Model in different contexts without the need to re-implement the access control rules.
- We can unit test the access control
- The rules will be based on Model methods and not depend on the application layer
- Adds complexity to the Domain/Models
- Being de-centralized, it could be harder to manage
For the Storefront, we have opted to use the Model based ACL approach. While it adds more complexity and implementation can be a little confusing, the advantages of being able to unit test and use the Models outside the application layer is a big advantage. It also gives us a chance to demonstrate some of the more advanced features of the ACL component.