[MODULE] HRBAC

Code is at : http://code.google.com/p/yii-hrbac/

and also at : http://www.yiiframework.com/extension/hierarchical-rbac/

It has had only minimal testing. Only for brave souls.

Can be used as a sub-module for the Yii-User-Management

module.

HRBAC uses an enhanced version of Yii rbac.

Main features:

  1. Single db query for access check.

  2. Support for ‘routes’. Can control access on module level,

    controller level or action level. Can also ignore some

    routes during automatic access check (based on config file).

  3. Two line change in index.php to activate it for all controllers.

  4. Support for ‘computed roles’. Roles such as ‘Author’, ‘Owner’,

    ‘Manager’ or such can be provided at run time depending on the

    users relationship to the target object.

  5. Complete set of UI to manage the roles and assignments.

Requirement:

  1. mysql : It’s mysql only at the moment.

  2. A user table with ‘id’ and ‘username’ columns.

Difference with Yii rbac:

  1. Single database query for access check if you do not use ‘bizrule’.

    Three db queries if you do.

  2. Each authorization item can have a conditions

    (set of boolean tests) associated with it.

  3. Each relationship can also have conditions associated with it.

Difference with srbac module:

  1. srbac uses Yii rbac only.

  2. srbac does not provide for bizrule with role assignment to users

    although it is supported by the Yii framework.

  3. srbac uses ajax extensively and certain tasks are easier.

  4. You need to change the base class of every controller that you

    want automated check against.

  5. Can generate auth items for controller actions by scanning code files.

Hi, i’m a newbie on yii . How should it be used to configure the Yii-User-Management module?

Yii-User-Management has it’s own lightweight role based access control. That will be made redundant when you use hrbac.

To use it as a sub-module change the config file main.php as follows.


	'modules' => array(

		'user' => array(

			'dateFormat' => "Y-m-d",

			'modules' => array( 

				'hrbac' => array(),

			),

		),

	),



Other than this follow the instructions in the readme.txt in the doc directory.

Note that any accessRules() within the controllers should probably be removed.

<span id=“result_box” class=“long_text”>Zeph, thanks for your reply, but I have a problem. I can’t get your module to work properly. Although you should have permission for all when I try to access the login for example I get the following error:

CHttpException

You do not have permission to perform this action. (D:\Proyectos\ems4t\project\webroot\protected\modules\hrbac\components\HrbacWebApplication.php:25)

#0 D:\Proyectos\ems4t\project\yii\base\CErrorHandler.php(235): HrbacWebApplication->runController(‘site/error’)

#1 D:\Proyectos\ems4t\project\yii\base\CErrorHandler.php(153): CErrorHandler->render(‘error’, Array)

#2 D:\Proyectos\ems4t\project\yii\base\CErrorHandler.php(96): CErrorHandler->handleException(Object(CHttpException))

#3 D:\Proyectos\ems4t\project\yii\base\CApplication.php(581): CErrorHandler->handle(Object(CExceptionEvent))

#4 [internal function]: CApplication->handleException(Object(CHttpException))

#5 {main}

I don’t understand why it happens. I followed the steps in the documentation …

Do take a look at the readme.txt

In this case you have to add


'openAccessRoutes'=>array('/'),

in your main.php. Once you’ve setup the permissions you’d change this line.

thanks Zeph, i going to try this. :)

Hi Zeph,

this sounded interesting, so i downloaded 0.005a to hava a look inside and the first thing i could say is "wtf?".

I’m sorry, but i read the readme.txt and configured everything accordingly.

Getting the exception that Controller.php couldn’t be found i first thought i’m nuts but then i saw that your module controllers are extending Controller instead of CController - whats that?

Afterwards the index action of HrbacuserController couldn’t be found. Well, of course not because there is no index action, but in CController ist the defaultAction which isn’t redefined in yours.

I also couldn’t find a place where you’re mentioning that you’re using the Menu and Breadcrumb widgets inside your views?!

Altogether i would say you’re using your own controller class which is extending CController but where is it?!

Really, i’m sorry, normally i’m not that upset with such things but i’ve wasted some frustrating time and afterwards i was ‘rewarded’ with those views :blink:

Regards

You are right Yoshi. It should have come with a noticeable warning.

I have this in the initial post but forgot it in the ‘extensions’ page. Will rectify that right away.

As to trying to resolve the issues you are having - if you are still interested - Change the base class of the offending controllers to CController and add the following in the controllers.


	public $menu=array();

	public $breadcrumbs=array();



This should get it working. I think part of the problem is because I’m using the latest Yii from the SVN.

I chose not to define a default action but feel free to add one to yours.

I have read your notice before. Of course contributed code may have bugs or other problems but please it should be at least usable.

Yes, that’s exactly what i did to correct those issues.

But you have to - when derive from CController - otherwise you’ll get an exception when calling the controller with ‘hrbac/hrbacuser’.

I really appreciate you’re replying to my post and not just ignoring such criticizing comments :)

Regards

Zeph, I have some problems with this extension.

I have everything working after installation (using latest version downloaded from here), but I want any user to log in. So I created Operation with the following path:

/user/access/login (that is the path to my action actionLogin in AccessController in module UserModule). I also attached this operation to the Anonymous user role. It should be working, but it is not.

I followed the execution, and I think the problem is in this query:


 SELECT senior_id, junior_id, path FROM AuthPath 

				WHERE junior_id IN (SELECT auth_id FROM AuthItem 

				WHERE INSTR('/user/access/login', name) = 1

					 AND RIGHT(name,1) = '/')  

					AND senior_id IN (2) 

					AND distance > 0 

				ORDER BY distance ASC

The internal query tries to match the generated path “/user/access/login” with “/user/access/login” I have in DB and it succeeds. However I don’t have the closing “/” after my data, so RIGHT(name,1)=’/’ fails and only auth_item=6 is selected. And this auth_item is not attached to Anonymous User role.

If I add trailing slash to my path, it works fine in the second check RIGHT(name,1)=’/’, but fails the first one, because strings do not match, second one is not find in the first one because of that trailing slash.

I wonder, why do you need the RIGHT(name,1)=’/’ check at all? What if I remove it? What will be broken?

I understand, that any path should match the “/” path which is assigned to admin only. But won’t any path match that one without RIGHT(name,1)=’/’ ?

When I changed my auth item to ‘/user/access/’, it worked fine, but it gives user access to all controller while I want to limit him to one particular action only.

Also, I did not find where I can extend roles. Could you please help me cause I’ve made some improvements (user table and primary key of that table can be configured) and going to share all my changes.

I just need to understand in full how is it working.

More questions. As far as I understood, the most interesting method there is HrbacManager::checkAccess().

I’ve went through it with debugger and I think I understood it partly, but still I prefer if you go though it with explanations either.

  1. What are computed roles and how to use them? As far as I understood, I should return them in my controller? Or what?

  2. Minor question, but still:


if(count($names)<4)

			$condition='name='.implode(' OR name=',$names);

		else

			$condition='name IN ('.implode(', ',$names).')';

Why not simply:


$condition='name IN ('.implode(', ',$names).')';

Is there any performance gain in the first variant?

  1. $this->usePhpRules. When this becomes true? What does this reflect? And coudl you please explain what goes on in that block:

if( !$this->usePhpRules )

		{

			

			$sql = "SELECT 1, auth_id, cond FROM {$this->assignmentTable} WHERE user_id = :userid 

				UNION ALL SELECT 0, senior_id, concat(senior_cond, chained_cond, junior_cond) 

					FROM {$this->pathTable} AS p, {$this->itemTable} AS i 

					WHERE p.senior_id = i.auth_id  

					AND {$juniorCond} 

					AND ( senior_id IN ( SELECT auth_id FROM {$this->assignmentTable} WHERE user_id = :userid) 

						OR $condition) ";

			$command = $this->db->createCommand($sql);

			$rows = $command->queryAll(false,array(':itemname'=>$itemName, ':userid'=>$userId)) ;

			if( count($rows) == 0)

				return false;

			$validRoles = array();

			foreach($rows as $row)

			{

				list($userCheck, $auth, $cond) = $row;

				if($userCheck && self::checkConditions($cond,$paramsCond))

				{

					$validRoles[] = $auth;

				}

				elseif ( in_array($auth, $validRoles) ) 

				{

					if( self::checkConditions($cond,$paramsCond) )

						return true;

				}

			}

			return false;

		}

What does that SQL select? And please explain the whole logic.

  1. You’re using INSTR($route, name) = 1. Can we replace it with name LIKE ‘$route%’? Will it be the same? I think the search should be faster with LIKE

  2. AuthPath table has distance field. Does this reflect the level of inclusion? say Role X includes task Y. Task Y includes task Z, task Z includes operation F. Could you please list example records for this case?

  3. What do we select here:


$sql = "SELECT senior_id, junior_id, path FROM {$this->pathTable} 

				WHERE $juniorCond  

					AND senior_id IN (".implode(', ',$auth_ids).") 

					AND distance > 0 

				ORDER BY distance ASC";

What is stored in the field "path"?

  1. I guess in lines 218-248 in this method (version 0.005b from here downloaded yesterday) we check if each of the selected auth items can be valid using the given bizRules/conditions

I also want to know how I can specify data for bizRules/conditions when they are checked. For example, I want user to edit only his profile. Access path is /user/profile. I add authItem Operation named "editProfile", but I want to assign it to the user only if he opens the page with his profile.

Alternative way is to add bizRule to the operation itself and it will check ifcurrent user is going to access his profile.

In Yii-native RBAC we did like this:


Yii::app()->user->checkAccess('editProfile', array('profile' => $profile))

bizRule would be:


return Yii::app()->user->id === $params['profile']->profileId

I am interested in accomplishing the same, but without additional actions in the actionProfileEdit, so it is checked by the HRBAC or SuperHRBAC or something automatically.

One more question is about overall execution. In order to use it I add to index.php:


require_once(dirname(__FILE__).'/protected/modules/hrbac/components/HrbacWebApplication.php');

Yii::createApplication('HrbacWebApplication', $config)->run();

How does the second line work?

Final question. Is there any way to implement "negative" rules. For example, I want user B do the same as A (extend), but prohibit login from IP XXX.XXX.XXX.XXX. Or prohibit certain task. This can be compared with extending and redefining some permission. Any ideas what can be changed in your extension in order to allow it?

Thank you for all the patience and replies!

Let me first address your initial post (#10) http://www.yiiframework.com/forum/index.php?/topic/8400-module-hrbac/page__view__findpost__p__46971

a ) The bug fix. Modify the code at the start of function checkAccess() to following.


		if( $itemName[0] == '/' )

		{

			// In route check

			$juniorCond = "junior_id IN (SELECT auth_id FROM {$this->itemTable} 

				WHERE ( INSTR(" . $this->db->quoteValue($itemName) . ", name) = 1

						AND RIGHT(name,1) = '/')  

					OR name = " . $this->db->quoteValue($itemName) . ")";



b ) Extending roles.

Consider this as adding other roles, operation groups or operations to the role you are extending.

Go to

mysite.com/index.php?r=hrbac/hrbacitem/index

To create a role, follow the link "Create Auth Item" or click on the role if it already exists.

The next page provides you with a link "Include other items". Click on that link to add other roles,op groups or ops to the role you are working with.

I might not have understood your question so let me know if this doesn’t address it.

Response to post #11 http://www.yiiframework.com/forum/index.php?/topic/8400-module-hrbac/page__view__findpost__p__46994

  1. What are computed roles and how to use them?

Consider the situation where a user is viewing a post that he authored. Here, the role ‘Author’ would be dependent on the target object. Or consider the situation where a group of writers is supervised by an ‘Editor’. The ‘Editor’ role would then depend on whether the target object or post was written by a writer from the users team. The Computed Roles is there to help with these kind of situations, where you might want to assign a role to the user based on information about the target object.

When you need this functionality, define a public function getComputedRoles() in your controller. This function would then return an array of roles (strings). The array can be empty.

I copied that code from the Yii core file. I don’t really know if there’s any performance benefit.

Hrbac extends the rbac feature of Yii core. PhpRules (called bizRule in the documentation) are a feature of the Yii core. It allows you attach php code to any auth item (roles and such) which would further check if an assigned role is enabled for that particular request. Using this, you could - to give a simple example - specify that a person is a moderator only from 9am to 11am.

Personally I don’t like the idea of executing php code from a database. Hrbac provides ‘Conditions’ that should help avoid PhpRules.

Additionally if we don’t use PhpRules, it improves the performance of checkAccess(). There are fewer database queries.

  1. You might be right, “LIKE ‘$route%’” would probably be faster. It would also make

				if($routeCheck && strpos($itemName, $row['name']) === 0)

					return true;



redundant.

  1. AuthPath table has distance field.

Yes, this is the generational distance in the hierarchy. An items distance from itself is 0, from it’s parent or child is 1, from it’s grandparent or grandchild is 2 and so on.

This entire table is for performance purposes and should not be edited manually.

  1. Column ‘path’ in table ‘AuthPath’

This again is for performance and deals with the hierarchy. It contains the items in the hierarchy between an ancestor and a descendant.

The code is basically to check whether a role has permission for a given operation within a single db query. Remember that a role might contain another role, which in turn may contain an operation group which in turn might contain the operation that we want to test for.

7.a ) Bizrule and data

The ‘data’ used by bizRule has to be in the database. However, Hrbac does not contain any user interface to specify this data as the data is considered to be binary. This is part of the Yii core. You will need addition code in your application to enter this data in the db. I can imagine that this might be useful where a user subscribes ( role : Subscribed User ) through a payment service and they provide data specifying how long the subscription is valid for.

7.b ) Conditions and runtime data

You can provide data for use with conditions by specifying the $paramsCond parameter when calling checkAccess(). When $paramsCond is not provided then the $_REQUEST global variable is used instead.

7.c ) Editing own profile

This is where ‘Computed Role’ comes in. A user would have the role of ‘Owner’ or ‘ProfileOwner’ but instead of this role being assigned in the DB it would be assigned only at runtime based on the target object ( the profile in this case). The ‘Owner’ role would have permission to edit a profile. See the first item in this post.

Note that you’d have to save any data queried from the DB in the controller so you don’t query for the same data when the action is finally called.

As to doing the check automatically, I don’t have any ideas in that regard.

7.d ) Yii::createApplication(‘HrbacWebApplication’, $config)->run();

This line just replaces the default line

Yii::createApplication(‘CWebApplication’, $config)->run();

and creates the subclass HrbacWebApplication instead of CWebApplication

7.e ) Negative rules.

For what you’ve described, you could use bizRules or Conditions.

For example, you could have a role ‘ForumModerator’. You could then create a role ‘ChineseForumModerator’ which could contain the role ‘ForumModerator’ but with the condition that forumid equals chinese. Or you could assign the role of ‘ForumModerator’ to a user with the condition that forumid equals chinese. That user would then be able to moderate only the Chinese forum.

As for blocking IP addresses, personally I’d have that be a separate system from the rbac.

Why do we need RIGHT(name,1) after all? Could you please give me an example where it is required? Because if we omit it, everything is OK.

It’s there to avoid unintended consequences. Suppose you have the item ‘/user/access/login’, removing RIGHT(name, 1) would cause it to match ‘/user/access’ or even ‘/user/access/log’.

We basically want the item to end with the ‘/’ to clearly indicate that all sub-modules or actions for that module or controller is allowed. Lacking the ending ‘/’, we are saying that it is an action and should match exactly.

Hello all,

I’,m having problems in the detectIdLoop method in the HrbacManage file.

The query


"SELECT COUNT(*) FROM {$this->pathTable} WHERE auth_id = $childId AND child_id = $itemId";

is returning an error “CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘auth_id’ in ‘where clause’” due to AuthPath table haven’t a column called ‘auth_id’.

This error is called when I call the function


$task->addChild('listProducts');

What should I do to fix the bug?

Probably some similar error can occurr in recreateAuthPathTable method with sql query:


$sql = "INSERT INTO {$this->pathTable} 

			SELECT auth_id, auth_id, 0, '', cond, cond, '' 

			FROM {$this->itemTable}";

Thank you in advance.

Regards:

Kike

I have been trying to fix the problem and really the method “addChild” is not working, after fixing temporaly the previous bug, I found that is giving me an other error in method “addItemChild” in HrbacMaager.php file is giving me an error because is trying to insert the names of the items in the authchild table instead of theirs id’s obtained from authitem table.

Is this module compatible with the rbac interface and prepaired to be used with the standard methods described in the yii manual?

Thank you very much.

Regards:

Kike