This is the first post of a small series covering how to setup, organize and implement an RBAC based authorization system in CakePHP using the CakeDC/Auth Plugin. We'll cover the basic concepts, setup and implementation of the basic permission rules in part 1.
What does RBAC mean in this context?
We'll use RBAC as "Role Base Access Control", meaning your app will be using the following concepts to define who can access what:
- "Who" is an existing user, mainly identified as his role in the system, such as an "admin" or "writer", etc.
- "What" is a specific action in your application, identified as the associated routing params, for example ['controller' => 'Posts', 'action' => 'add'].
- A permission in this context would be a link between who, and what.
Why not ACL?
ACL is a really good choice when your answer is 'yes' to any of the following questions:
- Do we need to let users create new roles on the fly?
- Do we need the roles to inherit permissions (tree structure)?
- Do we need to assign permissions NOT based on controller actions? For example CRUD based permissions, checked on the model layer for each operation on a given row.
If your answer is yes, you should consider using cakephp/acl. It provides a very powerful, reliable and flexible way to configure your permissions, but with greater power comes a bigger maintenance burden, that is keeping the acl data in your tables. Specially if you have several environments to maintain, you'll need to write migrations to populate your acl tables, then create import/export scripts and utilities to reproduce permission issues from live environments, and so on. Not an impossible task, but could increase the complexity of your project in a significant way...
Setting up CakeDC/Auth
There are other plugins you could use, but this one will cover everything you'll need, so let's go.
composer require cakedc/auth
bin/cake plugin load CakeDC/Auth
And last, but not least, add the RBAC Auth to the list of Authorize objects. Here is a working configuration based on the blog tutorial.
We'll be using the blog tutorial described in the book as an example application Change AppController.php Auth related configuration to:
$this->loadComponent('Auth', [ 'authorize' => ['CakeDC/Auth.SimpleRbac'], 'loginRedirect' => [ 'controller' => 'Articles', 'action' => 'index' ], 'logoutRedirect' => [ 'controller' => 'Pages', 'action' => 'display', 'home' ] ]);
With this change, we'll be using only the rules defined in config/permissions.php file. If this file is not present, default permissions will be in place. Default permissions will grant access to
admin role to all actions. To override permissions, you can copy the default permissions to your project and fix the rules:
cp vendor/cakedc/auth/config/permissions.php config/
Then edit this file and check the provided examples and defaults.
The core of the RBAC system is the ability to define permission rules that will match one given role with the actions granted. Rules are defined in an array, but you can extend the AbstractProvider class to retrieve the rules from somewhere else (database?). By default, nothing will be granted. Rules are evaluated top to bottom. The first rule matched will stop the evaluation, and the authentication result will be provided by the value of the
allowed key. Note we can use a callback to implement complex rules, or encapsulate the rules into classes that we could reuse across projects, like the
Owner rule class provided.
This is an example rule
[ 'role' => '*', 'plugin' => 'CakeDC/Users', 'controller' => 'Users', 'action' => ['profile', 'logout'], ],
We could read this rule as follows: "For any role, we grant access to actions 'profile' and 'logout' in the Users controller in plugin CakeDC/Users". Note default allowed value is
true, we can use an array, or a string to determine the role, plugin, controller and action. We can also use
* in the value to match anything or use
* at the start of the key to match anything but the values, for example
'*controller' => 'Users',
would match all the controllers but 'Users'.
As our first objective, we are going to grant access to all the index and view pages, using the rule
[ 'role' => '*', 'controller' => '*', 'action' => ['index', 'view'], ],
Stay tuned for the second post, where we'll deal with complex rules, rule classes and implementation tips for complex applications...