←A Summary of California's Maternity Leave| CakePHP ACL and Auth: Sample Website→
View full index of ACL Tutorial Articles
This article describes the initial steps used to created the sample website I created to demonstrate real-world usage of CakePHP and the Auth and ACL Components.
Get the source code for that sample website here.
This is more of a practical step-by-step tutorial than my previous tutorials on the ACL Component. I recommend reading those articles as well as the official Cake documentation if you want a broader overview of what ACL is.
This part of the tutorial will show the steps required to create and configure some basic ACL and Auth related elements in your database. I should point out from the start that this configuration will only work with the Auth Component in ‘crud’ mode. Although, the alternative ‘actions’ mode isn’t totally incompatible, you should read more about the differences between the two modes before creating your real application:
Obviously, you first need to have a working install of the latest nightly build of Cake. Also, take the time to make sure you are familiar with how to use the cake console.
On the command line, navigate to your app’s root directory (usually one step up from the app directory), and run the following command:
cake schema run create DbAcl
If this command runs with success, it will create 3 tables in your database. Here are the MySQL schema for these tables.
The application I am creating will organize it’s users into groups. Most permissions will be assigned to groups, and users will inherit access permissions based on their group membership. Exceptions will be possible, so that in some cases a user might get permission to something that other users in the same group do not have access to.
To make this possible, we will create the database tables which will hold data for Users and Groups. Users will be assigned group membership through a foreign key ‘group_id’.
In the case of this application, I will store the user’s login name in a field called “username” and store the password in a field called “passwd”. I am making the field length of “passwd” 255 characters long, because it will be holding a hashed version of the user’s password. By default this will be an sha1 hash, but PHP 5 allows even more secure hashes like SHA256 and Whirlpool, so I am making it extra long just in case.
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL auto_increment,
`username` varchar(50) NOT NULL default '',
`passwd` varchar(255) NOT NULL default '',
`name` varchar(50) NOT NULL default '',
`email` varchar(100) NOT NULL default '',
`group_id` int(10) unsigned NOT NULL default '0',
`active` tinyint(1) unsigned NOT NULL default '0',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
KEY `group_id` (`group_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
CREATE TABLE `groups` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(50) NOT NULL default '',
`parent_id` int(10) default '0',
`created` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
The following MYSQL commands will manually create your intial groups:
INSERT INTO `groups` SET
`id` = 1,
`name` = 'users',
`parent_id` = null;
INSERT INTO `groups` SET
`id` = 2,
`name` = 'editors',
`parent_id` = 1;
INSERT INTO `groups` SET
`id` = 3,
`name` = 'superusers',
`parent_id` = 2;
To manually create a user with a password, you will need to get the security salt used by your application. Look into your app/config/core.php file and get the salt value from a line like this:
Configure::write('Security.salt', 'welcometomykitchen');
In this case the salt is “welcometomykitchen”. Your salt should be randomized and unique. This salt is combined with the user’s password string for extra security. So, to manually create the first superuser for your site, keep the salt handy.
The following will create a superuser for your site. Use your own salt value in place of mine, and add your own email address. The code below will give the ‘pherme’ the password of ‘ILikeCake’.
INSERT INTO `users` SET
`id` = 1,
`username` = 'pherme',
`passwd` = SHA1("welcometomykitchenILikeCake"),
`name` = 'Pierre Herme',
`email` = 'pherme@nonexistant.com',
`group_id` = 3,
`active` = 1;
Now that we have the basic database tables set up, we can build our intial ARO Tree. This tree will allow us to assign permissions in such a way to allow an efficient use of inheritance. Both groups and users will be in the tree. However, as mentioned above, most permissions will ultimately be assigned to groups, with users inheriting most of their rights from the group they belong to.
After this intial setup, the ACL Behavior will manage this type of synchronization between the aros, users, and groups tables.
Here are the ACL commands to set up your groups and one user:
cake acl create aro root users
cake acl create aro users editors
cake acl create aro editors superusers
cake acl create aro superusers pherme
For the first group ‘users’ I am using the magic parent “root”, which gives the ARO node a parent_id value of null.
If you do:
cake acl view aro
You should now see something like this:
[1]users
[2]editors
[3]superusers
[4]pherme
This indicates that editors will inherit permissions from users, and so on down the tree.
The Cake Console only creates alias values in the aros table. In order for the ACL Behavior to work, however, the rows of the aros table have to have ‘model’ and ‘foreign_key’ values properly set.
The ‘model’ field should correspond to the CamelCase name of the model which the record belongs to — either Group or User, while the ‘foreign_key’ field represents the id of that record. Again, ACL Behavior will take care of this later on, but for now we will manually create them with the following MySQL commands:
UPDATE `aros` SET
`model` = 'Group',
`foreign_key` = 1
WHERE `alias` = 'users';
UPDATE `aros` SET
`model` = 'Group',
`foreign_key` = 2
WHERE `alias` = 'editors';
UPDATE `aros` SET
`model` = 'Group',
`foreign_key` = 3
WHERE `alias` = 'superusers';
UPDATE `aros` SET
`model` = 'User',
`foreign_key` = 1
WHERE `alias` = 'pherme';
Before assigning permissions, we must also have some values in the acos table.
This table should contain a list of entries with alias values that correspond to CamelCase names of your application’s controllers. It is also helpful to organize these under a parent node, which I call “Site”.
cake acl create aco root Site
In this example, we will eventually have controllers named Users, Usermanager, Groups, Articles and SecretRecipes, so we need to add corresponding ACO nodes:
cake acl create aco Site Users
cake acl create aco Site Usermanager
cake acl create aco Site Groups
cake acl create aco Site Articles
cake acl create aco Site SecretRecipes
Note how all of these have the parent of Site.
First lets give full access to our superusers group. We do this with the following command. By granting permission to the parent ACO named ‘Site’, the superusers will continue to have all permissions on any future ACO created whose parent is Site.
cake acl grant superusers Site *
All our users should get ‘read’ access on all acos:
cake acl grant users Site read
Except that we actually want to block users from accessing Usermanager:
cake acl deny users Usermanager *
Let’s make sure everything was set up correctly by running the check command (the expected responses are indicated by the arrow):
cake acl check pherme Articles create
--> pherme is allowed
cake acl check pherme Usermanager delete
--> pherme is allowed
cake acl check editors Articles read
--> editors is allowed
cake acl check users Usermanager create
--> users is not allowed
cake acl check users Usermanager read
--> users is not allowed
Spend some time now taking a look at your database tables. I have already written an example of how to dissect these tables. Take some time to understand the way the lft and rght values are used to represent the permission trees in BOTH the acos and aros tables. Also take a look at the aros_acos table and spend some time understanding how the permissions are granted and denied through the use of the ‘_create’, ‘_read’, ‘_update’, and ‘_delete’ columns.
You now have a good foundation in your database from which to now build out your application with more users and more permissions.
Again, to skip some steps and just see what all this looks like as a final website:
download the source code here
[EDIT: May 26, 2008] Made references to sample site source code.
Sunday May 18, 2008
Hi there, Thanks for the tutorial… I have been battling with various ACL and Auth tutorials for days now…
I think there is an error in the way that you set out your ARO table. Basically the group users should have children editors and normalusers or something like that. Then another top level group should be called superusers instead of superusers being a child of editors. The way i understand it – if superusers is a child of editors then you will not be able to grant “read only” access to all users and then grant all access to superusers because it is under the users group. Please verify!! thanks…
@Dan: The bottom line is that a superuser should not have any children. So it can either be at the bottom of the tree or be it’s own node.
Either way it works, because the permissions granted to the superusers override any more restrictive settings granted to the superusers parents.
I think it is instructive to have the superuser at the bottom of the tree, because it helps illustrate how inheritance works. But for simplicity, it does make a lot of sense to add a separate superuser node that is not part of any tree.
Hi Aran,
Many, many thanks for this tutorial, this is the only one that has make me understand very clearly how the ACL component works !! but I still have one unresolved question: how do you manage access to controller actions different from CRUD actions ? for example, if I have on a controller an action called viewNewPosts, which isn’t a CRUD action, how should I manage it’s authorization ?
Thanks.
I am wondering how I can have a “scope” for any particular action. either in actions mode or in CRUD mode. By scope, I mean I can edit my own articles, but not other peoples articles.
for example a user has a profile that really only they and admins might have control over.
By this I mean that the system would imply this automatically – I would not have to explicitly set a particular permission for user Luke or bettycrocker and so on.
Luke … as it is written, the Auth and ACL components don’t provide this kind of automatic record level control. If you search through the Google groups you will find some good discussions on this matter. For my own project, I decided to put that kind of record level control in the Controller.
Hey Aran. I was really imporessed by the tutorial and it gave me an insight into the way the Acl can work on the fly. However, as I have been able to add my Aros on the fly as I add my groups and users, I have been unable to accomplishe the same with the Acos. Is there any solution to theis because I tried using the code in the group.php model but saving Aco instead of Aro.
Great tutorial. The only thing that initially stumped me was using
grant superuser Site *
which returned a ‘permission denied’ when using acl check. I had to use
grant superusers Site all
for it to work.
←A Summary of California's Maternity Leave| CakePHP ACL and Auth: Sample Website→
I'm Aran Johnson and I make websites.
I primarily use: PHP, MySQL, SubVersion, CakePHP, TextPattern, Cream Text Editor, and Addi Turbo Needles
Oakland Parking Violations and Fines
CakePHP ACL and Auth: Record Level Protection
How To Play Hearts With Only Two Players
CakePHP ACL and Auth: Record Level Protection
CakePHP ACL and Auth: Sample Website
CakePHP ACL and Auth Tutorial: Database Setup
CakePHP ACL Tutorial: Introduction
CakePHP ACL Tutorial: Usage With Auth Component
CakePHP ACL Tutorial: Initial Setup
CakePHP ACL Tutorial: Auth Component Example
CakePHP ACL Tutorial: How To Check Access
Cake PHP ACL Tutorial: The Database Tables
Thank you so much for putting this all together! This is very helpful.
— Yura May 21, 06:44 AM #