Zend_Acl and storing roles and resources in a DB
Tuesday, 8. May 2007, 12:44:56
I've talked with a few people about using Zend_Acl and how to best approach the issue of resources, roles and users.
It isn't immediately obvious how one should do this:
- Create roles and resources in code?
- Load them from a database?
Creating roles/resources in code
The first of the above two, creating roles and resources in code is probably the best approach when the site in question is small and there aren't many roles or they don't change often. It's few SQL queries less and the database relations are simpler.We simply define the resources and roles in code. Then, to see if an user has access to a resource we just check his role. This can be done by simply saving the user's role in the user database table as text so it will be loaded with the users other info.
Loading roles/resources from a DB
This approach is slightly more complicated.For this you should create a custom Acl class inheriting from Zend_Acl. Have its constructor load the details from the DB.
For the database structure, you need three tables
id
login
password
role_id
id
name
role_id
id
name
inherit_id
In the users table you need to store the ID of the user's role and in resources table the ID of the role required to access it. The inherit_id column in roles table should store the ID of the role that role inherits from, if any.
Example code
Here's an example Acl class for using with a database.
class ResAcl extends Zend_Acl
{
public function __construct($db)
{
$sql = 'SELECT id,
name,
role_id
FROM resources';
$resources = $db->GetAll($sql);
$sql = 'SELECT roles.id,
roles.name,
inherits.name AS inherit_name
FROM roles
LEFT JOIN roles AS inherits ON inherits.id = roles.inherit_id
ORDER BY roles.inherit_id ASC';
$roles = $db->GetAll($sql);
//Loop roles and put them in an assoc array by ID
$roleArray = array();
foreach($roles as $r)
{
$role = new Zend_Acl_Role($r['name']);
//If inherit_name isn't null, have the role
//inherit from that, otherwise no inheriting
if($r['inherit_name'] !== null)
$this->addRole($role,$r['inherit_name']);
else
$this->addRole($role);
$roleArray[$r['id']] = $role;
}
foreach($resources as $r)
{
$resource = new Zend_Acl_Resource($r['name']);
$role = $roleArray[$r['role_id']];
$this->add($resource);
$this->allow($role,$resource);
}
}
}
This class uses ADODB. I also wrote an example ADODB class for using with Zend_Auth which can be used to load users to use with this code with some minor modifications.
The name column in the roles table isn't absolutely necessary: You could refer to the roles in Acl by just their ID column too, but if you're writing an admin panel it's probably much nicer to present users with a clear text name for the role instead of some weird ID number thingy.
For loading the users' roles, you will have to JOIN the roles.name column by the role_id in the users table.








Anonymous # 9. May 2007, 13:36
Hi.
Nice tut. my problem is that I have to set the security on data level.
for example:
Only admin can change the user data, but every one should bee able to change he´s own data. Or you can only delete your own article..
what is you suggestion?
regards
//Arro
zomg # 9. May 2007, 21:10
You could for example have an additional table with columns id, resource_id, role_id, mode. The mode field would store either "allow" or "deny". If you need more specific action based roles, you could add similar tables for actions as we had for resources in the article.
For more specialized access checks with assertions you'll probably have to add them in your Acl class yourself. For your issue this is probably the case.
Have a look at ZF Manual's ACL assertions page. It should be a good place to start. I might write about this later, too.
Anonymous # 19. June 2007, 10:24
With this DB structure you have a major problem. You can not use the roles with multiple inheritance. This might be a 'quick and dirty' implementation but not for real serious applications.
Anonymous # 14. July 2007, 17:38
Nicu,..
Do you have any suggestion for the DB structure?
Anonymous # 5. September 2007, 12:39
Hi,
grabbing your idea I've coded a full-featured class from scratch that creates the ACL from any database for which a Zend_Db_Adapter exists :)
My class supports multiple inheritance between roles, inheritance between resources, and storing deny rules in the dbase. It is freely available at: http://www.phpclasses.org/browse/package/4100.html
Kind regards,
Michael
zomg # 5. September 2007, 22:52
Anonymous # 19. September 2007, 15:02
although you've gotta register for phpclasses, it's actually a pretty damn good resource for PHP and classes related stuff...Quite lucky considering their URL.
Anonymous # 20. April 2008, 16:44
Great class from Michael Ziegler it only need one more thing.
It needs to separate the group roles as in "Admin, User, Editor' and the user roles instate of adding each user to the acl_roles table.
Then in the class he should only pull the acl_inheritance from the db that applies to the authenticated user at hand not all users.
Anonymous # 5. May 2008, 20:30
@Troy
Generalized groups make sense as most access is granted on group membership. But how would you manage user specific rights. For instance:
*groups: guests, users, managers, admin
*manager Adam is new, so he should not access the sensitive data (other managers are allowed)
other example:
users are not allowed to delete or modify entries, beside they are the owner/creator
Ok, last example you could solve by checking if creator == editor but I think you understand the need of some finer granularity. My idea is to keep all roles in one table and make use of inheritance. Actually I'm thinking on some tree structure to make the inheritance search faster. "Trees in SQL" by Joe Celko is a candidate but in my opinion inserts and updates are hard to handle.
Ideas welcome
Anonymous # 14. May 2008, 08:27
Parent roles must be inserted before their clients are, this is defined by the Role Registry of Zend Acl. If this rule is not met, an exception is thrown and the role is not inserted into the Registry.
If I look at your code you do no checking whether a parent already exists.
That would mean that once you inherit a role from another which was added later, the application would end in an exception.
I am still working out a way to solve this issue thus I cannot contribute the solution to this at current. (inserting a placeholder and later removing it and adding the real deal is not possible, it will decouple all inheritances which were related)
Have fun!
zomg # 14. May 2008, 12:23
Anonymous # 8. September 2008, 14:06
From what I see you are combining resources with a default permission which I think limits the effectiveness of the ACL system, for example when you are trying to deny some access somewhere.
Anonymous # 7. March 2009, 14:05
Hii
If I look at your code you do no checking whether a parent already exists.
That would mean that once you inherit a role from another which was added later, the application would end in an exception.
Ioffersearch.com Blogs - Just another Ioffersearch.com weblog