Yii 1.1: Simple RBAC

41 followers

If you need simple Role based access control without the long RBAC process then this article is just for you. Lets jump to the point.

The user model

On your user table make a column named 'roles'. Create the model accordingly. It will be named "User" here.

When you add users you can assign them a role among 'admin', 'user', 'staff' etc etc.

The authentication

In the file "protected/components/UserIdentity.php" write something like:

class UserIdentity extends CUserIdentity
{
    private $id;
 
    public function authenticate()
    {
        $record=User::model()->findByAttributes(array('email'=>$this->username));
        if($record===null)
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        else if($record->password!==md5($this->password))
            $this->errorCode=self::ERROR_PASSWORD_INVALID;
        else
        {
            $this->id=$record->id;
            $this->setState('roles', $record->roles);            
            $this->errorCode=self::ERROR_NONE;
        }
        return !$this->errorCode;
    }
 
    public function getId(){
        return $this->id;
    }
}

The important line is $this->setState('roles', $record->roles); It adds user roles to their session. You can fetch the role of the current user with Yii::app()->user->getState('roles') or simply Yii::app()->user->roles.

Checking permissions: structure

Modify or create the "WebUser.php" file under the "protected/components" directory so that it overloads the checkAccess() method.

<?php 
class WebUser extends CWebUser
{
    /**
     * Overrides a Yii method that is used for roles in controllers (accessRules).
     *
     * @param string $operation Name of the operation required (here, a role).
     * @param mixed $params (opt) Parameters for this operation, usually the object to access.
     * @return bool Permission granted?
     */
    public function checkAccess($operation, $params=array())
    {
        if (empty($this->id)) {
            // Not identified => no rights
            return false;
        }
        $role = $this->getState("roles");
        if ($role === 'admin') {
            return true; // admin role has access to everything
        }
        // allow access if the operation request is the current user's role
        return ($operation === $role);
    }
}

You can define your own logic in this checkAccess() methods.

Make sure this class is used by Yii. The config file "protected/config/main.php" must contain:

'components' => array(
    // ...
    'user' => array(
        'class' => 'WebUser',
    ),

Sidenote:
CWebUser::checkAccess() usually connects to the authorization system loaded in Yii. Here we are replacing it with a simple system that just deals with roles instead of the hierarchical system defined by the derivatives of CAuthManager. See the official tutorial, Role-Based Access Control for details.

Checking permissions: usage

  • In your PHP code, use Yii::app()->user->checkAccess('admin') to check if the current user has the 'admin' role. The call Yii::app()->user->checkAccess('staff') will return true if the user has the role "staff" or "admin".
  • In your controller, you can filter with accessRules() using the "roles" attribute of the rule.

See examples below.

How to filter access to actions

The controller must contain:

public function filters()
{
    return array(
        'accessControl', // perform access control for CRUD operations
    );
}
 
public function accessRules()
{   
    return array(
        array('allow',
            'actions'=>array('admin'),
            'roles'=>array('staff', 'devel'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

Here the "admin" action of the controller has restricted access: only those with roles "staff" or "devel" can access it.

As described in the API doc of CAccessRule, the "roles" attribute will in fact call Yii::app()->user->checkAccess().

How to display a different menu according to roles

You can also use just one menu for all users based upon different roles. for example

<?php
$user = Yii::app()->user; // just a convenience to shorten expressions
$this->widget('zii.widgets.CMenu',array(
    'items'=>array(             
        array('label'=>'Users', 'url'=>array('/manageUser/admin'), 'visible'=>$user->checkAccess('staff')),
        array('label'=>'Your Ideas', 'url'=>array('/userarea/ideaList'), 'visible'=>$user->checkAccess('normal')),
        array('label'=>'Login', 'url'=>array('/site/login'), 'visible'=>$user->isGuest),
        array('label'=>'Logout ('.Yii::app()->user->name.')', 'url'=>array('/site/logout'), 'visible'=>!$user->isGuest)
    ),
));
?>

Going further: access context

A very usual need is to allow a user to update its own data but not other's. In this case, the user's role is meaningless without the context: the data that will be modified.

This is why CWebUser::checkAccess() has an optional "$param" parameter. Now suppose we want to check is a user has the right to update a Post record. We can write:

if (Yii::app()->user->checkAccess('normal', $post)) {

Of course WebUser::checkAccess() must be extended to use this "$params" parameter. This will depend on your application's logic. For instance, it could be as simple as granting access iff $post->userId == $this->id.

Total 15 comments

#16647 report it
hasanavi at 2014/03/14 10:50am
Regarding Roles Column

Hi,

On your user table make a column named 'roles'.

Details described here.

http://www.yiiframework.com/wiki/328/simple-rbac/#hh0

#16642 report it
AussieNeil at 2014/03/14 08:16am
Arnold Gultom

Hi,

On your User model did you create a column called role or roles. In the example they have created a column called roles, which is not clear.

The key is this line:

$this->setState('roles', $record->roles);

It says assign the column "roles" from the record to a state variable called "roles".

Hope this helps.

Neil

#16614 report it
Arnold Gultom at 2014/03/11 12:07am
Property User.roles is not defined

Hi, I just a newbie in Yii. I have read this article and followed all instructions here, but I had error User.roles is not defined when I tried to login. Here is my UserIdentity.php

<?php
class UserIdentity extends CUserIdentity
{
    private $_id;
 
    public function authenticate()
    {
        $user = User::model()->findByAttributes(array
                ('username'=>$this->username));
 
        if($user===null){
            $this->errorCode=self::ERROR_USERNAME_INVALID;
        }
        else{
            if($user->password!==$user->encrypt($this->password)){
                $this->errorCode=self::ERROR_PASSWORD_INVALID;
            }
            else{
                $this->_id = $user->username;
                $this->setState('roles', $user->roles);
                $this->errorCode=self::ERROR_NONE;
            }
        }
        return !$this->errorCode;
    }
 
    public function getId(){
        return $this->_id;
    }
}

And then EWebUser.php

<?php 
class EWebUser extends CWebUser
{
    public function checkAccess($operation, $params=array())
    {
        if (empty($this->id)) {
            // Not identified => no rights
            return false;
        }
        $role = $this->getState("roles");
        if ($role === 'admin') {
            return true; // admin role has access to everything
        }
        // allow access if the operation request is the current user's role
        return ($operation === $role);
    }
}

At last accessRules method in UserController.php

public function accessRules()
    {
          .....
        return array(
            array('allow', // allow admin user to perform 'admin' and 'delete' actions
                'actions'=>array('admin','delete'),
                //'users'=>array('admin'),
                'roles'=>array('admin'),
        .....
        );
    }

I hope anyone can help solve this problem, thank you very much.

#14935 report it
Bjorn at 2013/09/23 07:50am
Great and simple

Work great for small projects, thanks for the wiki!

#11351 report it
hari maliya at 2013/01/08 04:08am
I con't access action according to role ?

Hi guys

I try to all type of set access role and set role , I got role in controller but this can't be access a action page

thnaks

hari maliya

#11343 report it
hari maliya at 2013/01/07 08:31am
How to create Access (view page) role in yii ?

Hi guys

How to create access role in yii and where in yii application part ?

I want to create access role in yii application but i have a problem and dont know about where to assign role in yii like i have three department role 1.admin -: admin have a all access role in our application 2.staff -: staff same of page and access role like to edit or update 3.user -: user have a all access page only viewing in our application

These type of role can set in controller but i can justify where to write all access in yii and how to set access role ,

thank hari maliya

#10792 report it
Jonas at 2012/11/23 05:30pm
One user with multiple roles

If you want to assign multiple roles to a user (separated by a comma) you can replace

$role = $this->getState("roles");
if ($role === 'admin') {
   return true; // admin role has access to everything
}
// allow access if the operation request is the current user's role
return ($operation === $role);

with

$roles = explode(',', $this->getState("roles"));
 
if (in_array('admin', $roles) || in_array($operation, $roles))
   return true;
#10633 report it
Giacomo at 2012/11/09 08:41am
checkAccess

Sorry guys, but this point it's not really clear for me

"The call Yii::app()->user->checkAccess('staff') will return true if the user has the role "staff" or "admin"."

What do i've to specify inside checkAccess? In my app, I've 3 user types (admin, operator, account) and i'd like to filter the access inside controllers and filter displayed content.

Inside the controller, i use

public function accessRules()
    {
        return array(
            array('allow', 
                'roles'=>array(User::ROLE_ADMIN,User::ROLE_OPERATOR),
            ),
            array('deny',  // deny all users
                'users'=>array('*'),
            ),
        );
    }

where User::ROLE_ADMIN and User::ROLE_OPERATOR are int 1 and 2, and it's the value assigned inside userIdentity

$this->setState('roles', $record->role_id);

where $record->role_id can be 1 or 2

But it's not working, if i chanche the accessRules with

'roles'=>array(User::ROLE_ADMIN),

users with User::ROLE_OPERATOR can access anyway. :(

Any suggestion? Thank you

ps:

this is the code inside checkAccess

$role_id = $this->getState('roles');
                if(($role_id == User::ROLE_ADMIN) || ($role_id == User::ROLE_OPERATOR))
            return true;
        else
            throw new CHttpException(404, '');
#10206 report it
sidtj at 2012/10/11 03:24am
How to display a different menu according to roles:

You can also use YiiSmartMenu extension.

$this->widget('ext.YiiSmartMenu',array( //calls YiiSmartMenu
    'items'=>array(             
        array('label'=>'Users', 'url'=>array('/manageUser/admin'), 'authItemName'=>'staff', //define what should be checked
...
#9897 report it
cgsmith at 2012/09/19 02:02pm
Multiple Role Check

Hello,

I needed to be able to check for multiple roles. With a minor change I can now do this:

Yii::app()->user->checkAccess('user,manager,admin')

*Note: you can do any combination | ; , . I just choose commas. The command will find any match.

Place the following below $role === 'admin' in protected/components/WebUser.php

if (strstr($operation,$role) !== false) { // Check if multiple roles are available
     return true;
}

Full protected/components/WebUser.php file:

<?php
class WebUser extends CWebUser
{
    /**
     * Overrides a Yii method that is used for roles in controllers (accessRules).
     *
     * @param string $operation Name of the operation required (here, a role).
     * @param mixed $params (opt) Parameters for this operation, usually the object to access.
     * @return bool Permission granted?
     */
    public function checkAccess($operation, $params=array())
    {
        if (empty($this->id)) { // Not identified => no rights
            return false;
        }
 
        $role = $this->getState("roles"); // Get role of user
        if ($role === 'admin') {
            return true; // admin role has access to everything
        }
 
        if (strstr($operation,$role) !== false) { // Check if multiple roles are available
            return true;
        }
 
        return ($operation === $role);// allow access if the operation request is the current user's role
    }
}
#9766 report it
Mao Danh at 2012/09/09 11:00am
very useful

I had the idea, thanks.

#9616 report it
joyal at 2012/08/29 06:35am
Thanks

Thank you very much for the text.

#8183 report it
SpiLLeR at 2012/05/15 06:52am
Importantly

If set autoLogin=>true in config all data will saved in cookie and i can change my role. WebUser it's better place for method getRole().

#8099 report it
Trejder at 2012/05/10 03:34am
Great text!

Hi, Thank you for a great text! I've been looking for "as simpliest as possible" solution like that, to incorporate it into smaller projects, that do not need a complex, "heavy" RBAC module. Thank you again.

#7967 report it
Sukhwinder at 2012/04/30 02:26am
The method isAdmin, isUser are not static

I have one doubt, since the methods isAdmin, isUser etc of Utils class are not static (public Methods) How can we refer them as Utils::isAdmin in access rules or i Zii widgest?

Leave a comment

Please to leave your comment.

Write new article