RBAC - Just can't get my head around it

Can we get a more complete example together for the RBAC documentation - or add RBAC functionality to the blog demo. I’m really struggling to get to grips with it - happy to do the work if someone points me in the right direction.

Here’s my specific question, based around the documentation.

I’ve got Posts in my blog, and I want to allow admins, editors, and the author himself to edit Posts.

So I follow the example in the documentation and create a task for author to updateOwnPost




// CREATE THE TASK

$bizRule='return Yii::app()->user->id==$params["post"]->authID;';

$task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule);

$task->addChild('updatePost');


// CREATE THE AUTHOR ROLE

$role=$auth->createRole('author');

$role->addChild('updateOwnPost');



And this is where I get stuck. Why is updatePost a child of updateOwnPost?

I also want the editor and admin to be able to update every post. I can add the updatePost operation as a child of their roles - no problem. But then in the controller how do I handle this? I could do the following but it seems longwinded.




class PostController extends CController

{

  ..


  public function actionUpdate ()

  {

    $model = $this->loadPost();

    

    if (Yii::app()->user->checkAccess('updatePost') 

        || Yii::app()->user->checkAccess('updateOwnPost',array('post'=>$post)))

    {

      // UPDATE THE POST

    }


  }

  ..

}



I haven’t tried it yet, but it also surprised me to see in the documentation the task ‘updatePost’ added as a child of ‘updateOwnPost’. Shouldn’t it be the other way around? Since all users with the ‘updatePost’ task should be able to pass the check for ‘updateOwnPost’, without even executing the business rule.

Is there a tutorial on how to use, manage and handle RBAC in a real context with some examples?

I’m sorry for bumping this, but I’m still struggling on.

To summarise the question: How can I allow an admin to update any post, but an author to only update their own?

At the moment I am having to do this in my controller:




<?php

if (Yii::app()->user->checkAccess('updateOwnPost',array('userId'=>$post->authorId)) OR Yii::app()->checkAccess('updatePost')) 

{

   // ALLOW ACCESS

}

?>



Is there any way for this to be automatic? Ie. A role with ‘updatePost’ automatically passes ‘updateOwnPost’.

You’re not the only one struggling - I’ve spent many hours on this, and still I’m none the wiser. I also read through general RBAC descriptions on Wikipedia and elsewhere to try to get a handle on this, to no avail. I was taught the theory in school, but that was 13 years agoo, and I’ve never used it since, so…

Yii is badly in need of a real-world application with practical demonstrations of RBAC and many other advanced features. Unfortunately, the demos only demonstrate the trivial stuff, which isn’t hard to come to terms with in the first place.

I’m with you guys!

I’m not fully understand this manual too.

And I suggest, let somebody, who understand this, draw some diagram, which help understand something

Me too!

I ask this question in Russian part of this forum, but nobody answered me yet :(

Quang, please Help us, save us! :rolleyes:

up. Help us :P

up

just to add to the conversation here, check out the SRBAC extension. it gives a very simple interface for managing access rules so you don’t have to code it.

Yes but it’s not a good way to depend on an extension :) I want to learn it :)

The extension is excellent, but using it effectively depends on understanding the core relationship between roles, tasks and operations.

I think the main problem is that the structure of the RBAC system is somewhat unclear. We all understand what roles are - an Admin role might consist of Editor and Author roles. But for me it all falls down on tasks and operations.

I understand the basics: a role has permission to carry out tasks, and each task is made up of operations. But a task can consist of other tasks, and an operation of other operations. I’m left wondering what the difference is between a task and an operation? You can check both with checkAccess(), and they can both contain bizrules. Are they the same thing, just referred to as different names?

Exactly. If you look Yii’s rbac code you can find that operations, tasks and even roles are the same thing. And different names are used to logically separate access rules.

Here is discussion and qiang explains this in the last post.

For anyone still following this or stumbling on it, I found this really useful:

http://www.yiiframework.com/forum/index.php?/topic/5577-solved-authmanager-question/page__pid__28665__st__0&

This is why. You should assign each user the ‘updateOwnPost’ task. Yii can only check permissions that are assigned to a given user. You then assign admins the ‘updatePost’ permission. In this way you can simply use one line to check for the proper permissions:




if (Yii::app()->user->checkAccess('updatePost', array('post'=>$post)))

{

  // update

}



Why does this work? Because the permissions system cascades. Because ‘updatePost’ is a child of ‘updateOwnPost’ when a user can satisfy the bizRule condition of the ‘updateOwnPost’ task they will have access to the ‘updatePost’ operation. If they do not satisfy the bizRule, the child permissions are not checked and they do not have access to ‘updatePost’. Does that make sense?

The description I gave above should help you on your way. Make ‘updatePost’ a child of ‘updateOwnPost’. That way when you do a checkAccess(‘updatePost’,array(‘userId’=>$post->authorId)) the author will satisfy the bizRule for ‘updateOwnPost’ and then cascade and have access to the ‘updatePost’ permissions. Also, if someone - like an admin - is given direct access to the ‘updatePost’ operation then the permission will validate for them as well.

However, if someone does not have direct access to the ‘updatePost’ operation and they cannot satisfy the bizRule for ‘updateOwnPost’ the checkAccess will fail and they will be denied access.

Does that help?

Yes, they are used for logical separation and they are essentially the same (seeing as they are stored in the same table), but there are some limitations to them. This is where the logical separation becomes application enforced and not just by the name.

The permissions hierarchy goes as follows (as specified by CAuthItem): Operations, Tasks, Roles. Each AuthItem can only have children of the same type or lower. The valid children are outlined below:

Roles Children: Roles, Tasks, Operations

Tasks Children: Tasks, Operations

Operations Children: Operations

This check happens in CAuthManager::checkItemChildType() (line 150 in 1.0.10).

This creates a logical hierarchy for creating a permissions system and helps to decrease the likelihood of permission loops in the system.

Outstanding, that really has helped me out.

The part that had me confused was that a bizRule can parameters can be passed to any authItem (even if it doesn’t have a bizRule), and will be passed through the hierarchy for each authItem to be checked.

I greatly appreciate you taking the time to help us out on this - you’ve brought some much needed clarity!

Thanks everyone for trying to clarify these topics - very helpful reading. (Someone ought to migrate some of the explanations from these posts to the documentation.)

On a related issue, when it comes to implementing business rules, what’s a good strategy for this?

Say I have a PostController with create, update and delete actions - to establish the business rule for edit and delete, you need to check if the Post record in question belongs to the current user. How do you do that?

What I mean is, when the business rule is evaluated, the Post has not been loaded yet - it doesn’t get loaded until the action is executed and loads it, so how do you establish the “context” of a request? (e.g. the Post in question)

It may just be me, but I try not to use bizRules with my permissions. It makes my release process much more difficult when I have to push database changes, and bizRules are stored in the database. I also don’t like bizRules because they are evaluated using eval(). They’re a useful concept, and have their place, but I prefer to implement my bizRules in my code rather than in the permissions system. That’s just me, though.

As for using bizRule’s in that context. It would only work after you loaded the model. So you would need to do a check inside your action to see if they have valid permissions rather than using the accessControl filter. I believe, from the accessControl perspective, the permissions can’t have states (but I may be wrong). I’ll dig into it a little more later.

Are you currently trying to use the accessControl filter for your permission checking, or are you doing it manually inside your action?