You are viewing revision #3 of this wiki article.
This is the latest version of this article.
You may want to see the changes made in this revision.
For those who feel, the Controller->accessRules() or RBAC (Role-Based Access Control) is too complicated or doesn't want the username(s) to be hard-coded in accessRules(), here is a very simple, easy-to-implement solution.
Considerations ¶
As usual, you will have a table, holding the user's data, such as: username, password, email, real_name, etc. To store the user rights, you need an additional field, named admin_level. This will be an unsigned tinyint, and will hold the user's rights to do things around the site. You will define the admin levels, according to your needs. Now, for this example, let's define 4 levels: 0: REGULAR_USER - basic access 1: EDITOR - access to product editing, translations, etc. 2: MANAGER - Higher level control: site settings, statistics, etc. 3: ADMIN - Delete/modify users, modify critical site settings, handle money, etc.
Implementation ¶
Step 1. Create/modify the user table: ¶
[sql]
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(25) CHARACTER SET ascii NOT NULL,
`password` varchar(128) COLLATE utf8_bin NOT NULL,
`email` varchar(100) COLLATE utf8_bin NOT NULL,
`admin_level` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '0: regular user; 1: editor; 2: manager; 3: admin'
)
Step 2. Create the user login-logout mechanism ¶
Following the standard, recommended procedure, you will have a login-logout functionality.
Step 3. Define the user levels and create the access-control functionality. ¶
Open the protected/components/Controller.php file and add the following to the beginning of the file:
define('REGULAR_USER', 0);
define('EDITOR', 1);
define('MANAGER', 2);
define('ADMIN', 3);
You will load and store the user's data every time a page is requested. To do this, modify the Controller class as follows:
Add a userData property:
public $userData; // Holds an activeRecord with current user. NULL if guest
Define (or extend) the init() function:
public function init() {
...
// Load the user
if (!Yii::app()->user->isGuest)
$this->userData = AR_Users::model()->findByPk(Yii::app()->user->id);
...
}
Add the allowUser function:
public function allowUser($min_level) { //-1 no login required 0..3: admin level
$current_level = -1;
if ($this->userData !== null)
$current_level = $this->userData->admin_level;
if ($min_level > $current_level) {
throw new CHttpException(403, 'You have no permission to view this content');
}
}
Step 4. Begin your controller actions calling the allowUser() function. ¶
If the current user will not have the required admin level, a CHttpException will be thrown. If you don't need any access control, don't use the allowUser() function.
class SomeController extends Controller {
public function actionIndex() {
// $this->allowUser(REGULAR_USER); // everybody can view index
$this->render('index');
}
public function actionLogin() {
// $this->allowUser(REGULAR_USER); // everybody can log in
$this->render('login', array(...));
}
public function actionEditproduct($id) {
$this->allowUser(EDITOR); // only editors, managers and admins can edit products
...
$this->render('editproduct', array(...));
}
public function actionSitesettings($id) {
$this->allowUser(MANAGER); // only managers and admins can change site settings
...
$this->render('sitesettings', array(...));
}
public function actionEdituser($id) {
$this->allowUser(ADMIN); // only admins can change user data
...
$this->render('edituser', array(...));
}
}
Final words ¶
- you can use the userData property to check, display or even modify user data. If userData === null, there is no user logged in, so you need to check first this.
- the userData->admin_level can be edited along with the other user data, so everybody's rights can be changed (by an admin)
- the user rights will be hard-coded. This will help not tech-guru site owners to manage their subscribed members, especially to allow/disallow employees to do/see certain tasks on the site.
use access control filter + accessRules() instead
Hi szfjozsef,
Your article is nicely written, but here's a couple of but, which are quite important from my point of view. I believe it's important information for newbies as well:
expression ACL
public function accessRules() { return array(array('allow', 'actions' => array('login', 'logout'), 'users' => array('*'),), array('allow', 'users' => array('*'), 'expression' => 'isset($user->role) && ($user->role==="admin")'), array('deny', 'users' => array('*'),),); }
you can also just use an expression in the accessRules method like the above. I have hardcoded admin but you can still define constants and use them too. I guess its a matter of preference if you want to define the access in each action or you prefer to use a central method where you define all the access rules.
@ ferisoft, yugenek
Several problems with the accessRules() function:
user names are hard-coded... The user names are third-party data. The code should work with any given database, regardless of the user's names, especially if the names are changeable by the owners. The admin can be 'developer', 'dan', 'joe', etc.
it's very high the possibility to forget to add an action or change the action's name, etc.
a quick look at the controller's action doesn't tell anything about who can access/where is the access defined...
accessRules()
Point by point:
usernames you can put * to target all usernames and use expression to evaluate whatever, if your logic for access is complex you can still move the logic in a method and just add the method to the expressions key.
__it's very high the possibility to forget to add an action or change the action's name, etc.
Well it basically depends on how good you develop, you can forget many stuff, also if you want to change the access for the whole controller or for lotsa methods then using your method you should go by each action and do it one by one, instead of having a central location to do so.
Either way I am not saying one method is better than the other I guess depends on the site and the coding style. I would usually use the AccessRules just because it gives me a central location to define all my access logic for that controller and when i want to modify I know exactly where to look for it.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.