This is the starting point for today's adventure: http://www.yiiframework.com/doc/guide/topics.auth
The article above walks you through adjusting the UserIdentity (found in /protected/components/UserIdentity.php ) to pull information from a database table for users, override the ID of the UserIdentity class and set up some basic authentication rules, but you have to read between the lines to get it all complete.
Prepwork!
If you've already got a User model set up, you can (Skip to the auth details)
Before modifying your UserIdentity, you need to first create a users table with a keyed ID field. I use MySQL, you can use whatever you like for a backend. The table simply needs to have (at a minimum) an id, username and password column, such as:CREATE TABLE User( `id` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(45), `password` VARCHAR(254), `title` VARCHAR(45), PRIMARY KEY(`id`)) ENGINE = MyISAM;I've included a title field simply because the yii guide example includes it and it's a good example of how to add additional data to your authenticated user. Obviously, in a real life situation you're going to want more information stored for your User, but this is the bare minimum to get the ball rolling.
To be easily able to manage and update your users, you should now open up the yiic shell and model Users. So, at the command prompt from your /protected directory:
$ ./yiic shell config/main.php $ model User $ crud User
You now have a fully manageable set of user identities which you can edit and add to via web form. (The model command creates the data model for the table User and calls the new object class User, if you need to change the table name, simply issue the command as: model User MyDB.MyTableName )
NOTE: (futher explaination of the above code)
The model command will generate the User model based on your database table that you created. It will also create for you the Unit Test class and fixture data for the User model.
The crud command will generate a UserController along with the preset CRUD actions for Create, Read, Update and Delete (as well as your standard search/admin).
These commands should be typed from within your web application's /protected/ directory, or if using the main webapp directory, then type: protected/yiic shell
The model command will generate the User model based on your database table that you created. It will also create for you the Unit Test class and fixture data for the User model.
The crud command will generate a UserController along with the preset CRUD actions for Create, Read, Update and Delete (as well as your standard search/admin).
These commands should be typed from within your web application's /protected/ directory, or if using the main webapp directory, then type: protected/yiic shell
To view your users list, you should be able to pull up: /index.php/User/ or index.php?r=User (depending on whether you've configured Yii to alter the path names)
You can now go back to the authentication guide linked above and alter your UserIdentity to access the User table and check access like so:
class UserIdentity extends CUserIdentity { private $_id; public function authenticate() { $record=User::model()->findByAttributes(array('username'=>$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('title', $record->title); $this->errorCode=self::ERROR_NONE; } return !$this->errorCode; } public function getId() { return $this->_id; } }NOTE the override for getId -- this is VERY important for the authentication systems later.
Now that that's out of the way, we can actually start telling the system *which* users have set access.
Now it's time to add more tables to the database for Authentication rule storage. In your main yii directory is a file called framework/web/auth/schema.sql . Run this file through your mysql command prompt (or editor of choice) and you will have three new tables set up for Authentication Management. They are: AuthAssignment, AuthItem, AuthItemChild
Finally, we're ready to go back to the main authentication guide and continue!!
Authentication
Move down to this section of the AuthManager guide, and follow the instructions for configuring your AuthManager as shown.Authentication in Yii has three "levels"- operations, tasks and roles.
We're going to step through setting up 3 basic roles. An admin role, an authenticated role, and a guest role. With this, we can add in infinite customized authentication tasks/operations for all our needs. The admin role will be specifically assigned to user IDs from our Users table.
It's time to head back into the yiic shell and set up our basic roles. If you prefer, you can enter these directly into the database tables you created, or copy the code into a php page which you run ONE TIME ONLY.
(For the purposes of this example, we will assume that you, the primary admin, are user id 1)
The shell version(running this from the shell automatically saves it, if you run it from a php page, I believe you need to call $auth->save() at the end -- I always use the shell (or hit the database directly which is a bit hack)):
$auth=Yii::app()->authManager; $bizRule='return !Yii::app()->user->isGuest;'; $auth->createRole('authenticated', 'authenticated user', $bizRule); $bizRule='return Yii::app()->user->isGuest;'; $auth->createRole('guest', 'guest user', $bizRule); $role = $auth->createRole('admin', 'administrator'); $auth->assign('admin',1); // adding admin to first user createdThe admin role is, as we said, assigned directly. The other two are polar opposites which will be checked and applied to all users as they load the site to determine whether they are authenticated or not. EVERY user therefor will fall into one of those two categories and can access tasks/operations assigned to authenticated or guest users.
To add in the default roles, open up the config/main.php file and modify the array where you added the AuthManager as follows:
'authManager'=>array( 'class'=>'CDbAuthManager', 'defaultRoles'=>array('authenticated', 'guest'), ),Now whenever someone comes to the site, they'll automatically be put into one of those two categories since they use the same business rule but one is for true and one for false, and your user id 1 will have all the permissions that an admin has.
Great ... but it doesn't actually DO anything yet.
In your Controllers (we'll use the User as an example since we created that one above) you can now change the /protected/controllers/UserController.php accessRules function to allow only your admin to delete users as such:
public function accessRules(){ return array( array('allow', // allow anyone to register 'actions'=>array('create'), 'users'=>array('*'), // all users ), array('allow', // allow authenticated users to update/view 'actions'=>array('update','view'), 'roles'=>array('authenticated') ), array('allow', // allow admins only to delete 'actions'=>array('delete'), 'roles'=>array('admin'), ), array('deny', // deny anything else 'users'=>array('*'), ), ); }But, we don't want the users to edit each other! This is where tasks/operations come into play.
We need a task which allows users to update their own information. Back to the shell:
$auth=Yii::app()->authManager; $bizRule = 'return Yii::app()->user->id==$params["User"]->id;'; $auth->createTask('updateSelf', 'update own information', $bizRule); $role = $auth->getAuthItem('authenticated'); // pull up the authenticated role $role->addChild('updateSelf'); // assign updateSelf tasks to authenticated users
Finally...
Open the UserController.php file again and move to the actionUpdate() function. We'll need to modify it as such:public function actionUpdate() { $model = $this->loadModel(); // set the parameters for the bizRule $params = array('User'=>$model); // now check the bizrule for this user if (!Yii::app()->user->checkAccess('updateSelf', $params) && !Yii::app()->user->checkAccess('admin')) { throw new CHttpException(403, 'You are not authorized to perform this action'); } ...
That's pretty much it! So for simple role or operation based access, changing the accessRules will do it, for more complex logic with a business rule, assign the task to the default role, then pull it up in the specific action. I'm sure there's a way to get the accessRules to be smarter, but I haven't figured it out yet! When I do, I'll modify this.
Thanks for your blog. It's helped me out quite a bit.
ReplyDeletethanks,
ReplyDeletewas helpful to authenticate with a database column.
http://rowsandcolumns.blogspot.com/2010/08/yii-authentication-from-mysql-database.html
Apart from a typo ("acessRules" should be "accessRules") this is a great article. Thanks for sharing the knowledge!
ReplyDeleteThanks for the catch! =) Glad it was helpful
ReplyDeleteNice article. It helped me a lot. I didn't understand these concepts after reading the yii book. But your article was pretty easy to understand. Thanks for sharing.
ReplyDeleteYou're welcome =)
ReplyDeleteThanks for the article, it might help me with the role stuff.
ReplyDeleteBtw, how did you figure out all this info, if it wasn't in the book? Did it came to you when you was sleeping?
Anyway, thanks
When I have something I need to do for an application that isn't in the documentation, I go to the API docs and the source code to figure out how to solve the problem I'm facing. Sometimes it's quick, sometimes it takes longer. But I try to write down the solution somewhere so that I don't have to redo the research if it comes up again ;)
ReplyDeleteNice article. I'm extremely new to Yii. I'm playing with a fresh install as I type this. Anyway, for clarification, I'm guessing that I'll want to setup at least one user after the model user / crud user step, but before the creating roles, assigning admin to the first created user, etc. under authManager. Does that make sense?
ReplyDeleteGood article. However, I can't get it to work. I log in with the first user and get a 403 error. I noticed that no biz rules were added to any of the tables placed in the database; only an auth.php file in the data directory was created. Any help is appreciated.
ReplyDeleteSorry, the AuthAssignment and AuthItem tables did have the biz rules, etc added. Still, my admin user got a 404 error:(
ReplyDeleteI mean '403' error.
ReplyDeleteyiic shell and set up our basic roles --
ReplyDeleteI don't understand this part please help
Michael,
ReplyDeleteWhich part don't you understand? I'm happy to help, I just need a little more information on what you want to do that isn't clear.
When I say "yiic shell" I mean that you should go to your webapp directory and type at the command line: protected/yiic shell OR you can go to the protected directory and type: ./yiic shell config/main.php
Then, within the shell, you would type in all those role creation commands that are shown (or your own that you wish to set up).
Another neat way to do it, now that there are DB migrations, is to make a migration that sets up all your RBAC roles. I have found that to be quite useful.
Does that clarify at all? Or are you unclear about what the roles are and how to use them?
Haven't finished the whole thing bug going step by step as Micheal said the yiic shell part was confusing but after reading your post i get that part now. Might be something worth rewriting.
ReplyDeleteHi,
ReplyDeleteThanks so much for writing this; I'm setting up Yii for the first time and this has been very helpful.
I do have a question, however. When you say to configure authManager as instructed here, it is my understanding that we edit /framework/web/CWebApplication.php and replace
return $this->getComponent('authManager');
with
return array(
'components' => array(
'authManager' => array(
'class' => 'CDbAuthManager',
'connectionID' => 'db',
),
),
);
(I'm using a MySQL database already defined in /protected/config/main.php as 'db'.)
Later on in your tutorial, though, you say to alter where we should have added
'authManager'=>array(
'class'=>'CDbAuthManager',
'defaultRoles'=>array('authenticated', 'guest'),
),
to protected/config/main.php.
I'm confused on whether we need to alter both protected/config/main.php and framework/web/CWebApplication.php or just the former or just the latter. I'd appreciate it if you could shed some light on this for me.
Thank you in advance.
This comment has been removed by the author.
ReplyDeleteDalf,
ReplyDeleteDid you get it working?
Raafia,
ReplyDeleteSorry for my VERY late response, I missed your post for some reason. You only need to modify your protected/config/main.php file.
where should i insert these? what directory?
ReplyDelete$auth=Yii::app()->authManager;
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
$role = $auth->createRole('admin', 'administrator');
$auth->assign('admin',1); // adding admin to first user created
and
$auth=Yii::app()->authManager;
$bizRule = 'return Yii::app()->user->id==$params["User"]->id;';
$auth->createTask('updateSelf', 'update own information', $bizRule);
$role = $auth->getAuthItem('authenticated'); // pull up the authenticated role
$role->addChild('updateSelf'); // assign updateSelf tasks to authenticated users
Jun,
ReplyDeleteYou're going to want to run those from the yiic shell (protected/yiic shell) or create a database migration to encapsulate the commands you want to add. The migrations are especially powerful to do that and ensure that it's only run once. Alternately, you could put it inside of a shell or command script. The simplest way to experiment with it though is to run from the command shell.
- Dana
Nick,
ReplyDeleteWhat Yii Verison are you running?
Do have a $ in front of the auth variable, correct?
so: $auth = Yii::app()->authManager;
I only ask because it's not in what's copied above and would certainly cause the problem if that was the case.
Just as a test, I ran it without the dollar sign and got the same error you're posting:
>> auth = Yii::app()->authManager;
PHP Parse error: syntax error, unexpected '=' in /var/www/sites/master/frameworks/yii/1.1.8/framework/cli/commands/ShellCommand.php(150) : eval()'d code on line 1
Parse error: syntax error, unexpected '=' in /var/www/sites/master/frameworks/yii/1.1.8/framework/cli/commands/ShellCommand.php(150) : eval()'d code on line 1
That should fix it =)
Glad to help ;)
ReplyDeleteYou're in the same shell session as the previous command, correct? (This is from v1.1.7 but 8 is the same)
ReplyDelete./yiic shell config/main.php
Yii Interactive Tool v1.1 (based on Yii v1.1.7)
Please type 'help' for help. Type 'exit' to quit.
>> $auth = Yii::app()->authManager;
>> $auth->createRole('auth2', 'test', NULL);
---
$auth is just a shortcut, you could just as well type:
Yii::app()->authManager->createRole('auth2','test',NULL);
If you continue to get that error, it may be that your authManager isn't configured in your yii config file.
You could try:
print_r($auth);
To see what it's current value is.
I'm very happy for the attempt to give back to the community, but for someone who doesnt know all of Yii already its quite difficult, not to say impossible, to have any idea where the chunks and pieces of code should go. Seems like you just copied and pasted stubs from that Auth example?
ReplyDeleteIn particular, this section doesnt make sense:
$auth=Yii::app()->authManager;
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
$role = $auth->createRole('admin', 'administrator');
$auth->assign('admin',1); // adding admin to first user created
In your Controllers (we'll use the User as an example since we created that one above) you can now change the /protected/controllers/UserController.php accessRules function to allow only your admin to delete users as such:
ReplyDeleteThis is the first time UserController is mentioned, where do you actually explain how to create it? My guesses is we should use Gii? I think ive read this entire tutorial for a few hours and its like a jigsaw puzzle to try to understand :(
Ola,
ReplyDeleteThe section that you quoted creates the generic business rules for the rbac.
I will break it down line by line:
// Assign a reference to the authManager so that
// we can access it more easily throughout
$auth=Yii::app()->authManager;
// The first two rules being created are going
// to be automatically assigned via the urlManager
// configuration to all users dynamically, so
// they have a very simple set of rules to determine
// which category a user falls into.
// Create a rule that will return true if the
// user is currently logged in to the system
// with a valid ID, otherwise, returns false.
// You may want to make this more complex if
// your business logic requires it.
$bizRule='return !Yii::app()->user->isGuest;';
// Assign our business rule to a user role called
// 'authenticated' so that we can simply check
// for the role 'authenticated'.
// The first param is the name of the role we are
// creating, the second is a descriptive name of
// the role, the third is the reference to the rule
$auth->createRole('authenticated', 'authenticated user', $bizRule);
// Now we're going to do the same thing in reverse
// so that we can explicitly check for people who
// are NOT valid authenticated users via role
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
// The following roles are going to be explicitly
// assigned to specific user IDs so that they have
// access to these roles when they need them.
// Create the admin role which can be assigned to
// site administrators or developers
// No business rule required for this because it's
// explicitly assigned and won't vary per page
$role = $auth->createRole('admin', 'administrator');
// Assign the role of admin to user ID 1
$auth->assign('admin',1); // adding admin to first user created
Does that help to clarify what the console commands are doing? These are just a generic example that are probably used by most people running core Yii auth. Your site's particular authentication will require many more specific roles and operations that are tied to your business logic.
txx_zz:
ReplyDeleteThese lines:
$ ./yiic shell config/main.php
$ model User
$ crud User
Create your UserController for you. The model command creates the User Model. The crud command creates the UserController and all the associated views.
Hi Dana,
ReplyDeletethis is very helpful for me but i was not understood its shell commands plz give me an example for this..
Great tips, thank you for sharing them.
ReplyDeleteSave a lot of headaches.
Mayur,
ReplyDeleteI'm not sure what shell commands you're having problems with. Can you be more specific?
Simon,
Thanks!! Glad it was helpful.
HELP!! I VERY LOST (THE PART BELOW)
ReplyDeleteAuthentication
Move down to this section of the AuthManager guide, and follow the instructions for configuring your AuthManager as shown.
Authentication in Yii has three "levels"- operations, tasks and roles.
We're going to step through setting up 3 basic roles. An admin role, an authenticated role, and a guest role. With this, we can add in infinite customized authentication tasks/operations for all our needs. The admin role will be specifically assigned to user IDs from our Users table.
It's time to head back into the yiic shell and set up our basic roles. If you prefer, you can enter these directly into the database tables you created, or copy the code into a php page which you run ONE TIME ONLY.
(For the purposes of this example, we will assume that you, the primary admin, are user id 1)
The shell version(running this from the shell automatically saves it, if you run it from a php page, I believe you need to call $auth->save() at the end -- I always use the shell (or hit the database directly which is a bit hack)):
$auth=Yii::app()->authManager;
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
$role = $auth->createRole('admin', 'administrator');
$auth->assign('admin',1); // adding admin to first user created
The admin role is, as we said, assigned directly. The other two are polar opposites which will be checked and applied to all users as they load the site to determine whether they are authenticated or not. EVERY user therefor will fall into one of those two categories and can access tasks/operations assigned to authenticated or guest users.
To add in the default roles, open up the config/main.php file and modify the array where you added the AuthManager as follows:
YOU SAID "Move down to this section of the AuthManager guide, and follow the instructions for configuring your AuthManager as shown."
I SHOULD FOLLOW THE LINK ??
ALSO I RAN THE THE CreatRole STUFF, WHAT SHOULD HAPPEN AFTER THAT, WHERE IS IT WRITNG TO, WHICH FILE, IT'S NOT CLEAR AT ALL, PLEASE HELP...
GREAT POST.
Yes, you need to follow the instructions in that guide to set up your database to handle the authentication storage.
ReplyDeleteWhen you create the roles through the shell script, it will create entries in the database for those roles.
How would you do this remotely? Like on godaddy?
ReplyDeleteHi Dana...
ReplyDeletehow to user function beforedelete in yii..
Hi, thank you for this. Like many others I had problem with running shell. I found solution in your reply. But it still didn't want to insert data in database. Some mistakes with pdo.php. (Same mistake with trying create model with shell, no mistake with gii)
ReplyDeleteThen I found this and it helped (of course, with many php notices I don't know), but no mistakes after inserting your lines.
php -c path/to/php.ini protected/yiic.php shell
Now I can go on.
Hello
ReplyDeleteI wanna make access role in my application when login with username (name) who has role as admin?
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions'=>array('update','delete'),
'users'=>array('@'),
'expression'=>'isset($user->level) && ($user->level==="admin")',
that's can't run
Hi,
ReplyDeletethanks for this article. Everything works fine for me.
Just a little question:
In UserIdentity you are setting the user state with
$this->setState('title', $record->title);
All the role based rules used by the controllers are relying on authAssignment.
So where does actually this setState setting come into the game and can I neglegt it?
Regards
Michael
Setting the state just saves that small piece of information to the users session data so that you can access it quickly without re-querying for it. You can definitely skip that part if it's not data you will access frequently.
ReplyDeleteThanks for the article, i stumbled a bit on the authentication rules in my controller, but then i realized i was declaring the admin and authenticated roles as users.
ReplyDeleteI also really liked the idea of a migration to create the auth tables and populate all the rules. Very handy place to put everything if it only has to run once and it can be made portable up to my server. So here's my migration (tested on MySQL 5.something or other)
class m120305_163808_create_auth_tables extends CDbMigration
{
// Use safeUp/safeDown to do migration with transaction
public function safeUp()
{
$this->createTable('AuthItem', array(
'name' => 'varchar(64) not null primary key',
'type' => 'integer not null',
'description' => 'text',
'bizrule' => 'text',
'data' => 'text',
));
$this->createTable('AuthItemChild', array(
'parent' => 'varchar(64) not null',
'child' => 'varchar(64) not null',
'primary key (`parent`, `child`)'
));
//$this->execute('alter table `AuthItemChild` add primary key(`parent`, `child`)');
$this->addForeignKey('fk_parent', 'AuthItemChild', 'parent', 'AuthItem', 'name', 'cascade', 'cascade');
$this->addForeignKey('fk_child', 'AuthItemChild', 'child', 'AuthItem', 'name', 'cascade', 'cascade' );
$this->createTable('AuthAssignment', array(
'itemname' => 'varchar(64) not null primary key',
'userid' => 'varchar(64) not null',
'bizrule' => 'text',
'data' => 'text',
));
$this->addForeignKey('fk_itemName', 'AuthAssignment', 'itemname', 'AuthItem', 'name', 'cascade', 'cascade');
$auth=Yii::app()->authManager;
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
$role = $auth->createRole('admin', 'administrator');
$auth->assign('admin',1); // adding admin to first user created
$bizRule = 'return Yii::app()->user->id==$params["User"]->id;';
$auth->createTask('updateSelf', 'update own information', $bizRule);
$role = $auth->getAuthItem('authenticated'); // pull up the authenticated role
$role->addChild('updateSelf'); // assign updateSelf tasks to authenticated users
}
public function safeDown()
{
$this->dropTable('AuthItemChild');
$this->dropTable('AuthAssignment');
$this->dropTable('AuthItem');
}
}
I am Bookmarking your article, wonderful job done on the authentication and authorization.. :-)
ReplyDeleteI just dont understand why Yii Made a twist and turn in their doc..
Just one question, why do we need to override getId in the UserIdentity Class?
Appreciate your help on this ..
You override the method so that it will reference the integer field rather than the string username.
ReplyDeleteIf you look at the core CUserIdentity, you'll see that getId returns the username value:
https://github.com/yiisoft/yii/blob/1.1.11/framework/web/auth/CUserIdentity.php#L67
Thanks So much ma'am .. I just faced the same problem :-)
ReplyDeleteAppreciate your efforts on quick reply.
Regards!
Thx for your article! Very helpful and well explained.
ReplyDeleteMuch more understandable than the related chapter/article in Yii book (Agile.. etc.) and web site.
Good job! ;)
Regards
This comment has been removed by the author.
ReplyDeleteawesome, although its a bit old but wicked!!!
ReplyDeletewhere can I find more info about creating bizrules and stuffs?
Thanks Dana ...!! Your blog help me a lot.. but i'm stuck at shell commands, can you give me example how to run those commands via php program's in which directory file should be in.
ReplyDeletei'm talking about these shell code need to execute in shell {
$auth=Yii::app()->authManager;
$bizRule='return !Yii::app()->user->isGuest;';
$auth->createRole('authenticated', 'authenticated user', $bizRule);
$bizRule='return Yii::app()->user->isGuest;';
$auth->createRole('guest', 'guest user', $bizRule);
$role = $auth->createRole('admin', 'administrator');
$auth->assign('admin',1); // adding admin to first user created
}
Hello Dana,
ReplyDeleteGreat effort and excellent explanation of implementing Yii's authentication and authorization. I must say that with your post along with Yii's documentation I almost completely understood the concept. Thank you very much for sharing knowledge.
Best
Bachi
Thanks Dana,
ReplyDeleteIt was nice to read your article and now it is clearer for me how to work with the bizRules.
Viorel
Good Blog..
ReplyDeleteThank you very much.....
ReplyDeleteI was trying very much for this. At last i could do that with the help of your blog post...
Dear Dana, thanks for the great article. I've bookmarked it already.
ReplyDeleteI have got things working for the most part, but I still have one problem, it would be great if you could help me.
I have my accesRules configured like this:
...
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'index' and 'view' actions
'actions'=>array('index','view'),
'users'=>array('admin'),
...
But this only works if my users username is indeed 'admin' too. If I change the username in the db, I get a 403. I thought it would match on userId instead of username. I have overridden the getId() of UserIdentity, as you've specified in your post.
Would you perhaps have any suggestions? Thank you in advance!
Cheers,
Jelmer
Jelmer,
ReplyDeleteYou're really close! You need to change the 'users' key to 'roles' to have it base on the admin role rather than a user with the ID of admin.
- Dana