In my series Learning the Yii Framework, I discuss the individual parts of the MVC (Model, View, Controller) architecture in some detail, from a Yii perspective. In the post on Controllers, I introduce Access Control Lists (ACLs), Yii’s default way of restricting who can take what actions. This is a key part of the security of any Web application. For example, a site’s content can often be read by anyone at all, registered or non-registered users alike (like the text you’re reading now). Some content may only be viewable by registered users and some by registered users of a certain type (e.g., paid members). Finally, some content may only be viewable by administrators. In this post, I detail how to completely control access to your Web application using Yii’s Access Control Lists.To start by repeating what I wrote in my Basic Controller Edits post, a Controller’s accessRules() method dictates who can do what. In a very simple way, the “what” refers to the Controller’s action methods, like actionList() or actionDelete(). In other words, only X type of user can call the actionDelete() method, which, of course, deletes a record. Your “who” depends upon the situation, but to start there’s at least logged-in and not logged-in users, represented by * (anyone) and @ (logged-in users), accordingly. Depending upon the login system in place, you may also have levels of users. So the accessRules() method uses all this information and returns an array of values. The values are also arrays, indicating permissions (allow or deny), actions, and users:
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'list' and 'show' actions
'actions'=>array('list','show'),
'users'=>array('*'),
),
array('allow', // allow authenticated user to perform 'create' and 'update' actions
'actions'=>array('create','update'),
'users'=>array('@'),
),
array('allow', // allow admin user to perform 'admin' and 'delete' actions
'actions'=>array('admin','delete'),
'users'=>array('admin'),
),
array('deny', // deny all users
'users'=>array('*'),
),
);
}That’s the default setting for a Controller, where anyone can perform list and show actions, meaning that anyone can list all records or show individual records in an associated Model. The next section allows any logged-in user to perform create and update actions. Next, only administrators can perform admin and delete actions. Finally, a global deny for all users is added, to cover any situation that wasn’t explicitly defined. This is just a good security practice. Note that these rules just apply to this Controller; each Controller needs its own rules.
So at the most basic level, your access rules begin by allowing or denying actions to logged-in or non-logged-in users. When you go to create your own rules, start with what anyone can do and end with what no one can do. For example, say you have some user management system that includes the Models User and UserType, corresponding to related tables in the database. The UserType would be a simple two-field object—id and type, where type might be reader, writer, editor, and so forth. Each User then is assigned a single, specific type. I’d be inclined to grant list and show permissions to everyone, as that might be useful for browsing users by type and would be necessary for adding new users. However, I probably wouldn’t allow create, update, admin (which is really a variation on list), or delete permissions on UserType at all, with the thinking that these are static values. The accessRules() would then be:
public function accessRules()
{
return array(
array('allow', // allow all users to perform 'list' and 'show' actions
'actions'=>array('list','show'),
'users'=>array('*'),
),
array('deny', // no one can create, update, or delete these:
'actions'=>array('create','update','admin','delete'),
'users'=>array('*'),
),
array('deny', // deny all users anything not specified
'users'=>array('*'),
),
);
}As you can see, just to be extra careful, I include a final deny clause still: if actions are added later the default behavior will be denial; only by then changing the rules can that action be executed by anyone.
To take this beyond logged-in vs. non-logged-in users, you need to know that the representation of logged-in users relies upon Yii’s authentication components. In the default rules, the admin user can perform certain actions, where “admin” is the actual name of the user that is logged in, and comes from protected/components/UserIdentity.php. I write about the authentication process in detail in two posts: the first covering simple authentication, the second covering more evolved authentication. The default Yii application allows for two users, with names of demo and admin, but you’d likely have a more elaborate system in place. If you know only a limited number of users will be admins, you could hardcode their usernames into the rules:
array('allow', // allow harold and maude user to perform 'admin' and 'delete' actions
'actions'=>array('admin','delete'),
'users'=>array('harold','maude'),
),
This is still a bit too static, though. Perhaps your users in the database would be registered by user type, or role, and that the permissions would be based upon these roles. In that case, you can add an expression element to the returned array in the access rules:
array('allow',
'actions'=>array('admin','delete'),
'users'=>array('@'),
'expression'=>'PHP code to be evaluated'
),
Two things about this expression. First, you still need to use the users element, and you’ll probably want to still restrict this to logged-in users (most likely). Second, the expression itself should be some PHP code, quoted, that when evaluated gives a Boolean result. If the code in the expression will be true, then permission will be allowed; false, denied. Say you wanted to restrict the publish action to only those with the role of editor:
array('allow',
'actions'=>array('publish'),
'users'=>array('@'),
'expression'=>'isset($user->role) && ($user->role==="editor")'
),
The $user->role value would have to be established when the user logs in, as part of the authentication process. I discuss this exact example in my second post that covers more evolved authentication. And I put the entire expression within quotes (it doesn’t matter whether you use single or double, so long as you don’t create a parse error). With that rule, users that aren’t logged in, or users that are logged in but have non-editor roles, won’t be able to take that action.
A final way in which I’ve enforced authorization in previous Yii projects involves a bit of a hack. I had a site where users with a certain role could create certain types of content. They could also update or delete those types of content, but only if they were the one to create that content in the first place. In other words, say a user creates an event, then their user ID gets associated with that event’s record. The update and delete actions in the EventController should only be executable if the currently-logged-in user’s ID is the same as the ownerId of the event in question. As far as I know, this cannot be addressed in the accessRules() method, because those rules are evaluated prior to the loading of any specific Model. My solution, perhaps a hack but it works, was to add the proper logic within the actions. Here’s a simplified version of how that would look within one of the actions:
public function actionDelete()
{
$event = $this->loadEvents(); // Fetch the specific event Model.
// Can only delete the events they created:
if ($event->ownerId == Yii::app()->user->id) {
$event->delete();
} else {
throw new CHttpException(403,'Invalid request. You are not allowed to delete this event.');
}
}As I said, that’s a hack but it works fine for me and is simple enough to follow (also, the actions that display events for editing and deleting get changed to only list those with a matching ownerId). A better solution would likely be to use Yii’s Role-Based Access Control (RBAC). I haven’t personally gone down that path yet as I haven’t had the need; the above knowledge has more than sufficed for the sites I’ve created thus far.
As always, thanks for reading and let me know what comments or questions you may have. Thanks, Larry

Hi Larry,
Thanx for all your Yii posts so far, they helped me a lot. I just figured out the same yesterday about ACL’s, and I came up with this in my accessRules method, pretty the same but a bit more readable:
…
‘expression’=>’User::hasRole(User::ROLE_ADMIN)’,
…
Anyway, thanks again and keep up the good posting…
Thanks for the nice words and for sharing your work. I assume, then, that the User Model has the ability to retrieve the currently-logged-in user and that the hasRole() method returns a Boolean value.
Oh, and about your actionDelete access checks, I think is ok to do it that way. Yesterday I read all about the CAuthManager components, but that ends up as pretty heavy to implement and even then you end up with a thing like this:
if(Yii::app()->user->checkAccess(‘updatePost’))
{
// update post
}
Thanks again for sharing. If that’s their solution, it’s pretty close to what I end up doing anyway.
I think you often needs to check whether the logged in user is the owner of a post.
And when you only need this as role management it is better do it without rbac because you must do the same then mindhout says.
And when you have for example a task updateOwnPost and check only this there are 3 queries needed. When you check only for an operation like update then there are needed 6 or seven queries to check it.
Read the rbac docs.
For simple said I agree with Larry. When you not need a complex role system wiht many of roles tasks and operations it is better to do this wiht ACL.
Thanks for the input, Klaus.
Thanks for the tip, I’ve problems with yii 1.1 accessRules.
In your second code snippet you have the line
array(‘allow’, // no one can create, update, or delete these:
Shouldn’t that read ‘deny’ instead of ‘allow’?
D’oh! Thank you for pointing that out. I fixed it.
Very helpful, exactly what I was looking for – thanks a lot!
Thanks. I spent two days in Yii RBAC, sRBAC and what nots. But, finally your method works like a charm and is much easier and faster.
Glad to hear it. Thanks for letting me know.
Hey Larry,
I just want to say thank you. I’m starting out in yii and your site is a better resource for me than the yii documentation.
IGerard
Thanks, Gerard. Good luck with your Yii studies!
complicated.. thx 4 share
thank you so much for this post,.
You’re welcome. Thanks for the feedback!
Hi Larry,
My situation is the same as Gerard above. Your writings have been far more useful. Thank you very much. Keep up the good work. You are really talented in it.
Maurice
shalomsoftware.com.au
Thanks for the nice words. I’m hoping to do a book on the Yii framework later this year.
While searching the web in trying to understand how to use Yii RBAC (for some time) your wonderful simplification that actually works popped up in the search results. Thank you so much!
Thanks for saying so. Much appreciated!
Thanks Larry! that put me right into what i wanted to do and explained a lot. Cheers from Poland!
Hi larry , i have this code and don´t filter good the users
return array(
array(‘allow’, // allow all users to perform ‘index’ and ‘view’ actions
‘actions’=>array(‘index’),
‘users’=>array(‘*’),
),
array(‘allow’, // allow authenticated user to perform ‘create’ and ‘update’ actions
‘actions’=>array(‘update’,'view’),
‘expression’=>’(Yii::app()->user->getState(“idTipo”)==1)’
),
array(‘allow’, // allow admin user to perform ‘admin’ and ‘delete’ actions
‘actions’=>array(‘admin’,'delete’,'create’),
‘expression’=>’(Yii::app()->user->getState(“idTipo”)==2)’,
),
array(‘deny’, // deny all users
‘users’=>array(‘?’),
),
);
@Larry thanks, Larry.
this post solved my problem.thanks.
I’m glad. Thanks for saying so.
Hi Larry, am using yii_1.1.7. but when i tried to edit UserIdentity.php in order to enable users from the DB to login, it gives me Incorrect username or password error whenever am loging in. I’ve tried this for three days and have failed what can i do?
I would recommend turning to either my support forum or the Yii support forum, providing more details in the process.
Just want to say, your are great write. I have learned many thing from your write ups. I have only one questions which is still confusing and i don’t know how to make that efficiently and that is how to make the admin panel and main website separate and how can i make the theme only for admin panel?
Thanks for the nice words on the writing. In answer to your question, you’ll want to create a “module”, which is like a separate Yii application.
Being an old Fusebox, CodeIgniter, and Zend Framework guy I was ready to learn something new. Picked up Yii and have been working on a pet project with it. I’ve read the Agile Yii book and what’s so far been released of the Yii 1.1 RAW cookbook. Learned a lot but still trying to get the feel. I must say though, what a treat it was to find your site. Your articles are so concise and spot on. Not to mention the fantastic prose! Thank you so much for having to passion to take the time and share with the rest of us.
Hello Jim,
Thanks for the nice words on my work. I’m glad you liked it and it’s kind of you to say so.
Cheers,
Larry
Thanks Larry this has really helped me.
You’re quite welcome. Thanks for the nice words.
Larry just a question because of some minor confusions I have controller with an actionAdmin so in my app I allow all authenticated users to do admin tasks but load only the items that belongs to that user. I have tried to right a code within the actionAdmin to load the users item based on the logged in user id but does work. how can i go about by modifying yii’s default actionAdmin.
The short answer is you just edit the method. The long answer is I don’t understand exactly what you mean and I’d prefer that questions like this, which would require longer replies and more replies and more replies and code not be done through the comments to a blog post. Please use my support forum or the Yii support forum if you need help.
Very nice post! Helped me a lot!
THANKS!
It is much easier to understand than the yii role-based-acces-control article on yiiframework.com …
I already spent a lot of time on the rbac tutorial… and I still have some understanding problems.
I read your post only one time and understood everything.
But I have a little question:
What is the “advantage” of using the “Yii-RBAC” instead of your solution?
Your solution is much easier, less work… and it does the same thing… (or not?)
Best Regards from germany!
Thanks for the nice words. Glad you liked it. As for your question, well, my solution is a bit of a hack, as I said, and hacking framework (i.e., standardized) code isn’t really a good thing. And the RBAC allows finer control. I’m not saying you should rush out to use RBAC, but it has its merits, of course.
Hello,
Regarding you last example (checking if the event being deleted belongs to the user), what if the relationship is MANY_MANY, that is, I don’t have a “ownerId” but an array of owners?
I could I guess iterate over the values of the array but I’m wondering if there is a Yii-way of doing this.
Well, you could just use in_array(). That’s not a “Yii-way” necessarily, but it’d only take a line of code.
Hi Larry! Thanx again for the great post! Just to get everything ordered in future – can you explain why somewhere is used === instead of == and :: instead of -> ?…
Hello Alex. === is an identical comparison; == is an equality comparison. It’s best to make identical comparisons in situations where you want to distinguish between true-like values and actual true, or false-like values (such as 0) and actual false. :: is the scope resolution operator, used to refer to a member of a class. -> is for referring to a member of an object. None of these are particular to frameworks; they’re part of PHP proper.
Larry, your tutorial series on Yii is really a great resource for the community, and more so for people just beginning on Yii, since these are specific use-case based deep dives.
Wondering if your method would be a good natural fit to where several thousand (maybe couple of hundred thousand) users, each having their own “private” content. E.g.
user0001 has access to content0001a, content0001b, content0001c…
user0002 has access to content0002a, content0002b, content0002c…
…
user000N has access to content000Na, content000Nb, content000Nc…
although all users, do have access to some limited shared / common content as well. The content in question is of sensitive nature (e.g. scanned tax filing images etc.), so the ACL needs to be pretty much bullet proof.
Would the ACL method scale well as per this use case ?
It sounds like going the more formal route of an RBAC would seem to make the most sense. I think of ACL as being a relatively simple approach and the problem is it needs to be hardcoded into each controller, which would be unmanageable in your case, I suspect. Thanks for the nice words!
This is a great post. I after looking more into ACLs, I found that RBAC was more for my needs. I created a blog post on my discoveries that might help out a few others if they decide to go down that path.
Yii and Authorization Rules (RBAC)
Thanks for the nice words and for sharing what you figured out. Hopefully that will help others.