Looking for advice of ACL implementation

Has anyone done any good solid implementation of Zend ACL class to work with database backend?
What I am looking for is to have these features:
User belongs to a some group
Each group has specific permissions, some groups inherit permissions from other groups, but that is optional, sometimes it is helpfull like every group will inherit from ‘EVERYONE’ group (like in Windows based server or XP or any windows after Windows NT)

A User typically gets his permissions from being a member of some group, but by default every user is a member of EVERYONE group. This helps with setting permissions for unregistered site visitors.

Here is the catch: a permission can also be assigned directly to a user, in which case his effective permission is calculated by what permissions are assigned directly to him plus his group’s permissions.

I have thought about the possibility of user belonging to multiple groups, but so far have decided against it just because it can lean to confusion with permissions. I think that if a user really needs some custom permissions, the admin should create a special group and make that user a member of a special group, then set special permissions on a special group.

Finally, how to use the ACL? ACL is basically an object that holds permissions for all groups (called roles in Zend jargon) and can calculate effective permissions on specific object if there are any inheritence of group permissions is involved.

Zends says that their ACL object can be serialized and stored anywhere. This is what I am interested it. I want to be able to store ACL object in cache, so it can be accessed very fast because I don’t want to run the database queries every time.

I Looked at Zend’s ACL related classes and they don’t specifically made them serializable, they are serializable ‘just because’ they only hold array as property and array is easily serializable. I think that could lead to a problem when storing the object in cache and when it wakes up it needs to find the class. I would personally rewrite it a little bit to make then implement serializable interface, but that may not be necessary, only after I try it I will know for sure.

Has anyone done anything like that? Are there any better permission management class than Zend ACL?

I’d continue my thought here about ACL
The problem I see now with Zend ACL, which actually is not a problem in a sense that it fails in something, but here it is:

The
isAllowed() method accepts the role name as the first argument.

I mean only one role name.

So if a user belongs to 2 groups or more specifically if a user has some permissions set directly on the user’s ID, then you need to call isAllowed() twice (I think)

First with the name of group the user belongs to and then with the user’s own userID

The example of Zend website has this example usage:
if($acl->isAllowed(‘staff’, null, ‘revise’)){
// do stuff if allowed
}

So, if a user belongs to ‘staff’ and ‘secret_agents’, then I need to check ACL twice, probably wrapping it into one function
First determine that my user belongs to staff and ‘secret_agents’ and then check permission like this:

if( $acl->isAllowed(‘staff’, null, ‘revise’) || $acl->isAllowed(‘secret_agents’, null, ‘revise’) ){
// do stuff if allowed
}

So it looks that if I want to allow user to belong to multiple groups or to allow permissions to be set directly on a user in addition to on a group, then I would need to write a wrapper class and have a method like this:

if($wapper->isAllowed(UserObject $myuser, null, ‘edit_something’)){
// edit stuff
}

my own isAllowed() would then extract all groups (called roles in Zend) for my user and checks ACL for every group, breaking the loop on the first successful isAllowed() call.

It would be nice if Zend ACL allowed to pass array or roles as first argument, then
the wrapper class would be unnecessary.

You should just be able to serialize it normally since there is no special data in there. I often have it load once when a user logs in (guest is cached) and store it in the session.

For your second problem, I usually do something like this with my user object (representing the user viewing the page)

public function can($resource) {
   foreach ($this->_groups as $group) {
       if ($this->_acl->isAllowed($group, $resource)) {
           return true;
       }
    }   

    return false;
}

then to use it…

if ($this->user->can('revise')) {

}

It’s been a little while since I’ve used the ACLs though, so I apologize if it’s a little vague or pseudocode-ish.

Cheers

Thanks. The $this_groups in your code represent the groups to which logged in user belongs, right?

By the way, I think that storing ACL in session is a bad practice. Consider that administrator decides to change some permissions, maybe revoke permissions of some usergroup to do certain things. But if you store ACL in session, the already logged in user will not notice any changes as long as they logged in, which can be a very long time if they just keep browsing the site.

For this reason ACL should never be stored in session.
Just my opinion.

Right.

Re: sessions, it just depends how you implement everything. It’s easy enough to invalidate it when you are performing maintenance on your permission configuration. Probably best not to do this sort of thing on your development environment anyway. Of course, just my opinion. At the end of the day, as long as it works for you. :slight_smile:

I examined the code of Zend ACL and my conclusion that it was written by a smart person, but probably not by someone with a computer science degree.

For a really good implementation of permissions I would expect to see some sort of binary math going on when calculating the permissions, I mean the logical & and logical |
when combining group permissions to get the resulting permission.

But they using arrays, fine, but since they want to go with arrays, then at the best logical solution would be to use array_merge to merge arrays of permissions for every group in the order of inheritance and the result array will be the sum of permissions.

I will keep searching for ACL class, maybe take a look outside the php crowd.

I spent a few days working on the Zend ACL as well in a homegrown CMS. I found it really frustrating.

  1. Specific child settings (e.g. role -> resource+permission) trickles up to it’s more general parents’ ACL (e.g. parentRole -> resource). Since the child is more specific it changes it’s parent so two calls were required and management is a nightmare.

  2. Single level permissions. This requires a lot of excess hierarchical resources and “dumb” permissions (e.g. read and write) and more complex ones require extra add/deny settings for child and parents. It just gets messy.

  3. A programming shortcoming is the inability to get a list of resources added. There’s a getRegisteredRoles() function but no getRegisteredResources(). Huh? That’s ridiculous.

All this might be solved if lot’s of resources are created and only add calls are made but that’s not practical imo.

Oh well, it might take extending the class. The ZF guys have a lot of work to do.

Also I think that in operating systems the explicit ‘deny’ permission usually takes presedence. so if any one of the groups that user belongs to has an explicit deny, then his permission will always be ‘deny’, ragardless if he also belongs to group with ‘allow’ permission.

This is just the nature of how permissions work. Basically there should be 3 types of permissions: Allow, Deny, and null (or undefined)

In Zend ACL, on the other hand, their is no notion of explicit deny, so if you belong to 2 groups and one of these groups has a ‘deny’ permission on a specific action and another has explicit ‘allow’, then your final permission depends in the order in which your groups are added to the ACL. This sort of thing does not seem like a proper way to handle ‘deny’ permission. An experienced network admin knows that explicit deny should take precedence.

I know it’s not going to be easy to write a good proper ACL class, so I am not going to attempt it at this time, but I decided to start working on it, first just planning it, writing down ideas, etc… Then maybe one day I’ll have time to actually write something like that.

A good ACL should really be modelled after the espeblished operating system. Linux permissions are good candidate from where to copy the logic.

By the way, for now it my project I decided to do with ACL based on Zend but just not to use advanced inheritances. If I go with the simple ‘one group per user’ and ‘one inheritance per role’, then things may actually work and not be too complicated.

For an average CMS the one group per member is usually enough - a user either belongs to ‘registered’ or ‘guests’ or ‘moderators’ or ‘admins’ or ‘suspended’
If user also has to belong to some special group like ‘spice girls fans’, then this is a job for ACL accertion. By the way, I have not yet examined how accertion interact with regular permissions and what takes the precedence - accertion of permission. I hope that accertions don’t really take precedence over regular permissions check.

I agree with everything you said. I use a superadmin role that has two children; admin and guestadmin so the inheritance problems are still there as are the general vs. the specific. The more I think about it, extending the class is probably the only way to get it to be somewhat straightforward for site admins and the applications implementing advanced ACLs. GL

The way ACL is written now, you can have a group guestadmin, then admin inherits from guestadmin and superadmin inherits from admin and your superadmin will inherit from guestadmin. This is sort of a fall through inheritance. So basically superadmin directly inherits from only one group - admin, it also indirectly inherits from guestadmin.
This is yet another not well document feature of Zend ACL, I call it pass through inheritence.
This is different from multiple group inheritence and is somewhat easier to manage because in multiple group inheritence the order of groups you add as parent groups is very important while in passthrough inheritance you don’t have to worry about the order of groups since you directly inherit from only one group, hence there is no such thing is the order of groups.

Since I discovered this behaviour of Zend ACL I realized I can use this in my app to make things simpler. I can assign default very limited rights to guest, then assign more rights to ‘member’ and make it inherit from guest, then even more extra rights to ‘moderator’ and make moderator inherit from ‘member’, then couple of extra rights to ‘super moderator’ and make it inherit from ‘moderator’.
So now my supermoderator, even though directly inherits from only one group - moderator, also by the way of pass through inheritance inherits from all other groups in the chain.

Once you realize that that how it works, then designing an ACL is not so difficult anymore.

Yeah, I get all that and have tested it under every circumstance imaginable. It always has one annoying hole that makes it unusable for my intended purpose. Good luck with yours.