- The user model
- The authentication
- Checking permissions: structure
- Checking permissions: usage
- Going further: access context
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 callYii::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
.
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?
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.
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().
Thanks
Thank you very much for the text.
very useful
I had the idea, thanks.
Multiple Role Check
Update: Please see Derek++ comment http://www.yiiframework.com/wiki/328/simple-rbac/#c18942 My usage of strtstr() is incorrect.
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 } }
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 ...
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, '');
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;
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
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
Great and simple
Work great for small projects, thanks for the wiki!
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.
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
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
Thank you very much.
Thank you very much for this document.Very useful for me.
Thank!
Re: Multiple Role Check
You shouldn't use strstr() for the role check. If there are two roles named user and superuser then a check for user will always match superuser as well. Better to use array search or have an explicit delimiter...
// would need to change input to an array // E.g. Yii::app()->user->checkAccess(array('user','superuser')) if(is_array($operation)) { return (array_search($role,$operation)!==false); } // ...or use an explicit delimiter and explode the string if(strpos($operation, ',')) { $operation_arr = explode(',',$operation); return (array_search($role,$operation)!==false); } // ...or use an explicit delimiter with padding if (strstr(','.$operation.',' , ','.$role.',') !== false) { return true; }
Here is the updated version...
<?php class WebUser extends CWebUser { /** * Overrides a Yii method that is used for roles in controllers (accessRules). * * @param mixed $operation Name of the operation required (here, a role). Can be either string or an array of roles. * @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(is_array($operation)) { // Check if multiple roles are available return (array_search($role,$operation)!==false); } return ($operation === $role);// allow access if the operation request is the current user's role } }
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.