Simpler Role Based Authorization in Yii 2.0

UPDATE: Starting with version 2.0.2, Yii2 Advanced Template does not contain “role” column in the User table by default. Before proceeding to the tutorial below, do the following:

  1. Create a column called role in the user table.
  2. Update the User model by adding the role attribute and updating the User class docblock accordingly.

Yii 2.0 has a built in Access Control that supports 2 roles out of the box to check whether the user is a guest or if the user is logged in. Sometimes there is a need to simply extend the Access Control Layer with few more roles to distinguish the logged in users i.e. admin, moderator, without the full blown RBAC graph with permissions, roles and role assignments that Yii provides.

In this post, I will show how to implement simple Role Based authorization by simply extending the AccessRule class that defines the default rules and overriding the matchRule() function call, which will provide the additional rule matching logic.

I will be using the advanced template, but you can adapt the code to basic template without much difficulty. The concept remains the same in both.

Let’s start by defining our roles. I will be creating 2 new roles, namely Admin and Moderator, in addition to the existing User role. These roles will allow us to distinguish the roles of the logged in users, and also allow us to restrict access to different parts of the application depending on the role assigned to the user.

Let’s get started with some code.


  1. Create a AccessRule class that extends the \yii\filters\AccessRule class. I have chosen to do so inside the common\components namespace. You may need to create the components folder inside common folder. Populate common\components\AccessRule.php with the following code:
    <?php
    
    namespace common\components;
    
    
    class AccessRule extends \yii\filters\AccessRule {
    
        /**
         * @inheritdoc
         */
        protected function matchRole($user)
        {
            if (empty($this->roles)) {
                return true;
            }
            foreach ($this->roles as $role) {
                if ($role === '?') {
                    if ($user->getIsGuest()) {
                        return true;
                    }
                } elseif ($role === '@') {
                    if (!$user->getIsGuest()) {
                        return true;
                    }
                // Check if the user is logged in, and the roles match
                } elseif (!$user->getIsGuest() && $role === $user->identity->role) {
                    return true;
                }
            }
    
            return false;
        }
    }

    Here we are overriding the matchRole() function. Much of the matchRole() code is copied from the \yii\filters\AccessRule class except we have now changed the last elseif-statement to match the role supplied inside the controller with the role defined in the database and the User model.

  2. Inside the common/models/User.php, we will define our new roles. There is already a const ROLE_USER = 10;  in the User model. We will add our new roles right below it so that our role constants look like this:
    const ROLE_USER = 10;
    const ROLE_MODERATOR = 20;
    const ROLE_ADMIN = 30;
  3. Now it’s time to use our new roles inside the controller. To use the new roles, we will use the new AccessRule component and make slight changes to the access behavior. Add the following use statements on top of your controller:
    use common\components\AccessRule;
    use common\models\User;

    Edit the access behavior as follows:

    'access' => [
        'class' => AccessControl::className(),
        // We will override the default rule config with the new AccessRule class
        'ruleConfig' => [
            'class' => AccessRule::className(),
        ],
        'only' => ['create', 'update', 'delete'],
        'rules' => [
            [
                'actions' => ['create'],
                'allow' => true,
                // Allow users, moderators and admins to create
                'roles' => [
                    User::ROLE_USER,
                    User::ROLE_MODERATOR,
                    User::ROLE_ADMIN
                ],
            ],
            [
                'actions' => ['update'],
                'allow' => true,
                // Allow moderators and admins to update
                'roles' => [
                    User::ROLE_MODERATOR,
                    User::ROLE_ADMIN
                ],
            ],
            [
                'actions' => ['delete'],
                'allow' => true,
                // Allow admins to delete
                'roles' => [
                    User::ROLE_ADMIN
                ],
            ],
        ],
    ],

    In the above example, we are first overriding the ruleConfig of the access behavior with our new AccessRule class. Then there are 3 actions create, update and delete which are controlled by our new AccessRule class and the following rules are applied.

    • create action is available to User, Moderator and Admin roles.
    • update actions is available to Moderator and Admin roles.
    • delete action is only available to the Admin role.

There you have it. Clean and simple role based authorization for applications that require more than simple logged-in check, but don’t require full blown Role Based Access Control provided by Yii 2.0.

Further Reading

http://www.yiiframework.com/doc-2.0/guide-security-authorization.html
http://www.yiiframework.com/doc-2.0/yii-filters-accessrule.html
http://www.yiiframework.com/doc-2.0/yii-filters-accesscontrol.html

Bonus Tip

Go through the Yii 2.0 source code. There are many times where you will think “Yii does x very nicely, I wish it did y too in the same manner”. The way Yii is structured, it is very easy to extend the functionality on top of the core Yii library without changing the core files. The core Yii code is very well commented and organized and it is a good source of learning PHP itself.

47 thoughts on “Simpler Role Based Authorization in Yii 2.0”

    1. Hi Ankur,

      Starting with v2.0.2, the advanced template no longer has role attribute included by default. To get the above tutorial working, do these 2 extra steps:

      1. Create a column called “role” in the user table.
      2. Update the User model by adding the role attribute and updating the docblock accordingly.

      From then on, make sure you assign a role to all users upon creation.

      I will update the post with proper code at a later time.

      Good luck!

  1. Great article and very useful for most people who need some level control and not the full blow RBAC. This should be default yii capability but for some reason they give you an all or nothing option instead.
    Great job.
    The only thing I would recommend is giving more info on adding “role” to the User model as it was a bit tricky to figure out when the model does not contain it at all.
    Thank you

    1. Hi Max,

      Look at the point #3 regarding ‘access’ behavior in the controller. There are 2 main steps:
      1. Overriding the ruleConfig class with the one we created;
      2. Setting up the rules array to allow/deny access to certain roles.

    1. The data type can be anything you would like it to be. I like to keep it as int, and then in the model define the roles as constants i.e. ROLE_ADMIN = 1, ROLE_USER = 2 and so on.

    1. Hi,

      Try using 'class' => \yii\filters\AccessControl::className(), in the access behavior definition.

      My IDE has code completion and detects the AccessControl in the correct namespace.

      Hope this helps.

  2. Is it possible to integrate Simple Role Based Authorization to Humhub with a module like “Privacy” or “AccessControl”?

    1. Hi,

      I am not an expert on HumHub and currently cannot go through the source. Maybe open an issue on HumHub’s github repo and request the integration?

  3. I got this error :-

    Forbidden (#403)
    You are not allowed to perform this action.

    Can you help me solve it? im using yii2 advanced template, and followed steps above.

  4. I have the same issue as the user above. I want to allow/deny the whole controller (not just some actions) to anyone who’s not the admin, so I have this behavior:
    ‘access’ => [
    ‘class’ => AccessControl::className(),
    ‘ruleConfig’ => [‘class’ => AccessRule::className()],
    ‘only’ => [‘index’,’create’,’update’,’delete’,’view’],
    ‘rules’ => [
    [
    ‘actions’ => [‘index’,’create’,’update’,’delete’,’view’],
    ‘allow’ => true,
    ‘roles’ => [User::ROLE_ADMIN],
    ],
    ],

    I also tried taking out actions and only but the result is the same.

  5. I use this tutorial at the basic project but it does not work.I make the role field and insert the code as explained.At user model etc.
    The user can not see the action even I have the write “10” at role field

  6. Can we put in hte config main.php

    like

    ‘components’ => [
    ………………
    “ruleConfig” => [“class” => “frontend\components\extra\AccessRule”],
    …………………..
    ]

  7. Please add how to put the component into the config/web.php.
    Otherwise, nice tutorial, thanks!

  8. Hello,
    Nice article
    There is no User::ROLE_USER, in common\models\User. The only const we have is const STATUS_ACTIVE = 10;
    Please correct you code so that i know what to do.
    Thanks

  9. There is no
    const ROLE_USER = 10;
    in the User.php in common\models. There are only
    const STATUS_DELETED = 0;
    const STATUS_ACTIVE = 10;

    what am I doing wrong?

  10. Hi sir! I followed your tutorial and I got an error:

    Unknown Property – yii\base\UnknownPropertyException

    Setting unknown property: common\components\AccessRule::ruleConfig

    How can I solve this?

  11. I would also like to know how to get this setup in the config/web.php file so it applies to the whole site rather than having to put this in each individual controller.

Leave a Reply

Your email address will not be published. Required fields are marked *