An idea for Role Based Access Control

Hi all,

I want to listen your opinion about my Role Based Access Control for Yii.

Actually, I love the idea of RBAC in Yii described here http://www.yiiframew…-access-control but I have a question: Why do I have to remember all those role names I’ve created and why I should verify permissions manually for each piece of code? I’m quite sure that Model should only be modified with correponding Controller Actions. So why not to have permissions and roles closely tied to my controllers and their actions. And let Yii automatically verify user permissions to access  that or other action.

So I've started with a database schema (MySQL):

Posted Image

As you can see each user can have multiple roles. Each role can have multiple permissions. Each permission allows access to corresponding controller/action pair. Also permissions can have business rule associated with it. Business rule is a simple piece of PHP code. For example here is a permission that will allow user to update his own profile (crud generated UsersController class will have a method called loadUser() to load current instance of 'User' model):

return Yii::app()->user->id==Yii::app()->getController()->loadUser()->id;

Here is corresponding SQL: http://paste2.org/p/153292

(i will also attach it to the post)

  1. Now we need a filter class that will allow/deny access to the controller/action pair.

Here it is: http://paste2.org/p/153293

(also attached, put it into /protected/filters folder)

  1. Now we need to add this filter to controller, this is easy:


// ..... in controller class....


/**


* @return array action filters


*/


public function filters()


{


	return array(


	  array(


            'application.filters.AccessControlFilter', 


          ),


	);


}


And that’s all! Mostly… I thought it would be good to have an yiic shell command that will be able to create/update/delete/grant/revoke/search permissions, users and roles. I’ve called it ‘rbac’, here is its code on PasteBin: http://paste2.org/p/153298

(code also in attachment, put it into /protected/command/shell folder)

and here is a sample console session



>> rbac grant roles=id:1 permissions=id:%


   add permission [1:list users:auth.users/list] to role [1:admins]?      [Yes|No|All|Cancel] a


      added


   add permission [2:delete users:auth.users/delete] to role [1:admins]?      [Yes|No|All|Cancel] Yes


      added


   add permission [3:show my profile:auth.users/show] to role [1:admins]?      [Yes|No|All|Cancel] Yes


      added


   add permission [4:edit my profile:auth.users/update] to role [1:admins]?      [Yes|No|All|Cancel] Yes


      added





>> rbac applist





USAGE


    rbac applist controllers


    rbac applist actions <controller.id>





DESCRIPTION


    This command will list application controllers and their actions.








>> rbac search permissions users=id:1 permissions=action:%show%


+----+-----------------+---------------------+---------------+-----------+---------------------------------------------------------------------------+


| ID | Title           | Description         | Controller.ID | Action.ID | Business rule                                                             |


+----+-----------------+---------------------+---------------+-----------+---------------------------------------------------------------------------+


| 3  | show my profile | show my own profile | auth.users    | show      | return Yii::app()->user->id==Yii::app()->getController()->loadUser()->id; |


+----+-----------------+---------------------+---------------+-----------+---------------------------------------------------------------------------+


RBAC search criteria: permissions=id:3





Search returned 1 rows





>> rbac missing permissions


There are no permissions defined for these actions: 


+---------------+-----------+


| Controller.ID | Action.ID |


+---------------+-----------+


| auth.users    | create    |


| auth.users    | admin     |


+---------------+-----------+


Note: we list only those controllers that are using our RBAC filter: application.filters.AccessControlFilter


To find which controllers are not using RBAC filter run >> rbac missing controllers








>> rbac delete permissions=id:%


   delete permission 'list users', id=1     [Yes|No|All|Cancel] n


       skipping 


   delete permission 'delete users', id=2     [Yes|No|All|Cancel] n


       skipping 


   delete permission 'show my profile', id=3     [Yes|No|All|Cancel] c 








>> rbac missing controllers


These controllers are not using RBAC: 


+-----------------------------+-------------------------------+


| Controller.ID               | Number of permissions defined |


+-----------------------------+-------------------------------+


| site                        | 0                             |


| auth.roles                  | 0                             |


| auth.globalgroups           | 0                             |


| accounting.employees        | 0                             |


| projectmanagement.customers | 0                             |


+-----------------------------+-------------------------------+








>> rbac create permission auth.roles


Create permission for the auth.roles/update action? [Yes|No|All|Cancel]y


   Enter name: Update role


   Enter description: update any role


   Enter bizrule (leave empty if not needed): 


Created permission 'Update role' with id=7


Create permission for the auth.roles/delete action? [Yes|No|All|Cancel]c








This looks very nice! I think it is quite useful. You should share it as an extension.

The RBAC implemented in Yii is a generic one that is not tied with MVC. I also believe you can implement the access control you did here on top of the Yii RBAC (e.g. use controller+action as operation IDs)

Thanks! I thought I would add it as extension after:

  1. check/add support for other databases, not only mysql

  2. add configuration options like db table names, etc

  3. it needs testing :)

Added as an extension, hope to hear any suggestions, bug reports, etc :)

http://www.yiiframew…/alternaterbac/

I'm going on build a little CMS with Yii. Where user can update Article like Joomla. Select Section and than select Category to menage their articles.

But I want to ristriction the section access. Any user can update one section but not manage the others.

Can I use RBAC to do this?

Thanks

Hi, surely you can. You will need to create a separate permission named "update my section" and this permission should have a bizrule attached.

The actual PP code in the bizrule will depend on your application, but it may be something like:

return Yii::app()->user->id==Yii::app()->getController()->currentSection()->author_id;

where currentSection() is your custom method of Section Controller which will return model object of currently being processed section.

Quote

I'm going on build a little CMS with Yii. Where user can update Article like Joomla. Select Section and than select Category to menage their articles.

But I want to ristriction the section access. Any user can update one section but not manage the others.

Can I use RBAC to do this?

Thanks

Nice Idea.  :)

I found one small error: The PHP-code asks for a table "roles_has_permissions", but the SQL-file contains only the table "permissions_has_roles".

Yep, sorry. It should be called roles_has_permissions.

SQL file updated (attached to the first post).

I have tested it with the blog demo. Great work.  :)

I have one suggestion. When I applied it to the PostController I wasn't able to log in. So i added a user named "guest" and added following code to the filter:

class AccessControlFilter extends CFilter {


...  


    protected function getUserid()


    {


        if (Yii::app()->user->isGuest) 


        {


         $sql = 'SELECT * FROM users  


                WHERE username="guest"';


         $command = Yii::app()->db->createCommand($sql);


         $row = $command->queryRow();


         $userid=$row[id];  


        } else {


         $userid = Yii::app()->user->id;


        }


        return $userid;


    }





    protected function preFilter($filterChain)


    {


    ... 


        $command->bindParam(":uid", $this->getUserid(), PDO::PARAM_INT); 


       //$command->bindParam(":uid", Yii::app()->user->id, PDO::PARAM_INT);"


    ...


    }


...


}


Now I am able to add roles/permissions to a user who is not logged in.

Have you an idea how to show/hide links depending on the rbac? For example the "update"-link should only be visible, if the user has the permission to access it.

Quote

I have one suggestion. When I applied it to the PostController I wasn't able to log in.

Well, if only few actions are needed for guests (like login action) I think it would be better to just bypass AccessControlFilter for this action:



public function filters()


{


   return array(


     array(


            'application.filters.AccessControlFilter - login',


          ),


   );


}


Quote

Now I am able to add roles/permissions to a user who is not logged in.

Yep, I agree, in case you need to allow guests to access more then contact or login action it would be better to follow your solution!

Quote

Have you an idea how to show/hide links depending on the rbac? For example the "update"-link should only be visible, if the user has the permission to access it.

Was going to finish this functionality today :) I will also check it with latest Yii 1.0.3-dev and will then upload new version of RBAC.

You’re right. To bypass the filter is the easier way. I’m looking forward to test the next version. :)

Okay, new version. :)

Changelog:

  • AccessControlFilter renamed to RbacFilter (file and the class, don't forget to update filters() method)

  • Added application component Rbac

  • requires Yii v1.0.3

  • rbac component implements two methods for access validation

Installation:

  1. put Rbac.php to /protected/components

  2. put RbacFilter.php to /protected/filters

  3. put RbacCommand.php to /protected/commands/shell

  4. Configure 'rbac' component:



'components'=>array(


        'rbac' => array(


            'class'=>'application.components.Rbac',


        ),


.......


  1. add filter to the controllers:


public function filters()


{


   return array(


     array(


            'application.filters.RbacFilter',


          ),


   );


}


  1. new methods you can use to check permissions in application code:


Yii::app()->rbac->checkAccess( string $controller, string $action, number $user = null, array $fakeGET = null);


example:



<td><?php 


        if(Yii::app()->rbac->checkAccess($this->id, 'update', Yii::app()->user->id, array('id'=>$model->id))){


            echo CHtml::link('Edit', array('update','id'=>$model->id));


        }


        ?></td>


and other method:



public function checkAccessEx(mixed $searchCriteria, number $user = null);


This is more general method but it can't validate business rules.

example:



//true if current user belongs to role 'admins'


$userHasAccess1 = Yii::app()->rbac->checkAccessEx('roles=name:admins');





//true if user belongs to role 'admins' AND has permissions with id=1 OR id=2


$userHasAccess2 = Yii::app()->rbac->checkAccessEx(array('roles=name:admins', 'permissions=id:1,id:2'));


search criteria syntax follows the same as yiic shell rbac command.

new version attached.

Works fine for me.

I have attached the blog demo from Yii 1.0.2, where I added the Rbac-extension. There are two user: admin/admin and demo/demo. admin has full rights, demo has only the right to create and update his own posts, but not to delete them.

I hope it is helpful.

I tried to use Rbac with the blog demo. When I try "rbac missing permissions", I get following error:

Yii Interactive Tool v1.0


Please type 'help' for help. Type 'exit' to quit.


>> rbac missing controllers


These controllers are not using RBAC:


+---------------+-------------------------------+


| Controller.ID | Number of permissions defined |


+---------------+-------------------------------+


| comment       | 0                             |


+---------------+-------------------------------+





>> rbac missing permissions





Fatal error: Call to a member function filters() on a non-object in C:xampphtdocsblog103protectedcommandsshellRbacCommand.php on line 360





C:xampphtdocsblog103>





I added

            $ci = Yii::app()->createController($c['id']);


            if(is_array($ci)) $ci = $ci[0];//added code


            if($ci != null){


                $filters = $ci->filters();

from missing actions. After that, it works. The error appears only with Yii 1.0.3.

Edit:

Attached blog demo from Yii 1.0.3 with Rbac.

How many queries executed during a check user's permissions? If I want to verify the user and hide when necessary, 10 links with different permissions?

1 SQL query per each checking.

Quote

How many queries executed during a check user's permissions? If I want to verify the user and hide when necessary, 10 links with different permissions?

I've done some pretty minor edits to switch the tables over to Singular upper case tables. If anyone is interested I'll post the code.

In a week or so I'm going to upload heavily modified code. It will be a configurable Yii module, with own login/logout/signup/passwordForgot features and admin interface for managing roles, permissions and users.

Quote

Edit:

Attached blog demo from Yii 1.0.3 with Rbac.

There may be name mismatch between the table and the code. Error message says

Quote

The table "Post" for active record class "Post" cannot be found in the database.
while the schema shows

Quote

DROP TABLE IF EXISTS `post`;

CREATE TABLE IF NOT EXISTS post (

  id int(11) NOT NULL auto_increment,

  title varchar(128) NOT NULL,

  content text NOT NULL,

  contentDisplay text,

:

Quote

In a week or so I'm going to upload heavily modified code. It will be a configurable Yii module, with own login/logout/signup/passwordForgot features and admin interface for managing roles, permissions and users.

Very much looking forward to it.