What
ACL or Access Control List is security concept about permission. It’s contain a list of permission to access some of resources. It specify who and what is allowed to access something.
In CakePHP ACL is used to specify users access to a controller, or specific action of a controller. Before we get to the how to section, there are a few terms we have to understand first.
- Access Request Object (ARO) is defined who want to access or request object. In simple term, it will be a user or group. This list of user or group is stored in table called ‘aros’.
- Access Control Object (ACO) is defined object that is being protected from a user or a group. In other word ACO related with which controller or action of controller you want to protect. ACO is stored in table called ‘acos’.
‘acos’ and ‘aros’ table is created automatically when you initialize the DB Acl tables (we’ll get into this later in how to part). Along with those table, there is table called ‘aros_acos’ which specify relationship between ARO and ACO. In english it will be mean which user has permission to a controller or action of controller.
How
Let’s get started.
First let’s create sample table for this tutorial, we will have table user, groups, notes, and sales.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
CREATE TABLE users ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password CHAR(40) NOT NULL, group_id INT(11) NOT NULL );
CREATE TABLE groups ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) NOT NULL );
CREATE TABLE notes ( id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, title VARCHAR(255) DEFAULT NULL, body text ); |
/app/model/user.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
<?php class User extends AppModel {
var $name = 'User'; var $belongsTo = array('Group'); var $actsAs = array('Acl' => array('requester'));
function parentNode(){ if (!$this->id) { return null; } $data = $this->read(); if (!$data['User']['group_id']){ return null; } else { return array('model' => 'Group', 'foreign_key' => $data['User']['group_id']); } } } ?> |
/app/model/group.php.
1 2 3 4 5 6 7 8 9 10 11 12
|
<?php class Group extends AppModel {
var $name = 'Group'; var $hasMany = array('User'); var $actsAs = array('Acl' => array('requester'));
function parentNode() { return null; }
} |
/app/model/note.php.
1 2 3 4 5 6
|
<?php class Note extends AppModel {
var $name = 'Note'; } ?> |
/app/app_controller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
<?php
class AppController extends Controller { var $components = array('Acl', 'Auth');
function beforeFilter() { //Configure AuthComponent $this->Auth->authorize = 'actions'; $this->Auth->loginAction = array('controller' => 'users', 'action' => 'login'); $this->Auth->logoutRedirect = array('controller' => 'users', 'action' => 'login'); $this->Auth->loginRedirect = array('controller' => 'notes', 'action' => 'index'); } }
?> |
/app/controller/users_controller.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
|
<?php class UsersController extends AppController {
var $name = 'Users'; var $helpers = array('Html', 'Form');
function beforeFilter() { $this->Auth->allow('login','logout'); }
function login() {
}
function logout() { $this->Session->setFlash('Good-Bye'); $this->redirect($this->Auth->logout()); }
function index() { $this->User->recursive = 0; $this->set('users', $this->paginate()); }
function add() { if (!empty($this->data)) { $this->User->create(); if ($this->User->save($this->data)) { $this->Session->setFlash(__('The User has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The User could not be saved. Please, try again.', true)); } } $groups = $this->User->Group->find('list'); $this->set(compact('groups')); }
function edit($id = null) { if (!$id && empty($this->data)) { $this->Session->setFlash(__('Invalid User', true)); $this->redirect(array('action'=>'index')); } if (!empty($this->data)) { if ($this->User->save($this->data)) { $this->Session->setFlash(__('The User has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The User could not be saved. Please, try again.', true)); } } if (empty($this->data)) { $this->data = $this->User->read(null, $id); } $groups = $this->User->Group->find('list'); $this->set(compact('groups')); }
function delete($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid id for User', true)); $this->redirect(array('action'=>'index')); } if ($this->User->del($id)) { $this->Session->setFlash(__('User deleted', true)); $this->redirect(array('action'=>'index')); } }
} ?> |
/app/controller/groups_controller.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
<?php class GroupsController extends AppController {
var $name = 'Groups'; var $helpers = array('Html', 'Form');
function index() { $this->Group->recursive = 0; $this->set('groups', $this->paginate()/span>); }
function view($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid Group.', true)); $this->redirect(array('action'=>'index')); } $this->set('group', $this->Group->read(null, $id)); }
function add() { if (!empty($this->data)) { $this->Group->create(); if ($this->Group->save($this->data)) { $this->Session->setFlash(__('The Group has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The Group could not be saved. Please, try again.', true)); } } }
function edit($id = null) { if (!$id && empty($this->data)) { $this->Session->setFlash(__('Invalid Group', true)); $this->redirect(array('action'=>'index')); } if (!empty($this->data)) { if ($this->Group->save($this->data)) { $this->Session->setFlash(__('The Group has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The Group could not be saved. Please, try again.', true)); } } if (empty($this->data)) { $this->data = $this->Group->read(null, $id); } }
function delete($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid id for Group', true)); $this->redirect(array('action'=>'index')); } if ($this->Group->del($id)) { $this->Session->setFlash(__('Group deleted', true)); $this->redirect(array('action'=>'index')); } } } ?> |
/app/controller/notes_controller.php.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
|
<?php class NotesController extends AppController {
var $name = 'Notes'; var $helpers = array('Html', 'Form');
function beforeFilter() { parent::BeforeFilter(); $this->Auth->allowedActions = array('index', 'view'); }
function index() { $this->Note->recursive = 0; $this->set('notes', $this->paginate()); }
function view($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid Note.', true)); $this->redirect(array('action'=>'index')); } $this->set('note', $this->Note->read(null, $id)); }
function add() { if (!empty($this->data)) { $this->Note->create(); if ($this->Note->save($this->data)) { $this->Session->setFlash(__('The Note has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The Note could not be saved. Please, try again.', true)); } } }
function edit($id = null) { if (!$id && empty($this->data)) { $this->Session->setFlash(__('Invalid Note', true)); $this->redirect(array('action'=>'index')); } if (!empty($this->data)) { if ($this->Note->save($this->data)) { $this->Session->setFlash(__('The Note has been saved', true)); $this->redirect(array('action'=>'index')); } else { $this->Session->setFlash(__('The Note could not be saved. Please, try again.', true)); } } if (empty($this->data)) { $this->data = $this->Note->read(null, $id); } }
function delete($id = null) { if (!$id) { $this->Session->setFlash(__('Invalid id for Note', true)); $this->redirect(array('action'=>'index')); } if ($this->Note->del($id)) { $this->Session->setFlash(__('Note deleted', true)); $this->redirect(array('action'=>'index')); } } } ?> |
Initialize ACL with this command. This command will create table ‘acos’, ‘aros’ and ‘aros_acos’.
cake schema run create DbAcl
You will get something like this
Welcome to CakePHP v1.2.1.8004 Console
---------------------------------------------------------------
App : app
Path: /var/www/labs/acl/app
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------
The following table(s) will be dropped.
acos
aros
aros_acos
Are you sure you want to drop the table(s)? (y/n)
[n] > y
Dropping table(s).
acos updated.
aros updated.
aros_acos updated.
The following table(s) will be created.
acos
aros
aros_acos
Are you sure you want to create the table(s)? (y/n)
[y] > y
Creating table(s).
acos updated.
aros updated.
aros_acos updated.
End create.
To insert new group and user, first we need to put temporary code in users_controller.php
function beforeFilter() {
$this->Auth->allow('login','logout');
}to
function beforeFilter() {
$this->Auth->allow('add', 'index', 'login','logout');
}and in groups_controllers.php add the following code
function beforeFilter() {
$this->Auth->allow('add', 'index', 'login','logout');
}This allow us to bypass permissions.
Add your group,
- Admin
- Manager
- Employee
Create user based on three group we have created before.
In this tutorial, we will create these permissions:
Employee
Manager
- list notes
- add notes
- view notes
- edit notes
Admin
- list notes
- add notes
- view notes
- edit notes
- list users
- add users
- view users
- edit users
- list groups
- add groups
- view groups
- edit groups
In your ‘aros’ table you’ll see something like this.
mysql> select * from aros;
+----+-----------+-------+-------------+-------+------+------+
id parent_id model foreign_key alias lft rght
+----+-----------+-------+-------------+-------+------+------+
1 NULL Group 1 NULL 1 4
2 NULL Group 2 NULL 5 8
3 NULL Group 3 NULL 9 12
4 1 User 1 NULL 2 3
5 2 User 2 NULL 6 7
6 3 User 3 NULL 10 11
+----+-----------+-------+-------------+-------+------+------+
6 rows in set (0.00 sec)
Now, change back the temporary code in users_controllers.php and groups_controllers.php.
Creating ACO
You can use automatic script to create all ACO based on every action from your controllers. But in this tutorial I use cake command line to create ACO.
This is permission tree I will create.
- controllers
- notes
- add
- edit
- delete
- users
- list
- add
- edit
- delete
- groups
- list
- add
- edit
- delete
Execute this following code to create ACO list.
cake acl create aco root controllers
cake acl create aco controllers Users
cake acl create aco Users add
cake acl create aco Users edit
cake acl create aco Users delete
cake acl create aco controllers Groups
cake acl create aco Groups add
cake acl create aco Groups edit
cake acl create aco Groups delete
cake acl create aco controllers Notes
cake acl create aco Notes add
cake acl create aco Notes edit
cake acl create aco Notes delete
After we have ARO and ACO, it’s time to connect both of them. We can connect a user or a group to list of permissions.
Grant permission Group 1 (Admin group) to all controller actions.
cake acl grant Group.1 controllers all
Grant permission Group 2 (Manager group) to all notes controller actions.
cake acl grant Group.2 Notes all
That’s it. You can try it yourself. If you find any mistake, please give me comment, and I’ll try to update and fix the problem. Thank you.