The cake built in CRUD auth is way too powerful and way too slow and memory consuming. In 99% of all cases there is no need for that. Also, I never really used the groups + roles of the core ACL. Basic groups usually do the trick.
If you just want to have some basic control over the access to specific actions, you need sth else. Here it comes.
Preparations
Please make sure the Tools Plugin is properly loaded (see the plugin readme for details). If you plan on using prefixed routing (admin, …), enable those in your core.php or bootstrap.php.
I assume you already got the AuthComponent included in the $components array of your AppController. You probably also excluded all public views with sth like
$this->Auth->allow('contact_form'); # in beforeFilter() of the specific controllers
This here (in the contact controller) makes Auth skip this action completely. The action will be accessable to everybody right away.
This is especially important for your login/logout actions:
// UsersController public function beforeFilter() { parent::beforeFilter(); $this->Auth->allow('login', 'logout', 'register', ...); }
Those actions should never trigger any Authorize module. All other actions then use our ACL to determine if access is granted or not.
You probably got a Role model (User belongsTo Role / User hasAndBelongsToMany Role) attached to the User. If you don’t want this, use Configure to store your keys like so:
// in your config.php if applicable or using Configure::write('Role', array(...)) $config['Role'] = array( // slug => identifier (unique magical number or maybe better a constant) 'superadmin' => 1, 'admin' => 2, 'moderator' => 3, 'helper' => 4, 'user' => 5, );
You should at least have an user – and maybe an admin role – for it to make sense.
You must also have some kind of Authentication in your AppController:
$this->Auth->authenticate = array('Form')); # uses username and password for login
Important: At least one type of authentication is necessary for any Authorize module to be usable.
So far so good. You can login/logout and once you are logged in browse all non-public pages. Even admin pages, of course. Thats where the TinyAuth class comes in.
TinyAuth
The code can be found at github.
First of all include it in your beforeFilter() method of the AppController:
$this->Auth->authorize = array('Tools.Tiny');
If you don’t place this code into your “Tools” plugin, make sure to remove the Tools. prefix then.
Now create a file in /Config/ called acl.ini like so:
[Tools.Countries] * = superadmin ; this is a comment [Account] edit,change_pw = * [Activities] admin_index,admin_edit,admin_add,admin_delete = admin,superadmin index = * [Users] index,search = user * = moderator,admin
The format is normal PHP INI style. I already included all kind of examples. * is a placeholder for “any”. The plugin prefix for controllers is not necessary as of now (maybe for Cake3 where the same controller name is allowed multiple times due to PHP5.3 namespaces). Comments in ini files start with “;”.
Explanations:
- Superadmin can access all Countries actions
- Account actions are accessable by all roles (and therefore logged in users)
- Activities can be modified by all admins and listed by all everyone
- Users can search and list other users, but only moderators and admins have access to all other actions
That’s it. Really easy, isn’t it?
Some details
TinyAuth expects a Session Auth User like so:
Auth.User.id Auth.User.role_id (belongsTo - role key directly in the users table)
or so:
Auth.User.id Auth.User.Role (hasAndBelongsToMany - multi role array containing all role keys)
As you can see it can manage both single and multile role setup. That’s sth the core one lacks, as well.
The current configuration is cached in the persistent folder by default. In development mode (debug > 0) it will be regenerated all the time, though. So remember that you have to manually clear your cache in productive mode for changes to take effect!
Quicktips
If you have a cleanly separated user/admin interface there is a way to allow all user actions to users right away;
$this->Auth->authorize = array('Tools.Tiny'=>array('allowUser'=>true));
Only for admin views the authorization is required then.
If you got a “superadmin” role and want it to access everything automatically, do this in the beforeFilter method of your AppController:
$userRoles = $this->Session->read('Auth.User.Role'); if ($userRoles && in_array(Configure::read('Role.superadmin'), $userRoles)) { # Skip auth for this user entirely $this->Auth->allow('*'); // cake2.x: `$this->Auth->allow();` without any argument! }
UPDATE 2012-01-10
The auth model can now be anything you like. It doesn’t have to be Role or role_id.
The new cake2.x uses “groups” per default.
You can easily adjust that now by passing or aclModel => 'Group' to the Tiny class, for instance.aclKey => 'group_id'
UPDATE 2013-03-10
Some will be happy to hear that the deeper “contained” Role array is now supported besides the flat array of role keys. This deep/verbose array of roles has been introduced in Cake2.2 with the new “contain” param for Auth. So it made sense to support this in TinyAuth. See the test case for details.
Notes
NOTE 2012-02-25
It seems that especially new-beys seem to mix up the meaning of * in the ACL. Although it is already laid out in the above text I will try to make it more clear:
This any placeholder for “roles” only refers to those users that are logged in. You must not declare your public actions this way!
All those must be declared in your controller using $this->Auth->allow() (in earlier versions of cake `$this->Auth->allow(‘*’)).
The reason is that Authenticate comes before Authorize. So without Authentication (logged in) there will never be any Authorization (check on roles).
NOTE 2013-02-12
You can use this in conjunction with my Auth class for a quick way to check on the current user and its role(s) anywhere in your application:
App::uses('Auth','Tools.Lib'); // in your bootstrap if (Auth::id()) { $username = Auth::user('username'); // do sth } if (Auth::hasRole(Configure::read('moderator'))) { // if you used configure slugs // do sth } if (Auth::hasRoles(array(ROLE_ADMIN, ROLE_MODERATOR)) { // if you used configure and constants instead of magic numbers // do sth }
See the inline class documentation or its test cases for details.
Upcoming
A shell to quickly modify the ini file (and batch-update for new controllers etc) should be ready some time soon.
There might some day also the possibility to use a database and some CRUD backend to manage the ACL. It would only need a few tables and controllers/views. If someone wants to help, go for it.
