Programmer's Reference Guide
| Zend_Acl |
Introduction
Zend_Acl provides a lightweight and flexible access control list (ACL) implementation for privileges management. In general, an application may utilize such ACL's to control access to certain protected objects by other requesting objects.
For the purposes of this documentation:
-
a resource is an object to which access is controlled.
-
a role is an object that may request access to a Resource.
Put simply, roles request access to resources. For example, if a parking attendant requests access to a car, then the parking attendant is the requesting role, and the car is the resource, since access to the car may not be granted to everyone.
Through the specification and use of an ACL, an application may control how roles are granted access to resources.
Resources
Creating a resource in Zend_Acl is very simple. Zend_Acl provides the resource, Zend_Acl_Resource_Interface, to facilitate creating resources in an application. A class need only implement this interface, which consists of a single method, getResourceId(), for Zend_Acl to recognize the object as a resource. Additionally, Zend_Acl_Resource is provided by Zend_Acl as a basic resource implementation for developers to extend as needed.
Zend_Acl provides a tree structure to which multiple resources can be added. Since resources are stored in such a tree structure, they can be organized from the general (toward the tree root) to the specific (toward the tree leaves). Queries on a specific resource will automatically search the resource's hierarchy for rules assigned to ancestor resources, allowing for simple inheritance of rules. For example, if a default rule is to be applied to each building in a city, one would simply assign the rule to the city, instead of assigning the same rule to each building. Some buildings may require exceptions to such a rule, however, and this can be achieved in Zend_Acl by assigning such exception rules to each building that requires such an exception. A resource may inherit from only one parent resource, though this parent resource can have its own parent resource, etc.
Zend_Acl also supports privileges on resources (e.g., "create", "read", "update", "delete"), so the developer can assign rules that affect all privileges or specific privileges on one or more resources.
Roles
As with resources, creating a role is also very simple. All roles must implement Zend_Acl_Role_Interface. This interface consists of a single method, getRoleId(), Additionally, Zend_Acl_Role is provided by Zend_Acl as a basic role implementation for developers to extend as needed.
In Zend_Acl, a role may inherit from one or more roles. This is to support inheritance of rules among roles. For example, a user role, such as "sally", may belong to one or more parent roles, such as "editor" and "administrator". The developer can assign rules to "editor" and "administrator" separately, and "sally" would inherit such rules from both, without having to assign rules directly to "sally".
Though the ability to inherit from multiple roles is very useful, multiple inheritance also introduces some degree of complexity. The following example illustrates the ambiguity condition and how Zend_Acl solves it.
Example #1 Multiple Inheritance among Roles
The following code defines three base roles - "guest", "member", and "admin" - from which other roles may inherit. Then, a role identified by "someUser" is established and inherits from the three other roles. The order in which these roles appear in the $parents array is important. When necessary, Zend_Acl searches for access rules defined not only for the queried role (herein, "someUser"), but also upon the roles from which the queried role inherits (herein, "guest", "member", and "admin"):
- $acl = new Zend_Acl();
- $acl->addRole(new Zend_Acl_Role('guest'))
- ->addRole(new Zend_Acl_Role('member'))
- ->addRole(new Zend_Acl_Role('admin'));
- $acl->addRole(new Zend_Acl_Role('someUser'), $parents);
- $acl->add(new Zend_Acl_Resource('someResource'));
- $acl->deny('guest', 'someResource');
- $acl->allow('member', 'someResource');
Since there is no rule specifically defined for the "someUser" role and "someResource", Zend_Acl must search for rules that may be defined for roles that "someUser" inherits. First, the "admin" role is visited, and there is no access rule defined for it. Next, the "member" role is visited, and Zend_Acl finds that there is a rule specifying that "member" is allowed access to "someResource".
If Zend_Acl were to continue examining the rules defined for other parent roles, however, it would find that "guest" is denied access to "someResource". This fact introduces an ambiguity because now "someUser" is both denied and allowed access to "someResource", by reason of having inherited conflicting rules from different parent roles.
Zend_Acl resolves this ambiguity by completing a query when it finds the first rule that is directly applicable to the query. In this case, since the "member" role is examined before the "guest" role, the example code would print "allowed".
Note: When specifying multiple parents for a role, keep in mind that the last parent listed is the first one searched for rules applicable to an authorization query.
Creating the Access Control List
An Access Control List (ACL) can represent any set of physical or virtual objects that you wish. For the purposes of demonstration, however, we will create a basic Content Management System (CMS) ACL that maintains several tiers of groups over a wide variety of areas. To create a new ACL object, we instantiate the ACL with no parameters:
- $acl = new Zend_Acl();
Note: Until a developer specifies an "allow" rule, Zend_Acl denies access to every privilege upon every resource by every role.
Registering Roles
CMS's will nearly always require a hierarchy of permissions to determine the authoring capabilities of its users. There may be a 'Guest' group to allow limited access for demonstrations, a 'Staff' group for the majority of CMS users who perform most of the day-to-day operations, an 'Editor' group for those responsible for publishing, reviewing, archiving and deleting content, and finally an 'Administrator' group whose tasks may include all of those of the other groups as well as maintenance of sensitive information, user management, back-end configuration data, backup and export. This set of permissions can be represented in a role registry, allowing each group to inherit privileges from 'parent' groups, as well as providing distinct privileges for their unique group only. The permissions may be expressed as follows:
| Name | Unique Permissions | Inherit Permissions From |
|---|---|---|
| Guest | View | N/A |
| Staff | Edit, Submit, Revise | Guest |
| Editor | Publish, Archive, Delete | Staff |
| Administrator | (Granted all access) | N/A |
For this example, Zend_Acl_Role is used, but any object that implements Zend_Acl_Role_Interface is acceptable. These groups can be added to the role registry as follows:
- $acl = new Zend_Acl();
- // Add groups to the Role registry using Zend_Acl_Role
- // Guest does not inherit access controls
- $roleGuest = new Zend_Acl_Role('guest');
- $acl->addRole($roleGuest);
- // Staff inherits from guest
- $acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);
- /*
- Alternatively, the above could be written:
- $acl->addRole(new Zend_Acl_Role('staff'), 'guest');
- */
- // Editor inherits from staff
- $acl->addRole(new Zend_Acl_Role('editor'), 'staff');
- // Administrator does not inherit access controls
- $acl->addRole(new Zend_Acl_Role('administrator'));
Defining Access Controls
Now that the ACL contains the relevant roles, rules can be established that define how resources may be accessed by roles. You may have noticed that we have not defined any particular resources for this example, which is simplified to illustrate that the rules apply to all resources. Zend_Acl provides an implementation whereby rules need only be assigned from general to specific, minimizing the number of rules needed, because resources and roles inherit rules that are defined upon their ancestors.
Note: In general, Zend_Acl obeys a given rule if and only if a more specific rule does not apply.
Consequently, we can define a reasonably complex set of rules with a minimum amount of code. To apply the base permissions as defined above:
- $acl = new Zend_Acl();
- $roleGuest = new Zend_Acl_Role('guest');
- $acl->addRole($roleGuest);
- $acl->addRole(new Zend_Acl_Role('staff'), $roleGuest);
- $acl->addRole(new Zend_Acl_Role('editor'), 'staff');
- $acl->addRole(new Zend_Acl_Role('administrator'));
- // Guest may only view content
- $acl->allow($roleGuest, null, 'view');
- /*
- Alternatively, the above could be written:
- $acl->allow('guest', null, 'view');
- //*/
- // Staff inherits view privilege from guest, but also needs additional
- // privileges
- // Editor inherits view, edit, submit, and revise privileges from
- // staff, but also needs additional privileges
- // Administrator inherits nothing, but is allowed all privileges
- $acl->allow('administrator');
The NULL values in the above allow() calls are used to indicate that the allow rules apply to all resources.
Querying an ACL
We now have a flexible ACL that can be used to determine whether requesters have permission to perform functions throughout the web application. Performing queries is quite simple using the isAllowed() method:
- "allowed" : "denied";
- // allowed
- "allowed" : "denied";
- // denied
- "allowed" : "denied";
- // allowed
- "allowed" : "denied";
- // allowed because of inheritance from guest
- "allowed" : "denied";
- // denied because no allow rule for 'update'
- "allowed" : "denied";
- // allowed because administrator is allowed all privileges
- "allowed" : "denied";
- // allowed because administrator is allowed all privileges
- "allowed" : "denied";
- // allowed because administrator is allowed all privileges
| Zend_Acl |
Add A Comment
Please do not report issues via comments; use the ZF Issue Tracker.
If you have a JIRA/Crowd account, we suggest you login first before commenting.

Comments
lines 9-26 in 2nd last codeblock.
<?php
// Guest may only view content
$acl->allow($roleGuest, null, 'view');
// Staff inherits view privilege from guest, but also needs additional
// privileges
$acl->allow('staff', null, array('edit', 'submit', 'revise'));
?>
Is it simply that they are called in order? So as you add items they inherit from all previous items?
(Line 3-5)
$roleGuest = new Zend_Acl_Role('guest');
$acl->addRole($roleGuest);
$acl->addRole(new Zend_Acl_Role('staff'), $roleGuest); // A new role 'staff' is added and has inherited the 'guest' role.
[...]
We also can directly write "$acl->addRole(new Zend_Acl_Role('staff'), 'guest');".
Yeah after some sleep and rereading the code I see that you simply pass a 2nd argument to addRole(...) to indicate the inheritance.
Thanks.
Here is a stab at populating an acl class from a database:
Create a custom acl model class (e.g. class Application_Model_User extends Zend_Acl ) defined in folder application/models/User.php, that can be used/called from a custom controller (e.g. application/controllers/AuthorizationController.php)
Create a function within your custom acl model class to populate the acl from the database, for example within Application_Model_User:
public function findByName($name) {
//setup the db parameteres if necessary
$db = new Zend_Db_Adapter_Pdo_Mysql(array(
'host' => '127.0.0.1',
'username' => 'user',
'password' => 'password',
'dbname' => 'profiles'
));
// sample join of user, roles, resources by user id, role id and resource id
$sql = 'select u.id, u.username, u.password, r.name as role, rc.controller, rc.action
from profiles.roles r, profiles.resources rc, profiles.users u, profiles.roleresources rs
WHERE u.username = ? and u.roleid = r.id and r.id = rs.roleid and rs.resourceid = rc.id';
//fetch all the rows with the $name parameter
$rows = $db->fetchAll($sql, array($name));
//process each row to populate the acl roles, resources
foreach ($rows as $row) {
$this->setId($row['id']);
$this->setName($row['username']);
$this->setPassword($row['password']);
$role = new Zend_Acl_Role($row['role']);
//add a role to this acl if it does not exist
if (!$this->hasRole($role))
$this->addRole($role);
$resource = new Zend_Acl_Resource($row['controller']);
//add a resource to this acl if it does not exist
if (!$this->has($resource))
$this->add($resource);
//add the permissions for the role, resource and action
$this->allow($this->getRole(), $row['controller'], $row['action']);
} //end foreach
//store this acl object in Session or you could make this class a Singleton
$_SESSION['User'] = $this;
}
You can use your acl class within an AuthController and test for isAllowed on each action called, for example within any controller there is an indexAction:
public function indexAction()
{
//retrieve the custom User acl object that was initialized by your custom AuthController
$user = $_SESSION['User'];
if($user->isAllowed($user->getRole(), $this->_name, 'index'))
{ // if NO ROLES have access, redirect to unauthorized page
} else {
//throw new Exception($user->getRole());
$this->_helper->redirector('error', 'error');
}
}
Hope this helps get you started.
Please add some proper tutorials. You can't expect your framework to get used by people if it's so hard to get into it.
What is here is more than enough for a vaguely competent developer to get it running.
The Symfony2 documentation has a handy comment at the start of each block which tells you the file they're displaying (eg http://symfony.com/doc/current/book/controller.html). Something like that would be handy here, especially as I got to this page after clicking links for a 'getting started guide' but don't know where I'm meant to be putting the ACL to get started with it!
I'm not asking to be taught PHP, I just want to know where the framework wants me to put things - as if I already knew that I wouldn't be reading the documentation anyway ;)
Cheers :)
It also seems that I'm not the first to ask this question, perhaps it would be worth adding that to the docs ;)
As for the documentation: we all know they're not perfect, however it's up to us to change this :) Learn & Share :) That's for the forums up here are anyway. The guys at Zend are doing a bloody good work providing us with such a powerful framework for free (in Community Edition), now it's up to us to help others learning how to use it :)
Why they couldn't write a code sample that is complete and have all the classes used in it, reference the sample, instead of useless fragments and snippets, is baffling.
They should write a cookbook using the framework where there are basic examples, including setting up users & roles in a DB, accessing them through the frameworks ORM and applying the roles to different sample pages.
Then they could have each class used in the example, reference the example page.
They should take a leaf out of the jQuery docs, as these documents are as unhelpful as your post.