Reputation: 1769
I have a role, task and operation hierarchy set up, and assigned to users, with operations mirroring controller:action.
From the docs, if I want to check that a user has access to an operation, I check, e.g:
if(Yii::app()->user->checkAccess('createPost'))
This gives the impression that I have to manually add code in every action to check if the user is authorised to the corresponding role. Am I missing something - surely there's a way of doing this automatically for each action.
I could probably extend the base Controller class and add something (in preFilter?) that uses Yii::app()->controller and Yii::app()->controller->action to generate the role, and then checks the user is authorised to that role.
But I'm sure there must be a way of doing this already?
I know the accessRules filter allows you to pass roles in, but that seems to defeat the purpose of having a role>task>operation hierarchy, and having to assign such rules to each action is nearly as bad as doing the check in each function in the first place.
Is there some other option I'm missing that will automatically check that a user performing e.g. /item/delete is authorised to the operation 'item:delete' (or whatever format is expected in the DB AuthItem table)?
EDIT
Clarifying my issue with accessRules alongside database RBAC:
I define a role structure in my database - a number of roles, each of which is authorised to multiple tasks, which in turn are composed of granular operations (e.g. role 'Reader' might be authorised to task 'Browse Posts', which is made up of 'post/index' and 'post/view'). I then assign a user to a role, which grants them authority to that role's tasks, and the operations within those tasks. If I then call user->checkAccess for a controller action (i.e. operation), it checks in the DB that the user is authorised to a role, which contains a task, which includes that operation.
But, if I go through each controller and define what roles are authorised to each action, I'm duplicating the work I've already done in the database, possibly with conflicting rules.
Upvotes: 1
Views: 2429
Reputation: 1769
Filters seem to have done the trick.
Assuming you've got a base controller defined in protected/components/Controller.php, add the following function to it to define a filter. If for some reason you don't have a base Controller class, define the filter in a separate class, as described in the Controller documentation. It might be better (more reusable) to do it that way anyway.
public function filterAccessControl($filterChain)
{
$controller = Yii::app()->controller->id;
$action = Yii::app()->controller->action->id;
//The RBAC admin module I'm using creates entries for operations as, e.g. Post:Create
// You may need to change this to match whatever entry format you have in your AuthItem table
$operation = ucfirst($controller) . ':' . ucfirst($action);
Yii::log('Checking auth for user: ' . Yii::app()->user->id. ' to operation: ' . $operation, 'info');
if(Yii::app()->user->checkAccess($operation))
{
Yii::log('User authorised', 'info');
$filterChain->run();
return true;
}
else
{
Yii::log('Unauthorised user!!!!!', 'info');
throw new CHttpException(401, 'You are not authorized to perform this action.');
return false;
}
}
(I'm using RBAM to administer the Auth tables in the DB, which stores the operation name as ControllerName:ActionName, hence the ucfirst).
To run this filter for every action in every controller, apply the filter by adding a filters() function, again in your Controller.php:
public function filters()
{
return array(
'accessControl',
);
}
Some caveats:
Upvotes: 2
Reputation: 196
What about using yii-auth ? https://github.com/Crisu83/yii-auth
you can avoid checking every place this - if(Yii::app()->user->checkAccess('createPost'))
and use something like
public function filters()
{
return array(
array('auth.filters.AuthFilter'),
),
}
Upvotes: 0
Reputation: 1467
My answer might not shed much of a new light on the problem:
Eventually, the application cannot guess who is allowed to do each operation. You'll have to tell Yii who is allowed to do what action - this way (which might be longer) or the other way (which may be shorter).
You can do this in the 'long way', which is adding a 'checkAccess()' in the beginning of each action method, and reacting with a custom message if the user does not have the access. Note that this way, even if long, is verbose and clean, which leads to easier maintenance of the code.
You can also utilize, as you point out, 'access control filter' with a 'accessRules' method. That's shorter, but less clear to an outsider (which can be you, after a few months away from that mental area of the code), hence the code is less maintainable, IMHO.
I do want to point out that using the 'access control filter' with RBAC does NOT contradict or undermines a very nice permission's hierarchy. Remember that this hierarchy comprises of roles that inherit from each other, and each role comprising tasks and possibly operations (YMMV. I typically do roles that inherit from each other, and each role is "comprised" of tasks, some of which have bizrules). If you can elaborate more why do you think this will "defeat the purpose of having a role>task>operation hierarchy" that can be fruitful for this discussion.
Upvotes: 0