= Yii 2 Role Based Access Control =
== Configuration ==
Enable RBAC in ''common/config/main.php'' (''app/config/console.php'' and ''app/config/test.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'':
[
'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'':
[
'admin',
],
2 => [
'registered',
],
1801 => [
'registered',
],
//...
];
File ''@common/rbac/data/rules.php'':
'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 '' . $statuses[$aStatus] . '';
case self::STATUS_INACTIVE:
return '' . $statuses[$aStatus] . '';
default:
return '' . $statuses[$aStatus] . '';
}
} 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'':
createdBy == $user : false;
}
}
''common/rbac/UserRoleRule.php'':
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:
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 to ''defaultRoles => [...]'' array in ''common/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
],
],
],
...
];
}
== References ==
See also:
* [[http://www.yiiframework.com/doc-2.0/guide-security-authorization.html|Yii 2 Guide: Security Authorization]]
* [[http://www.fabioferreira.pt/rbac-with-yii2-user-quick-tutorial/|RBAC with Yii2-User - Quick Tutorial]]
* [[http://www.yiiframework.com/forum/index.php/topic/60439-yii2-rbac-permissions-in-controller-behaviors/#entry269913|Forum: Yii 2 RBAC Permissions in Controller Behaviors]]
* Sample RBAC implementations:
* [[https://github.com/zelenin/yii2-rbac-module]]
* [[https://github.com/vova07/yii2-rbac-module]]
* [[https://github.com/trntv/yii2-starter-kit]]