This is an old revision of the document!
Yii 2 Role Based Access Control
Configuration
Enable RBAC in common/config/main.php
(console/config/main.php
and test/config/main.php
as well), by adding component authManager
and assigning one of these authorization managers: PhpManager
or DbManager
.
PhpManager
When using simple file based roles, enable PhpManager
:
return [ 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'components' => [ ... 'authManager' => [ 'class' => 'yii\rbac\PhpManager', 'defaultRoles' => ['admin', 'editor', 'author', 'registered'], // Default roles // By default, yii\rbac\PhpManager stores RBAC data in files under @app/rbac/ directory. // Here, let us place them in @common/rbac/, and make sure dir is web writable. 'itemFile' => '@common/rbac/data/items.php', // Default path to items.php 'assignmentFile' => '@common/rbac/data/assignments.php', // Default path to assignments.php 'ruleFile' => '@common/rbac/data/rules.php', // Default path to rules.php ], ], ];
File @common/rbac/data/items.php
:
<?php return [ //--------------------------- // PERMISSIONS //--------------------------- 'createUsers' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'Create Users', ], 'updateUserProfile' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'Update User Profile', ], 'createContent' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'Create Content', ], 'updateContent' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'Update Content', ], 'updateOwnContent' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'Update own content', 'ruleName' => 'isAuthor', 'children' => [ 'updateContent', ], ], 'viewTechnicalManual' => [ 'type' => yii\rbac\Item::TYPE_PERMISSION, //2, 'description' => 'View Technical Manual', ], //--------------------------- // ROLES //--------------------------- 'registered' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', ], 'module' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', 'children' => [ 'registered', 'viewTechnicalManual', ], ], 'poweruser' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', 'children' => [ 'module', ], ], 'author' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', 'children' => [ 'poweruser', 'createContent', 'updateOwnContent', ], ], 'editor' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', 'children' => [ 'author', 'updateUserProfile', 'updateContent', ], ], 'admin' => [ 'type' => yii\rbac\Item::TYPE_ROLE, //1, 'ruleName' => 'userRole', 'children' => [ 'editor', 'createUsers', 'updateContent', ], ], ];
File '@common/rbac/data/assignments.php
:
<?php return [ 1 => [ 'admin', ], 2 => [ 'registered', ], 1801 => [ 'registered', ], //... ];
File @common/rbac/data/rules.php
:
<?php return [ 'userRole' => 'O:24:"common\\rbac\\UserRoleRule":3:{s:4:"name";s:8:"userRole";s:9:"createdAt";N;s:9:"updatedAt";N;}', 'isAuthor' => 'O:22:"common\\rbac\\AuthorRule":3:{s:4:"name";s:8:"isAuthor";s:9:"createdAt";N;s:9:"updatedAt";N;}', ];
DbManager
When using enterprise level DB based roles, enable DbManager
:
return [ 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'components' => [ ... 'authManager' => [ 'class' => 'yii\rbac\DbManager', // NOTE: DbManager requires to migrate first: // $ yii migrate --migrationPath=@yii/rbac/migrations ], ], ];
DbManager requires to migrate first. Execute the following command at the yii console:
$ yii migrate --migrationPath=@yii/rbac/migrations
Roles
Define user roles in [app]\common\models\User.php
... class User extends ActiveRecord implements IdentityInterface { // Roles const ROLE_ADMIN = 1; // Super user const ROLE_MANAGER = 2; // Create/update content, edit user profiles const ROLE_EDITOR = 3; // Create/update content const ROLE_AUTHOR = 4; // Create content, update own content only const ROLE_POWERUSER = 5; // Access to company content (not for public) const ROLE_USER = 10; // Basic site use. Same as ROLE_REGISTERED const ROLE_REGISTERED = 10; // Basic site use. Same as ROLE_USER //... public function rules() { return [ ['role', 'default', 'value' => self::ROLE_REGISTERED], ['role', 'in', 'range' => [ self::ROLE_ADMIN, self::ROLE_MANAGER, self::ROLE_EDITOR, self::ROLE_AUTHOR, self::ROLE_POWERUSER, self::ROLE_REGISTERED, ]], //... ]; } /** * @inheritdoc */ public function getRole() { return $this->role; //return Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId()); // requires authManager //return Yii::$app->authManager->getRolesByUser(User::findByUsername($this->username)->getId()); // requires authManager } public static function getRoleLabel($aRole) { $roles = self::getRoles(); return $roles[$aRole]; } public static function getRoles() { return [ self::ROLE_ADMIN => 'admin', self::ROLE_MANAGER => 'manager', self::ROLE_EDITOR => 'editor', self::ROLE_AUTHOR => 'author', self::ROLE_USER => 'user', self::ROLE_POWERUSER => 'poweruser', self::ROLE_REGISTERED => 'registered', ]; } }
Status
Define user status in [app]\common\models\User.php
... class User extends ActiveRecord implements IdentityInterface { // Status const STATUS_BANNED = -2; const STATUS_DELETED = -1; const STATUS_INACTIVE = 0; const STATUS_ACTIVE = 1; //... public function rules() { return [ ['status', 'default', 'value' => self::STATUS_INACTIVE], ['status', 'in', 'range' => [ self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED, self::STATUS_BANNED, ]], //... ]; } public static function getStatusLabel($aStatus, $is_html=true) { $statuses = self::getStatuses(); if ($is_html) { switch($aStatus) { case self::STATUS_ACTIVE: return '<span class="label label-success">' . $statuses[$aStatus] . '</span>'; case self::STATUS_INACTIVE: return '<span class="label label-warning">' . $statuses[$aStatus] . '</span>'; default: return '<span class="label label-danger">' . $statuses[$aStatus] . '</span>'; } } else { return $statuses[$aStatus]; } } public static function getStatuses() { return [ self::STATUS_BANNED => 'banned', self::STATUS_DELETED => 'deleted', self::STATUS_INACTIVE => 'inactive', self::STATUS_ACTIVE => 'active', ]; } }
Rules
Create basic rules:
common/rbac/AuthorRule.php
:
<?php namespace common\rbac; use yii\rbac\Rule; /** * Checks if authorID matches user passed via params */ class AuthorRule extends Rule { public $name = 'isAuthor'; /** * @param string|integer $user the user ID. * @param Item $item the role or permission that this rule is associated with * @param array $params parameters passed to ManagerInterface::checkAccess(). * @return boolean a value indicating whether the rule permits the role or permission it is associated with. */ public function execute($user, $item, $params) { return isset($params['content']) ? $params['content']->createdBy == $user : false; } }
common/rbac/UserRoleRule.php
:
<?php namespace common\rbac; use yii\rbac\Rule; use common\models\User; /** * Checks if Registered role matches user passed via params */ class UserRoleRule extends Rule { public $name = 'userRole'; /** * @param string|integer $user the user ID. * @param Item $item the role or permission that this rule is associated with * @param array $params parameters passed to ManagerInterface::checkAccess(). * @return boolean a value indicating whether the rule permits the role or permission it is associated with. */ public function execute($user, $item, $params) { // check the role from table User if(isset(\Yii::$app->user->identity->role)) { $role = \Yii::$app->user->identity->role; } else { return false; } if ($item->name === 'admin') { return $role == User::ROLE_ADMIN; } elseif ($item->name === 'editor') { // editor is a child of admin return $role == User::ROLE_ADMIN || $role == User::ROLE_EDITOR; } elseif ($item->name === 'author') { // author is a child of editor and admin return $role == User::ROLE_ADMIN || $role == User::ROLE_EDITOR || $role == User::ROLE_AUTHOR; } elseif ($item->name === 'poweruser') { // poweruser is a child of author, editor and admin return $role == User::ROLE_ADMIN || $role == User::ROLE_EDITOR || $role == User::ROLE_AUTHOR || $role == User::ROLE_POWERUSER; } elseif ($item->name === 'registered') { // registered is a child of author, editor, and admin. // if we have no role defined, this is also the default role. return $role == User::ROLE_ADMIN || $role == User::ROLE_EDITOR || $role == User::ROLE_AUTHOR || $role == User::ROLE_REGISTERED || $role == NULL; } else { return false; } } }
Init Script
Create console script console/controllers/RbacController.php
to generate permissions and roles, then add them to users:
<?php //---------------------------------------------------------------------------------------- // Controller: /console/controllers/RbacController.php // Documentation: http://www.yiiframework.com/doc-2.0/guide-security-authorization.html // Usage: // - Create data directory for RBAC settings files (items.php, assignments.php, rules.php): // $ mkdir ../common/rbac/data // - Execute once as a console script: // $ yii rbac/init //---------------------------------------------------------------------------------------- namespace console\controllers; use Yii; use yii\console\Controller; use common\rbac\AuthorRule; use common\rbac\UserRoleRule; class RbacController extends Controller { public function actionInit() { $auth = Yii::$app->authManager; $auth->removeAll(); // remove previous rbac data files under common/rbac/data //----------------------------------- // Rules //----------------------------------- // Add rules $ruleUserRole = new \common\rbac\UserRoleRule(); $auth->add($ruleUserRole); $ruleAuthor = new \common\rbac\AuthorRule(); $auth->add($ruleAuthor); //----------------------------------- // Permissions //----------------------------------- // Permission to create users $permCreateUsers = $auth->createPermission('createUsers'); $permCreateUsers->description = 'Create Users'; $auth->add($permCreateUsers); // Permission to edit user profile $permUpdateUserProfile = $auth->createPermission('updateUserProfile'); $permUpdateUserProfile->description = 'Update User Profile'; $auth->add($permUpdateUserProfile); // add "createContent" permission $permCreateContent = $auth->createPermission('createContent'); $permCreateContent->description = 'Create Content'; $auth->add($permCreateContent); // add "updateContent" permission $permUpdateContent = $auth->createPermission('updateContent'); $permUpdateContent->description = 'Update Content'; $auth->add($permUpdateContent); //----------------------------------- // Roles //----------------------------------- // add "registered" role with only basic permissions $roleRegistered = $auth->createRole('registered'); $roleRegistered->ruleName = $ruleUserRole->name; $auth->add($roleRegistered); // add permissions as children of 'registered' ... //none in this example // add "author" role, and give this role the "createContent" permission $roleAuthor = $auth->createRole('author'); $roleAuthor->ruleName = $ruleUserRole->name; $auth->add($roleAuthor); $auth->addChild($roleAuthor, $roleRegistered); // 'registered' is a child of 'author' // add permissions as children of 'author' $auth->addChild($roleAuthor, $permCreateContent); // editor role $roleEditor = $auth->createRole('editor'); $roleEditor->ruleName = $ruleUserRole->name; $auth->add($roleEditor); $auth->addChild($roleEditor, $roleAuthor); // 'author' is a child of 'editor' // add permissions as children of 'editor' $auth->addChild($roleEditor, $permUpdateUserProfile); // 'editor' can edit profiles $auth->addChild($roleEditor, $permUpdateContent); // 'editor' role can update content // add "admin" role and give this role the "updateContent" permission // as well as the permissions of the "author" role $roleAdmin = $auth->createRole('admin'); $roleAdmin->ruleName = $ruleUserRole->name; $auth->add($roleAdmin); $auth->addChild($roleAdmin, $roleEditor); // 'editor' is child of 'admin', for consequence 'author' and 'registered' is also child of 'admin' // add permissions as children of 'admin' $auth->addChild($roleAdmin, $permCreateUsers); // admin role can create users and also edit users because is parent of editor $auth->addChild($roleAdmin, $permUpdateContent); // admin role can update content //----------------------------------- // Permissions with dependencies in Roles/Rules //----------------------------------- // add the "updateOwnContent" permission and associate the rule with it. $permUpdateOwnContent = $auth->createPermission('updateOwnContent'); $permUpdateOwnContent->description = 'Update own content'; $permUpdateOwnContent->ruleName = $ruleAuthor->name; // add permissions $auth->add($permUpdateOwnContent); $auth->addChild($permUpdateOwnContent, $permUpdateContent); // "updateOwnContent" will be used from "updateContent" $auth->addChild($roleAuthor, $permUpdateOwnContent); // allow "author" to update their own contents //----------------------------------- // Roles assignment to users //----------------------------------- // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId() // usually implemented in your User model. $auth->assign($roleRegistered, 2 /* user_id */); $auth->assign($roleAdmin, 1 /* user_id */); } }
Default Role
Add support to set default user role when signing up new users. Modify frontend\models\SignupForm::signup()
as follows:
public function signup() { if ($this->validate()) { $user = new User(); ... $user->save(); // Set default role for new user. // This block is required when using authManager (see /common/config/main.php): $auth = Yii::$app->authManager; $authorRole = $auth->getRole('registered'); // set a default role, eg 'registered' $auth->assign($authorRole, $user->getId()); return $user; } return null; }
Initialize Database
If using yii\rbac\PhpManager
(instead of DbManager
), create a data directory for RBAC settings files (items.php, assignments.php, rules.php):
$ mkdir /common/rbac/data
Execute console script:
$ yii rbac/init
If you need to create new permissions and/or roles:
- Add permissions and roles to
console/controllers/RbacController.php
, - Add new roles to
common/rbac/UserRoleRule.php
and todefaultRoles ⇒ […]
array incommon/config/main.php
. - Execute the console script again. Eg:
$ yii rbac/init
Getting Access
Querying Permissions with user->can()
To use RBAC roles and permissions, use it like this:
if(\Yii::$app->user->can('createUser')) { // call view with form to create users } else { // call view telling the users that cannot create users } // OR if (\Yii::$app->user->can('updateContent', ['content' => $content])) { // update content }
Querying Permissions with authManager->getRolesByUser()
use app\models\User; // Get user roles (requires authManager to be configured) if (in_array(User::ROLE_ADMIN, Yii::$app->authManager->getRolesByUser(Yii::$app->user->getId()))) { // Do something }
Applying Access Control Rules
For system wide access control, setup access control rules in the required controller. Eg: For CustomersController
, we need the following:
public function behaviors() { return [ 'access' => [ 'class' => AccessControl::className(), 'rules' => [ [ 'actions' => ['add'], 'roles' => ['manager'], 'allow' => true ], [ 'actions' => ['index', 'query'], 'roles' => ['user'], 'allow' => true ], ], ], ... ]; }