Last updated on January 25th, 2020 at 11:29 am

In this post i am going to explain how to implement Access Control List (ACL) in CakePHP Application. If you want to learn some more about CakePHP please follow this link. To implement ACL in CakePHP application you need have good basic knowledge of CakePHP. Now without wasting any time we can start creating CakePHP application. Please follow this step.

Step 1. Now create new project using composer

composer.phar create-project --prefer-dist cakephp/app cakeacl

Cakephp3 ACL Implementation

Step 2. Now download CakePHP ACL plugin from github using composer. Go to your project directory and run the following command

composer require cakephp/acl

Step 3. Open your bootstrap.php file and add your plugin

Plugin::load('Acl', ['bootstrap' => true]);

Step 4. Create ACL table by running below command

bin/cake migrations migrate -p Acl

Step 5. Now we need to create some more table for our application like Users, Groups, Posts run below schemas on your server

CREATE TABLE users (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(255) NOT NULL UNIQUE,
    password CHAR(60) NOT NULL,
    group_id INT(11) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE groups (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    created DATETIME,
    modified DATETIME
);

CREATE TABLE posts (
    id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    user_id INT(11) NOT NULL,
    title VARCHAR(255) NOT NULL,
    body TEXT,
    created DATETIME,
    modified DATETIME
);

Step 6. Now bake your application using cake bake run bellow command one after one

bin/cake bake all groups
bin/cake bake all users
bin/cake bake all posts

Step 7. Now once you bake your MVC open your UsersControllers.php and add login() and logout() function in it and also create view for login.

public function login() {
        if ($this->request->is('post')) {
            $user = $this->Auth->identify();
            if ($user) {
                $this->Auth->setUser($user);
                return $this->redirect($this->Auth->redirectUrl());
            }
            $this->Flash->error(__('Your username or password was incorrect.'));
        }
    }

    public function logout() {
        //Leave empty for now.
        $this->Flash->success(__('Good-Bye'));
        $this->redirect($this->Auth->logout());
    }

Login.ctp

<?= $this->Form->create() ?>
<fieldset>
    <legend><?= __('Please enter your username and password') ?></legend>
    <?= $this->Form->control('username') ?>
    <?= $this->Form->control('password') ?>
</fieldset>
<div class="text-center">
<?= $this->Form->button(__('Login')); ?>
</div>
<?= $this->Form->end() ?>

Step 8. Now open your UsersTable.php and replace code with below one

<?php
namespace App\Model\Table;

use App\Model\Entity\User;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Auth\DefaultPasswordHasher;

class UsersTable extends Table
{
    
   
    public function initialize(array $config)
    {
        parent::initialize($config);
        $this->setTable('users'); 
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');
        //$this->table('groups');  For version below 3.6
        //$this->displayField('id'); For version below 3.6
        //$this->primaryKey('id'); For version below 3.6
        $this->addBehavior('Timestamp');
        $this->addBehavior('Acl.Acl', ['type' => 'requester']);
        $this->belongsTo('Groups', [
            'foreignKey' => 'group_id',
            'joinType' => 'INNER'
        ]);
        $this->hasMany('Posts', [
            'foreignKey' => 'user_id'
        ]);
    }
    
    public function validationDefault(Validator $validator)
    {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create');
        $validator
            ->requirePresence('username', 'create')
            ->notEmpty('username')
            ->add('username', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);
        $validator
            ->requirePresence('password', 'create')
            ->notEmpty('password');
        return $validator;
    }
   
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->isUnique(['username']));
        $rules->add($rules->existsIn(['group_id'], 'Groups'));
        return $rules;
    }
    
    public function beforeSave(\Cake\Event\Event $event, \Cake\ORM\Entity $entity, 
        \ArrayObject $options)
    {
        $hasher = new DefaultPasswordHasher;
        $entity->password = $hasher->hash($entity->password);
        return true;
    }
}

and after this open your User.php inside Entity and paste bellow code

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;
use Cake\ORM\TableRegistry;

class User extends Entity
{
    protected $_accessible = [
        '*' => true,
        'id' => false
    ];

    protected $_hidden = [
        'password'
    ];

    public function parentNode()
    {
        if (!$this->id) {
            return null;
        }
        if (isset($this->group_id)) {
            $groupId = $this->group_id;
        } else {
            $Users = TableRegistry::get('Users');
            $user = $Users->find('all', ['fields' => ['group_id']])->where(['id' => $this->id])->first();
            $groupId = $user->group_id;
        }
        if (!$groupId) {
            return null;
        }
        return ['Groups' => ['id' => $groupId]];
    }
    
}

Step 9. Now open your GroupsTables.php and paste below code in it

<?php
namespace App\Model\Table;

use App\Model\Entity\Group;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

class GroupsTable extends Table
{
    public function initialize(array $config)
    {
        parent::initialize($config);
        $this->setTable('groups'); 
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
        //$this->table('groups');  For version below 3.6
        //$this->displayField('name'); For version below 3.6
        //$this->primaryKey('id'); For version below 3.6
        $this->addBehavior('Timestamp');
        $this->addBehavior('Acl.Acl', ['type' => 'requester']);
        $this->hasMany('Users', [
            'foreignKey' => 'group_id'
        ]);
    }
   
    public function validationDefault(Validator $validator)
    {
        $validator
            ->add('id', 'valid', ['rule' => 'numeric'])
            ->allowEmpty('id', 'create');
        $validator
            ->requirePresence('name', 'create')
            ->notEmpty('name');
        return $validator;
    }
}

after this open your Group.php and add below code

<?php
namespace App\Model\Entity;

use Cake\ORM\Entity;

class Group extends Entity
{
    protected $_accessible = [
        '*' => true,
        'id' => false
    ];

    public function parentNode()
    {
        return null;
    }
    
}

Step 10.  Now we can override Auth Temporary for some time add below method in your GroupsController.php and UsersController.php to create Group and add User.

public function initialize()
{
   parent::initialize();
   $this->Auth->allow();
}

Step 11. Now open your AppController.php and configure the AuthComponent and the AclComponent

<?php
namespace App\Controller;

use Cake\Controller\Controller;
use Cake\Event\Event;

class AppController extends Controller
{
    public $components = [
        'Acl' => [
            'className' => 'Acl.Acl'
        ]
    ];

    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('RequestHandler', [
            'enableBeforeRedirect' => false,
        ]);
        // For Version below 3.6 uncomment bellow code and remove line no 19.
        /* 
        $this->loadComponent('RequestHandler');
         */
        $this->loadComponent('Flash');

        $this->loadComponent('Auth', [
            'authorize' => [
                'Acl.Actions' => ['actionPath' => 'controllers/']
            ],
            'loginAction' => [
                'plugin' => false,
                'controller' => 'Users',
                'action' => 'login'
            ],
            'loginRedirect' => [
                'plugin' => false,
                'controller' => 'Posts',
                'action' => 'index'
            ],
            'logoutRedirect' => [
                'plugin' => false,
                'controller' => 'Users',
                'action' => 'login'
            ],
            'unauthorizedRedirect' => [
                'controller' => 'Users',
                'action' => 'login',
                'prefix' => false
            ],
            'authError' => 'You are not authorized to access that location.',
            'flash' => [
                'element' => 'error'
            ]
        ]);
    }

    
    public function beforeRender(Event $event)
    {
        if (!array_key_exists('_serialize', $this->viewVars) &&
            in_array($this->response->getType(), ['application/json', 'application/xml'])
        ) {
            $this->set('_serialize', true);
        }
    }

    // For Version below 3.6 remove beforeRender from above and uncomment bellow code
    /*
    public function beforeRender(Event $event)
    {
        if (!array_key_exists('_serialize', $this->viewVars) &&
            in_array($this->response->type(), ['application/json', 'application/xml'])
        ) {
            $this->set('_serialize', true);
        }
    }  */
}

Now after this go to browser and type : localhost/cakeacl/groups/add and create group like Admin, User etc. And after adding this go to: localhost/cakeacl/users/add and create two user with specific role.

Create ACL ACOs

Now we need to run command to automatically sync our table in ACL ACOs run below command to do that

bin/cake acl_extras aco_sync

Now remove that temporary Auth we add in step 10.

Permissions using the ACL shell

Now we can set permission to group which what can see like Admin can do anything but user can have limited access.

  • Grant permission to all controller:  bin/cake acl grant Groups.1 controllers
  • Deny permission for all controller: bin/cake acl deny Groups.1 controllers
  • For specific controller: bin/cake acl grant Groups.2 controllers/Posts
  • For specific method: bin/cake acl grant Groups.3 controllers/Posts/index

If you face any permission issue on ubuntu run below command

sudo chmod -R 777 cakeacl/app/tmp

Now you can login and check That’s all hope this will help you to create your Cake Application. 🙂