dql7588
2013-01-09 16:04 阅读 55
已采纳

Yii的accessRules误解

I'm trying to build a site using Yii, and found myself in a situation which requires some clarifications on access control metaphor in Yii. Specifically, one can override accessRules method in CController's descendant.

Prerequisite:

Here is a code generated by default by Gii, much similar to the documentation:

public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'index' and 'view' actions
            'actions'=>array('index','view'),
            'users'=>array('*'),
        ),
        array('allow', // allow authenticated user to perform 'create' and 'update' actions
            'actions'=>array('create','update'),
            'users'=>array('@'),
        ),
        array('allow', // allow admin user to perform 'admin' and 'delete' actions
            'actions'=>array('admin','delete'),
            'roles'=> array('admin'),
        ),
        array('deny',  // deny all users
            'users'=>array('*'),
        ),
    );
}

The Question:

What is not clear here to me, is -- why we need to define access rules for many different users and groups, and different actions instead of checking rights of only one current user and action? -- here in italics my main question is highlighted, just in case it may not so clear for readers (answer authors).

This code is executed in a context of specific request, for which a specific controller, a specific action, and a specific user are already known. If, for example, the user is a guest, I don't see any reason to define rules for "admin" role or authenticated users.

Reasoning:

After some elaboration I came up to the following implementation:

public function accessRules()
{
    return array(
      array(Yii::app()->user->hasRights()?'allow':'deny'),
      );
}

where hasRights is a simple custom method added into a CWebUser descendant:

class WebUser extends CWebUser
{
  private $ACL = // ACL example
    array('user' => // controller id
      array('index' => User::AC_MODERATOR, // action ids
            'view' => User::AC_MODERATOR,
            'create' => User::AC_MODERATOR,
            'update' => User::AC_ADMIN,
            'delete' => User::AC_ADMIN,
            'admin' => User::AC_ADMIN),
           // ...
      );

  public function hasRights()
  {
    return (Yii::app()->user->getState('accessRights') >=
      $this->ACL[Yii::app()->controller->id][Yii::app()->controller->action->id]);
  }
}

As you may see, hasRights uses current user "rights" (read from DB as usual), current controller and action to calculate single boolean true or false value as a decision about access.

What is wrong with this approach? Why Yii does not use something simple like that by default?

The Gii-generated accessRules above do not only look excessive, but also imply that access rules are scattered among many controllers. In my approach a single and compact ACL is used.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享

2条回答 默认 最新

  • 已采纳
    duande3134 duande3134 2013-08-02 22:32

    Here's where I think you're confused.

    First of all, Yii is in fact only looking at the current user and the current action. Just like how you got the current permissions for the controller by grabbing a nested array, indexed by the controller ID, Yii is creating an instance of your controller and only looking at those access rules. Also, just like you're only looking at the permissions of a particular action, Yii is only looking at the rules pertaining to the current action. It accesses those permissions just as easily as you return the value of the nested array.

    As far as the user goes, it's also only looking at the current user; the current user name, the current user role, etc. The difference is, there are multiple attributes Yii will allow within a rule, in contrast to the single permission value associated with the user.

    It seems what you don't like about this approach is you think permissions should be handled all in one place. This could be simpler in some cases, but more difficult in others. What happens when you have multiple controllers and multiple actions in each controller? You now have a very large array to manage, that references data from multiple different contexts. In the way Yii does it, the current controller has control over how it's data structure can be accessed. This lines up with Yii's MVC structure, and the concept of encapsulation.

    Your solution is more elegant in some respects, but it hinges on the idea that permissions will only ever need to be structured in a cascading one directional structure. What I mean is your permissions are like one long hallway, with doors separating one area from another. If the user doesn't have the key to one door, they shouldn't be able to access the next, etc. But what happens if, in your example, you need a user to both view content, and update it without being able to create new content? This is a much more complicated scenario which will need to be handled using roles. So then you'd have to process rules within your array, much like Yii does. Except instead of Yii's object oriented approach, you've got everything embedded in one long array.

    So perhaps your solution will work in some cases, but I'm sure you can see why Yii chose its approach since it will work, by default, for far more situations.

    点赞 评论 复制链接分享
  • douzhankui0758 douzhankui0758 2013-01-09 21:18

    Yii implements a hierarchical RBAC scheme via its CWebApplication ->authManager application component, which actually similar to what you did,

    Take a look at this

    EDIT

    "let us suppose that we use RBAC and add some rules into the accessRules method, such as with admin role in the example above." 
    

    you don't add rules into accessRules method when you use RBAC

    what you do is , you define authorization hierarchy which involves three steps 1)define authorization itmes which includes roles and operations, this would be stored in DB,in authitem table

    Eg. Operations

    siteAddProduct->in this case 'site' is the controller name and addProduct is the action in 'site' controller
    sitedeleteProduct ...etc
    

    Roles

    admin,editor...etc
    

    2)Estabish relationship between authorization items(between role and operations) this would be stored in authitemchild table

    eg.

    admin -> siteAddProduct
    editor -> sitedeleteProduct
    admin->editor
    

    3)assign roles to application users this would be stored in authassignment table

    for example userid 1 ->admin

    now in your base controller( assuming that you have a controller class from which all other controllers in your app extneds from ) you override beforeAction() method and check the logged in user(current user) permission using

    Yii::app()->user->checkAccess($operation);
    

    $operation is the requested controller + action

    you can get the controller name using $this->id and action name using $this->getAction()->id

    点赞 评论 复制链接分享