Does Anyone Have A Working Example Of Rbac?

Assuming your user is associated with some AR model:




<?php

// access rules

return array(

    // Admin has full access. Applies to every controller action.

    array(

        'allow',

        'expression' =>'Yii::app()->user->model->isAdmin',

    ),

    // delegate to user model methods to determine ownership

    array(

        'allow',

        'expression' =>'Yii::app()->user->model->isPostOwner(Yii::app()->request->getQuery("postId"))',

        'actions'    =>array('editPost', 'uploadPostImage'),

    ),

    array(

        'allow',

        'expression' =>'Yii::app()->user->model->isImageOwner(Yii::app()->request->getQuery("imageId"))',

        'actions'    =>array('editImage'),

    ),

    array('deny'),

);

...


// In your user model

public function isPostOwner($postId)

{

    $post = Post::model()->findByPk($postId);

    return !$post || ($post->owner_id === $this->id);

}


public function isImageOwner($imageId)

{

    $image = Image::model()->findByPk($imageId);

    return $image && ($image->owner_id === $this->id) && $this->isPostOwner($image->post_id);

}



If you abstract your action params away from the HTTP request, a la my yii-rest extension or the Symfony2 FOSRESTBundle, you can do this more systematically.

In general, I think action parameters should be treated as a model, not as a raw array like Yii does out of this box. This is a good reason why. Suppose the action param $postId was actually just an instance of Post; your access rules would be extremely clear. (See below.) You could also validate them!




// Access rules if action params was an object/model

return [

    [

        'allow',

        'expression' =>'Yii::app()->user->isAdmin',

    ],

    [

        'allow',

        'expression' =>[$this->actionParams->post, 'isOwnedByUser'],

        'actions' =>...

    ],

    [

        'allow',

        'expression' =>[$this->actionParams->image, 'isOwnedByUser'],

        'actions' =>...

    ],

];



Also, if we’re talking about images, what’s the best way to check access to ‘parent’ record (‘post’ in this case)?

The thing is, if we’re not checking access to parent, nasty user can attach images to other users’ posts, that’s insecure. So we need another check for owner or rights.

That includes yet another DB query and checkAccess() call. Is there any way to avoid it, or make less annoying?

PS. One day I was thinking about merging owner_id and primary key, like this:


111111_222222

^----^ ^----^

owner   PK

That’s crazy but no need to query DB for parent :)

I’d say just query the AR relationship. Use “with =>‘post’” if you want to avoid the extra DB call.

I definitely would not combine the keys. That seems like a maintenance nightmare down the road.

You could also validate the post_id attribute in your model. E.g. instead of just validating that the post exists, validate that the current user is owns it.

And to simplify the above further, you could just make your own authmanager component implementing the CDbAuthManager::checkAccess() method/signature. That way if you ever wanted to switch to RBAC you could just swap components.


// In your user model

public function isPostOwner($postId)

{

    $post = Post::model()->findByPk($postId);

    return !$post || ($post->owner_id === $this->id);

}

This doesn’t seem good to me also.

First of all, on real-world examples user model will become bulky very soon.

Next, you use extra query here.

As for me, I’d like to

  1. keep the code as clean as possible

  2. avoid extra queries for rights check.

Let’s look at common update action :


function actionUpdate($id)

{

    $record = Model::find($id);

    if (...POST) {... $record->save();}

}

We’ve already found the record, so the only thing left is to check for rights.


function actionUpdate($id)

{

    $record = Model::find($id); // HERE IT IS

    if (... cannot edit...) throw exception.

    if (...POST) {... $record->save();}

}

Yes, we can store it somewhere (private $_record) and use $this->getRecord($id) to avoid extra query.

But still we need to check for parent… :(

Yes, finally I’ve come to that. Don’t like it either, btw.

Make your own auth manager component implementing checkAccess(). Takes 10 minutes and you consolidate all access checks there. If you switch from RBAC later, you keep the same interface. (Only have to swap component.)

Also you don’t eliminate the multi-db-query problem using RBAC. If anything, RBAC adds a LOT of extra DB calls.

Any decent-sized RBAC implementation will require a web interface to manage. If you’re not planning on taking it that far it’s not worth the effort.

Besides, access checks should not be in your actions. The action should be dead simple. The ideal is something like:




<?php

public function actionSave(CActiveRecord $model, array $data=null)

{

    $model->setAttributes($data);

    $saved = $model->save();

    $this->render('saved', array('saved' =>$saved, 'model' =>$model));

}



That means handling parameter mapping and authorization before the action is ever invoked.

In most cases PhpManager is enough, so no DB calls (except for those for record and parent)

Ok, I’ll think about that. Thanks for a lot of code :)

PS. I hope we didn’t give a fright to this post’s author :)

Haha, yeah! I hope some good ideas came across. I think Yii in general is REALLY bad about keeping controllers/actions thin. Here’s the gist of my thinking. Actions should be EXTREMELY thin. Action parameters should be supplied by a persistent (throughout the request) model, so that they can be filtered. Controllers just link together filters and actions; they should only be minimally involved in the logic required to load a model.

First of all, thank you very much. I really appreciate both the extensiveness of your responses, and the thorough discussion!

ORey: thanks for explaining RBAC. It enables me to make an informed decision. I think it would be a good basis for the RBAC doc on yiisoft github. If you want I can try to submit it, with credit going to you.

In my application, everyone except admin is only able to view (different amounts of) data, so there is no need to assign owners to uploaded assets ets.

Therefore, I think I’ll skip RBAC and go with @danschmidt5189’s suggestion. My roles are expressed as an integer, where a higher number means more acces. This means I can simply check whether the user’s role number is equal to the minimum allowed role number.

Actually this is my very first bite of RBAC (before Yii2 I was using something similar to danschmidt’s approach), so I’m not sure I get it right. This needs to be reviewed and corrected by Yii core devs (or by somebody who knows how to do it the right way).

But in general I don’t mind if you use it for commit or something. Just fix my terrible English :)

Future proof yourself by implementing this through your own AuthManager component that implements checkAccess(). Use the \yii\rbac\Manager::checkAccess() signature. ($itemName, $userId, $params.)

It’s a quick implementation, and should you ever decide to switch to RBAC you will only have to change the component and not all of your authorization checks. IF it ever comes to that, you will be happy you did.

Ok, I’m done. Happy coding. :)

Only one little correction: we’re inside Yii 2.0 thread, so class names and signatures may differ.

Thanks for the advice!

By the way, I added your info to the Yii doc: https://github.com/yiisoft/yii2/pull/1300

I’m not very sure this is the way RBAC is running but I’m just starting trying to understand yii2 RBAC. In the above example, I suppose a role should be assigned to ‘roles’ and not an operation. EG:




[

    'allow' => true,

    'roles' => ['moderator'],

]



Or am I totally wrong?

Note that if I’m right then the official doc should be fixed: Basic information about RBAC

Yah im now learning the hard way that the official doc is pretty out of date with what should be done to implement this. So much source code to read to figure out how this works! Im just going to abandon RBAC altogether now lol.

Does anyone have a working example of YII2 RBAC PhpManager with a working update own post??

(With the Yii advanced template)

Because I want thousands of special users to be able to update own posts and images.

I don’t think it is that hard but after googling for weeks I only get lots of information not to use the DbManager

because it will create lots of extra db calls.

And not how to theoraticly set up RBAC to update own posts…

The best working example with RBAC is http://www.yiiframework.com/extension/yii2-improved-advanced-template/

And I’ve asked the developer to ad a simple posts table to update own posts…

Thanks in advance!!

1 Like

You can place the check in the behaviors() function to easily control access to actions but you can also use it 1) in views to show and hide things based on permissions (you should use permissions rather than roles) and also you might need finer control over permissions such as if user has permission X then a post is added with a property set to something, otherwise it is not set. That kind of fine grained control can live in the action function itself.

I made a video about rbac in case anyone’s interested. The concept takes longer to understand than it does to code it.

Ha…ha…

Looks like I will use clasic one instead. Need more time to learn RBAC but still can’t find any working example with large user and certain user group beside “admin” username.

If anyone have working example please give it here… :rolleyes: