rbac Rule doesn't work

Hello,

I’ve created a yii basic project. I’m using DbManager to manage roles, permissions and rules. The first two of them works fine, but the rules are freaking me out. When I try to check the access (via “can” function) doesn’t work at all.

What do I’ve done:

I’ve created a class named “CompetencyRule”, inside a folder that i’ve created as well called “rbac”. So, the full path is the following: “app/rbac/CompetencyRule.php” (yes, “rbac” is in the same level that “models”, “controllers”, “config”, etc.)

here is the source:




namespace app\rbac;


use yii\rbac\Rule;


class CompetencyRule extends Rule

{

    public $name = 'competencyRule';

    

    public function execute($user, $item, $params)

    {

        return false;

    }


}



As you can see, I’ve forced to return false no matter what, to verify that the function is called. But the function is never called!

here is the stract of code in where that function shoud be called:





public function actionUpdate( $id, $formationPlanId = null )

{

    $model = $this->findModel( $id );

    

    if( !Yii::$app->user->can('update-comp', ['comp' => $model]) ){

            return $this->redirect( ['view', 'id' => $model->id] );

    }

        

        //more unusefull code bellow...




The idea is that the user can update this model, only if certain logic occurs. According to the code above, the flow should enter inside that “if” sentence, but it doesn’t.

The logged in user has a “Director” role, i’ve already loaded the code below:





        $rule = new CompetencyRule();

        $auth->add($rule);

        

        $editOwnCompetency = $auth->createPermission('editOwnCompetency');

        $editOwnCompetency->description = 'Edita su propia competencia';

        $editOwnCompetency->ruleName = $rule->name;

        $auth->add($editOwnCompetency);

        

        $editCompetency = $auth->getPermission(Permissions::$COMPETENCY_UPDATE);

        $auth->addChild($editOwnCompetency, $editCompetency);

        

        $dir = $auth->getRole('Director');

        $auth->addChild($dir, $editOwnCompetency);




Any Idea?

I hope you can help me please, if you need more information, please tell me and I’ll provide it.

Thanks in advance

Everything looks fine…

Is $COMPETENCY_UPDATE a static attribute of class Permissions with value ‘update-comp’?

yep, I wanted to remove this $COMPETENCY_UPDATE attribute from my comment to make it clearer and I forgot to change it in that line.

I think this is because you forgot to assign the role ‘Director’ to the current logged user, as below:




$auth->assign($dir, \Yii::$app->user->id);



you can check the table auth_assignment to see whether Director is assigned to that user.

when you call:


Yii::$app->user->can('update-comp', ['comp' => $model])

Yii first look in the database to check the user has permission on ‘update-comp’ or not. if yes, the code in CompetencyRule will be run, otherwise it will return false immediately

Yes I did, I assigned that role to the logged user. The code to create the user is the following:





public static function createDirectorAccount($model)

    {

        $auth = Yii::$app->authManager;

        $newUser = new User();

        

        $newUser->username = str_replace(".", "", $model->rut);

        $newUser->password = crypt( '1234', Yii::$app->params["salt"] );

        $newUser->rut = $model->rut;

        $newUser->name = $model->name;

        $newUser->last_name = $model->last_name;

        $newUser->email = $model->email;

        $newUser->phone = $model->phone;

        $newUser->auth_key = SiteController::randKey( "abcdef0123456789", 150 );

        $newUser->access_token = SiteController::randKey( "abcdef0123456789", 150 );

        $newUser->activate = true;

        

        $newUser->save();

        

        //se establecen los permisos de director

        $auth->assign($auth->getRole('Director'), $newUser->id);

        

    }




And then, I login with that account and that’s when I got the problem… Obviously, the “createDirectorAccount()” function is executed far after the code I have commented in my first post.

I look in the database and everything looks fine, the tables "auth_item", "auth_item_child", "auth_assignment" are perfectly matching every relation…

I don’t know what to do :(

I’ve solved it (I think so).

Could be the guide wrong?

the solution (until now) is to call the “can” function with the permission’s name that contains the rule’s name.

I mean:




public function actionUpdate( $id, $formationPlanId = null )

{

    $model = $this->findModel( $id );

    

    if( !Yii::$app->user->can('editOwnCompetency', ['comp' => $model]) ){

            return $this->redirect( ['view', 'id' => $model->id] );

    }

        

        //more unusefull code bellow...




(see my code above to understand)

the ‘editOwnCompetency’ permission is the new one that I created to asign the rule…

I don’t know what’s wrong but, as I said, until now that works for me.

Thanks for your help (?)

Edit:

I know that the algorithm makes a search in all the auth_assignments in which the item is related to. The algorithm makes a call to “executeRule” with ‘update-comp’ and then, recursively, with all their relations but never with the new permission associated with the rule… so I have to force the call by doing the “solution” that I said above in this comment…

I hope this could be useful for anyone. And I hope someone could tell me why is this happening…

Anyway, cheers.

It’s strange.

Doesn’t ‘update-comp’ have another child other than ‘editOwnCompetency’?

It looks to me that the user has another route from ‘update-comp’ to ‘Director’ (or another role that the user is assigned).

[EDIT]

Oh, I should have asked "Doesn’t ‘update-comp’ have another parent other than ‘editOwnCompetency’?

  • ‘update-comp’ is child of ‘Administrador’, ‘Director’ and ‘editOwnCompetency’

  • ‘Administrador’ and ‘Director’ are roles.

  • ‘update-comp’ has no children.

  • ‘Director’ is parent of ‘editOwnCOmpetency’ and ‘update-comp’

  • the user only has the ‘Director’ role.

  • ‘editOwnCompetency’ has the rule associated.

That is how I have the information in the database by executing the code that you already have seen. Is there something wrong with that?

The image attached contains a diagram with the elements of the list above. (rectangles are roles, rectangles with rounded corners are permissions, the face is the user and the other shape is the rule. the arrows represent the heritage, I hope it is clear)

7104

Captura.PNG

‘update-comp’ should not have ‘Director’ role as a direct parent.

Or, user could follow the path of ‘Director’ => ‘update-comp’, by-passing ‘editOwnCOmpetency’.

what you say really make sense to me, indeed I thought the same… but when I do it the "Forbidden (#403)" page is shown and not the view page as I wanted…

I don’t know why, but “my solution” is the only way to make it work

How did you set up ACF? The access control filter will show the error page before calling the controller action.

I don’t know if I understood your question, but here are all my configurations:

first, the behaviors in the controller class:




class CompetencyController extends Controller

{


    /**

     * @inheritdoc

     */

    public function behaviors()

    {

        return [

            'verbs' => [

                'class' => VerbFilter::className(),

                'actions' => [

                    'delete' => ['POST'],

                ],

            ],

            'access' => [

                'class' => AccessControl::className(),

                'only' => ['index', 'view', 'create', 'update', 'delete', 'new', 'addGlobal'],

                'rules' => [

                    ...

                    

                    [

                        'actions' => ['update'],

                        'allow' => true,

                        'roles' => ['update-comp',],

                    ],


                    ...

                ],

            ],

        ];

    }



now, the config/web.php




$config = [

    ...

    'components' => [

        'authManager' => [

            'class' => 'yii\rbac\DbManager',

        ],

       ... 



and it’s the same in config/console.php

I don’t know what other setup are you telling me…

Anything I’m missing, please tell me and I’ll provide you.

Thanks for your time.

This is the configuration for ACF.

As you are requiring ‘update-comp’ for ‘update’ action, ACF will check to see if the user can do ‘update-comp’. And when ‘update-comp’ is a child of ‘editOwnCompetency’, then ACF will check to see if the user can do it also. If the result is negative, then ACF will stop processing the request and show the exception page, instead of proceeding to call ‘update’ action. That’s why you got 403 forbidden page instead of redirecting to ‘view’ page.

So, if you want to check the access right in the controller as you have been trying, you need to set a weaker rule for ‘update’ in the ACF configuration. The same rule for ‘view’, for instance, will do it.

[edit]

Checking ‘editOwnCompetency’ in the controller will not be a solution, because it will deny ‘Administrator’ to edit other person’s comp.

what you said has worked very well… but I don’t get the logic here… I put another configuration to the update action like this:





'access' => [

                'class' => AccessControl::className(),

                'only' => ['index', 'view', 'create', 'update', 'delete', 'new', 'addGlobal'],

                'rules' => [

                    ...

                    

                    [

                        'actions' => ['update'],

                        'allow' => true,

                        //'roles' => ['update-comp',],

                        'roles'=>['view-comp',],  

                    ],


                    ...

                ],

            ],




and it works. Administrator can edit every ‘comp’ and the user can edit just their own… but it’s a little bit confusing

you are totally right about that

well, I think I’ll have to think more about that logic and see how it really works…

thanks for your time.

Ps: maybe you can do a full explanation about this case? =P

Edit:

I also removed the direct relation between Director and update-comp, as you suggested.

The key point of RBAC is the network of auth items (permissions and roles) connected with one-way arrows.

If a user can find a path starting from a certain action to him/her following the arrows, then he/she can do it.

ACF (Access Control Filter) is sometimes considered to be an alternative to RBAC, but it is not. They are not in the same level. In fact, you can use RBAC in ACF. When you specify some role name in the rule of ACF, you are telling ACF to check the user’s right using RBAC.

The counterpart of ACF may be the manual auth checking in the controller.




if (Yii::$app->user->can('update-comp')) {

    ...

}



If the user has not the right to do ‘update-comp’, then he/she will be denied the access to the action by ACF.

If he/she has the right, then he/she will pass the access check twice: one for ACF and another for the manual checking witten in the controller.

So, it could be a bit redundant in a way.

I would recommend keeping ACF simple and light. Heavier access checking using rules can be done better with the manual checking approach in the controller.

I forgot to reply, my bad.

ok, I think I understand it. Thank you for everything.

Happy code!