CakePHP 4 ACL Implementation

In this post, I am going to explain how we can implement Access Control List (ACL) in CakePHP 4. If you want to learn how to implement ACL in CakePHP 3 you can check my old tutorial (Cakephp3 ACL Implementation).

About CakePHP Framework.

CakePHP is a free open-source framework just like Larvel. CakePHP follows Model View Controller (MVC) approach. You can create your applications simpler, faster, and require less code.

If you are new to the CakePHP framework and want to learn more, please let me know in the comment section. Now without wasting any time let get started.

Prerequisites

  1. HTTP Server. For example Apache. Having mod_rewrite is preferred, but by no means required. You can also use Nginx, or Microsoft IIS if you prefer.
  2. Composer required to download Cake Framework and PHP Plugin.
  3. Minimum PHP 7.2 (8.0 supported).
  4. mbstring PHP extension
  5. intl PHP extension
  6. SimpleXML PHP extension
  7. PDO PHP extension

Step 1. Now install and create a new project using composer.

composer create-project --prefer-dist cakephp/app:~4.0 my_new_app

Step 2. Once a new project created we need to install the CakePHP Acl plugin using the below command.

composer require cakephp/acl

Step 3. If the plugin installed successfully we need to load the plugin in the Application.php file. You can load the plugin using the below command.

bin/cake plugin load acl

You can also manually load the plugin inside the src/Application.php file inside the bootstrap() method.

public function bootstrap(): void { $this->addPlugin('Acl'); /*.. Other Code...*/ }

Step 4. Create a database in phpMyAdmin and add database details in config/app_local.php. Find Datasources array and add your details.

CakePHP 4 ACL implementation

Step 5. Now create the required ACL table by running the below command.

bin/cake migrations migrate -p Acl

Step 6. Now we can create two more table Groups and Users, Run the below schema inside your database.

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
);

Step 7. Now we can bake (generate MVC) the User and Group table.

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

Step 8. Once the MVC file is generated we can first modify our UserController to add login and logout methods inside /src/Controller/UserController.php. open file in your editor.

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()); }

Create Login.php inside /templates/Users/login.php and add the below code.

<?= $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 9. Now open UsersTable.php inside src/Model/Table and place the specific code in defined method.

public function initialize(array $config): void { parent::initialize($config); /*..Other Code..*/ $this->addBehavior('Acl.Acl', ['type' => 'requester']); // Add this code }

Now add the bellow method at bottom of your class.

<?php
declare(strict_types=1);
namespace App\Model\Table; use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Auth\DefaultPasswordHasher; class UsersTable extends Table
{ /*.Other methods */ public function beforeSave(\Cake\Event\Event $event, \Cake\ORM\Entity $entity, \ArrayObject $options) { $hasher = new DefaultPasswordHasher; $entity->password = $hasher->hash($entity->password); return true; }
}

Now open your User.php inside src/Model/Entity and add the below code

<?php
declare(strict_types=1); namespace App\Model\Entity; use Cake\ORM\Entity;
use Cake\ORM\TableRegistry; class User extends Entity
{ /* Other code */ 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 10. Now open your GroupsTables.php and add the below code to it.

<?php
declare(strict_types=1); namespace App\Model\Table; use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use App\Model\Entity\Group; class GroupsTable extends Table
{ public function initialize(array $config): void { /* Other code*/ $this->addBehavior('Acl.Acl', ['type' => 'requester']); // Add this }
}

Now open your Group.php inside src/Model/Entity and add the below code

<?php
declare(strict_types=1); namespace App\Model\Entity; use Cake\ORM\Entity; class Group extends Entity
{ /* Other code*/ // Add This public function parentNode() { return null; }
}

Step 11. Now open UsersController.php and GroupsController.php and add initialize() method in it and allow access to create a new Group and user account.

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

Step 12. Now open your AppController.php and configure the AuthComponent and the AclComponent as shown below.

<?php
declare(strict_types=1); namespace App\Controller; use Cake\Controller\Controller;
use Cake\Event\EventInterface; class AppController extends Controller
{ public function initialize(): void { parent::initialize(); $this->loadComponent('RequestHandler', [ 'enableBeforeRedirect' => false, ]); $this->loadComponent('Flash'); $this->loadComponent('Acl', [ 'className' => 'Acl.Acl' ]); $this->loadComponent('Auth', [ 'authorize' => [ 'Acl.Actions' => [ 'actionPath' => 'controllers/', 'userModel' => 'Users' ] ], 'authenticate' => [ 'Form' => [ 'fields' => ['username' => 'email'], 'userModel' => 'Users' ], ], 'loginAction' => [ 'plugin' => false, 'controller' => 'Accounts', 'action' => 'login' ], 'loginRedirect' => [ 'plugin' => null, 'controller' => 'Users', 'action' => 'index' ], 'logoutRedirect' => [ 'plugin' => null, 'controller' => 'Users', 'action' => 'login' ], 'unauthorizedRedirect' => [ 'controller' => null, 'action' => 'login', 'prefix' => false ], 'authError' => 'You are not authorized to access that location.', 'flash' => [ 'element' => 'error' ] ]); } public function beforeRender(EventInterface $event) { $this->set('_serialize', true); }
}

Now after this go to the browser and type: localhost/my_new_app/groups/add and create a new group like AdminUser, etc. Now go to localhost/my_new_app/users/add and create a user with a specific role.

Create ACL ACOs

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

bin/cake acl_extras aco_sync

Now remove that temporary Auth we add in step 11.

Permissions using the ACL shell

Now we can set permission to the group which can see like Admin can do anything but a 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 the below command

sudo chmod -R 777 cakeacl/app/tmp

Now you can log in and check Thatโ€™s all hope this will help you to create your Cake Application. ๐Ÿ™‚

Related Posts