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:

<?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 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