Mick O'Hea
Mick O'Hea

Reputation: 1769

RBAC in Yii - access checking

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

Answers (3)

Mick O'Hea
Mick O'Hea

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:

  • if you have existing filters in any of your controllers, you'll need to apply this filter there instead, since that controller filters() function will override this one
  • It might be safer to do it in each controller individually anyway, since if necessary you can then limit what actions the filter applies to if for some reason you want some exceptions. Plus if you only do it here, then forget and later add other filters to some controller, you'll lose your access control for those!
  • Before doing this, ensure you've configured your default guest role in your authManager config, and that role has access to /site/login. Otherwise you won't be able to log in! (If this happens, just disable the filter until you've sorted it out)

Upvotes: 2

Munawer Aziz
Munawer Aziz

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

Boaz Rymland
Boaz Rymland

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

Related Questions