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:

Create a column called role in the user table. 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.

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; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <?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. 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; 1 2 3 const ROLE_USER = 10 ; const ROLE_MODERATOR = 20 ; const ROLE_ADMIN = 30 ; 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; 1 2 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 ], ], ], ], 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 '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.

action is available to , and roles. update actions is available to Moderator and Admin roles.

actions is available to and 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.