While there are plenty of tutorials out there covering how to use CakePHP’s ACL Component, and getting it to jive with CakePHP’s Auth Component, most of the tutorials out there take different approaches towards implementing this technology. If this is your first time using ACL, you might be feeling a little confused about exactly how ACL works, and if you’re like me, diving through all of these conflicting tutorials (some which use deprecated functions based on prior versions of cake) only serves to further confuse.
So now that I’ve spent a good couple of days wrapping my head around this concept, I thought I’d share what I’ve learned with you, so hopefully you can get this up and running much quicker than I did. To do this you’re going to need to understand some basic concepts of ACL, reading up on this in depth will only help you. I’ll try and point out some of the major pitfalls I ran into, so you don’t make the same mistakes. I’m not going to give you code, since this will just further confuse you. You’re going to have to write a fair bit of code on your own to get this working for your application. But I will provide a link to my source code for my project and an sql dump of my ARO tables so you can see what they should look like at the end of the tutorial.
Step One, What You Need to Learn
ACL basically defines what actions each person or group (AROs) can do to each item (ACOs). The important thing to remember is that an ARO and an ACO are really exactly the same. In a web application, everything is a row in a table. Each user or group or post or comment is just a row in the User, Group, Post or Comment table. So what we are doing is saying, when someone is logged in as a certain row in the User table, which rows in the Post table are they able to access? Groups are used so that users can inherit permissions. This saves us effort and results in fewer rows in our ACL tables because we don’t have to redefine permissions for each user, they simply inherit the permissions of the group they are in. A user can have a combination of any of the following permissions on an item: create, read, update and delete.
Setup the ACL tables by following the ACL section of the CakePHP manual. Once you have it setup, open up phpmyadmin or cocoa and have a look at the tables that were created, they should be acos, aros, and aros_acos. Lets look at the fields in these tables to understand what is going on. If it helps, whenever you see ARO think User, and whenever you see ACO think Post.
The acos and aros tables
These tables are identical. This is because (as I said before), everything in a web application is just rows in tables. A User is just a row in a table, and a Post is just a row in a table. When a user is logged in and tries to edit their profile, we tell the application to check if the ARO that corresponds to that users ID has permission to update the ACO that corresponds to the users ID. If this permission is setup in the aros_acos table, then we let the action take place, otherwise we block it. Here is an explanation of each field in the aros and acos table:
- The “id” field is simply the primary key.
- The “parent_id” field contains the id of the parent ARO or ACO. For example, if you have a row in the ARO table for the Admin group with an id of 3, then the User who belongs to the Admin group would have a parent_id of 3. Both AROs and ACOs can have parents, this means you can let all AROs in a group edit an ACO, or you can let one ARO edit all the ACOs in a group.
- The “model” and “foreign_key” field tells ACL which row in which table this ARO or ACO controls. For example if you are creating an ARO for the user in your User table with an id of 4, then the “model” would be “User” and the “foreign_key” would be 4. If you are trying to create a group that doesn’t have a model associated with it these fields can be NULL.
- The “alias” is a simple keyword used to quickly identify an ARO or ACO. You want this value to be unique. I use the following format for my User aliases “User::80″ where 80 is the id of the User in the User table. For an Admin group I simply use “Admin”. One of my first mistakes was trying to use the alias to describe what group a user was in. So the Administrator user has the alias “Administrator::User::80″. There’s no point to having this kind of “path” as an alias, and it only complicates things.
- The “lft” and “rght” fields get filled in automatically as long as you use the correct “parent_id”. From my understanding this is how ACL makes inheritance work. Basically if your Admin group has “lft” and “rght” values of 1 and 4, then the user with “lft” and “rght” values 2 and 3, will inherit the permissions of the Admin Group.
Remember that a row in your applications table will sometimes be both an ARO and an ACO. If you have a user profile, you want to make sure that when this user is logged in, they are able to edit their profile. Therefore you have to grant “Update” access for the ARO User::20, to the ACO User::20.
The aros_acos table
This is the table that defines what each aro can do to each aco. Simple enough? The fields are as follows:
- The “id” field is simply the primary key.
- The “aro_id” field is just the row in the AROs table we’re defining permissions for.
- The “aco_id” field is just the row in the ACOs table we’re defining permissions for.
- The “_create”, “_read”, “_update”, “_delete” fields are the actions we are allowing. If its empty, its not allowed, if it is 1 it is allowed, placing a -1 in all columns denies all actions.
Step Two, Setting Things Up
To set things up I recommend creating an init_acl controller, with a function inside called initAll() that creates all the default users and groups you want to have. If you end up messing things up, you can call this function to flush your tables and rebuild them from scratch. You can also use the console to do this, but I like the idea of doing this with PHP. Remember to delete or secure this controller once you put your site live ( You can use ACL to do this
).
In this controller, first create all the groups that you require. A group is just a row in the aros table. There are two kinds of groups, those based on models and those that are purely theoretical. The difference being that, if you have a Model that represents a group ( Let say you can register your baseball team which adds a row to the Team model with the id 101 ), then when you create this group, set the “model” field in the aros table to “Team” and the “id” to 101, and the alias of this ARO would be something like “Team::101″. You can also have theoretical groups, that aren’t represented by models, but just segment your Users by what actions they are allowed to do. Lets say some Users can delete posts and others can’t, you can have a group with the alias “Users_Deleters” and a group with the alias “Users_Posters”. In this case you can leave “model” and “foreign_key” blank.
What happens when you have groups that can be created by the users of your website? What if these newly created groups have sub groups? Basically you set it up so that creating a group, creates the ARO for that group, and then also creates additional AROs for the subgroups. For example, you register your baseball team that has two sub groups, infielders and outfielders, when the new Team ARO is created, you also create an Infielder and Outfielder ARO whose “parent_id” field points to the Team ARO. A good place to do this is in the AfterSave() function of your model.
Restricting Users, and Handling Guests
So now hopefully you understand the concept of how AROs and ACOs are set up. Now let’s cover how permissions are defined.
To define permissions you simply state the alias for the ARO of the user, the alias for the ACO they want to perform an action on, and the type of action they hope to perform. Lets say the User with “id” 72 wants to edit the post with “id” 33. We check if “User::72″ has access to update “Post::33″. Simple as that. Remember that ACL automatically takes care of inheritance, so even if User 72, hasn’t explicitly been given permission to edit Post 33, if the group that User 72 belongs to has the permissions (and we haven’t overwritten it by explicitly blocking User 72) that user will be able to update that post. This check is performed in the function in your controller that allows the action to occur.
But what about Users that aren’t logged in?
A good way to do this is to pretend like they are a logged in User. Basically if no User is set, you tell your application to treat them as the ARO you have defined as the “Guest” ARO.
The Code
Here is a sample zip file with my controllers / models / behaviours as well as an SQL dump of the related tables, which demonstrates these concepts so you can get a better idea of how to code all of this.
Quick Code Tips
Have an issue with your permissions getting overwritten with the previous one? Use this line of code to ensure you’ve created a new row in the aros_acos table before you create the new permission:
$this->Acl->Aro->Permission->create();
If you want to automate the creation of ARO and ACO nodes by placing them in the afterSave function of your model use this code:
$aro = new Aro(); $aro->create(); $aro->save(array( 'parent_id' => $parentid, 'model' => null, 'foreign_key' => null, 'alias' => "Alias" ));
If you want to automate the creation of permissions by placing them in the afterSave function of your model use this code:
$acl = new AclComponent(); $aro = new Aro(); $admin = $aro->findById($adminId); $acl->Aro->Permission->create(); $acl->allow($aroAlias,$acoAlias, array("create","read","update","delete"));
To retrieve an ARO or ACO to use as a parent use the following code:
$aro = new Aro(); $parentAlias = "User::".$this->id; $parent = $aro->findByAlias($parentAlias); //or $aco = new Aco(); $parentAlias = "User::".$this->id; $parent = $aco->findByAlias($parentAlias);
If you want to tie your current login session to an Aro node, you simply use this code in app_controller.php’s beforeFilter() function.
if(!$this->Auth->user()) { $guestGroup = $this->Acl->Aro->findByAlias('User_Guest'); $guestUser = $this->Acl->Aro->findByParentId($guestGroup['Aro']['id']); $this->current_user = $this->User->findById($guestUser['Aro']['foreign_key']); } else { $this->current_user = $this->Auth->user(); }
Then in your controller, to construct aliases and check permission of a user to edit a profile use the following:
function profile($id=NULL) { $aroAlias = "User::".$this->;current_user['User']['id']; $acoAlias = 'User::'.$id; if ($this->Acl->check($aroAlias, $acoAlias, 'update')) { echo 'Update access allowed for User Id'.$this->current_user['User']['id']; } else { echo 'Update access denied for User Id'.$this->current_user['User']['id']; } }

28 Comments »
hi Dan Imbrogno…
I think this is the best tutorial on ACL i have ever seen….
U r the best on ACL
Hi..
I am learning cakephp myself and uptill now ACL is the tuffest topic..
I am working on a project called TMS-Task Management System.
In that
There is groups table which has a entry like administrator,manager,developer,designer,client.
And I have a users table. In which each user belongs to one of this group.
I want to give limited access to each group of users. for example the users of client group can only see the tasks..
so how to do entries in aro,aco and aro_aco tables?
My tables are:
-Groups
-Users
-Projects
-tasks
-attachments
-comments
-status
Each row in your groups table has a corresponding row in the ACO table (therefore you create an ACO row for your client group). Then when you create the ACO row for your user you make the parent_id the id of the ACO row of the client group. Now when you want to set permissions for a group, you use the ACO id of the row you created for the group, as opposed to the row you created for the user. By doing this your user automatically inherits the permissions of the group.
Hope that helped, let me know if you need clarification.
but can you tell me what will be aro table entry?
so, where exactly is this zip with the example code? I could not find it.
Zip file seems to be missing?
Otherwise, great article. Thanks! This has been confusing me forever.
Sorry, the zip file is here:
http://brolly.ca/wp-content/uploads/2008/12/acl_code.zip
hey Dan where r u? reply me.. need help on ACL..
Hi sorry for the delay, I was away on vacation. The setup for ARO table entries is actually exactly the same as for ACO’s. Follow the instructions above and just replace the word ACO with ARO.
Setting things up this way will let you do things like: grant users permissions to edit their own profiles and allow administrators to edit the profiles of entire groups of users.
hey Dan…
now my aro, aco, aro_aco entry is perfect.
but now one new problem arise.. I checked permission for the current logged in user using yout check()-given above.
If logged in user has no permission it shows me the message “permission denied”…
But still It allows that logged in user to access that page for which he doesn’t have permission…
So what I ll do???? Cant get whats the problem..
Help me If u understand my problem
Hey tell me one thing.. after checking permission if user is not allowed then what to do with that user is our decision or this is done by cakephp automatically???
Under the section “the aros_acos table”, for the the “_create”, “_read”, “_update”, “_delete” fields, you mentioned that a “-1″ value means all actions are allowed.
I believe “-1″ should mean “deny or not allowed”.
Wow! Thank you so much for your post! Maybe you should write the next IBM 5 part series for Cake PHP! Reading how ACL works in plain english finally allowed me to setup my application properly!
Thank you!
Haha, If you know how to hook that up let me know
I might be interested!
You could very well be right. This might be a typo in my post. I’ll look into it and update the post accordingly. Thanks for your feedback!
[...] CakePHP + ACL concepts from a (former) noob posted under Uncategorized [...]
hi i’m new on cake, the app asks me for the response component ? i did see it in the controller /components . Can someone help
fyi , this is probably the best tutorial i read about acl , thanks a lot, but i really would like to try your code and this f;. response component !!!
i was meaning in my first post : i did NOT see it int the components
Great tutorial… Seriously, I finally understand.
Hi! Thanks for your comments. This is a mistake on my part, I have a response component I use quite frequently and forgot to remove it from the tutorial. You should be able to comment anything out related to the response component and it should work fine. I’ll update the file now.
Hi Dan,
i admit this is by far the greatest article about ACL.I read it very carefully and i point that you dont use group model and i am a little confused with the way you use Acl behavior.
Do you teach in a school/uni?
Great explanation!
Thank’s.
Very good this post about acl, the code show perfectly like works. I recommend to all!
Unfortunately we dont have something so in portuguese.
I Ask authorization for copy and translate to portuguese, so post in my new blog.
Thank you!
Muito bom este post sobre ACL, o código mostra perfeitamente como funciona. Eu recomendo a todos!
Infelizmente, não temos algo assim em português.
Peço autorização para copiar e traduzir para o português, para pôr no meu novo blog.
Obrigado!
can I use this in cakephp 1.2 ? what is the use of actAs=array(‘acl’);
hello Dan, 1 Question can we give permission to groups on action base i.e if I have 3 groups i.e admin, producer and publisher, and i have 1 controller called post with actions – addPost, editPost, deletePost, listPost, myPost and myFavPost. so can i set permission for admin can do all action, producer can do addPost, editPost and myPosts and producer can do listPost and myFavPost.
thanks in advance
hey..i am a new on cake php.
i make a side using cake php to learn cake.
i have 3 types of users
1. unregisterd user’s who only can browse the side
2.Registered user who can browse and alos post a comment after login
3.Admin users who can do all the things.
Please tell me how can i control them..
Great guide, really!
This guide still valid to CakePHP 1.3.x?
Regards!
hmm sorry Thiago couldn’t tell ya, I assume the principals are the same, but the code implementation may have changed. I haven’t worked with CakePHP in some years now. If you find this article is still relevant please let me know, or if there are changes that should be made, please report back!
Leave a comment