5

Learning Yii Framework 2.0 I have tried to use Role Bases Access Control from the documentation of Yii 2.0. But the guide documentation is too short to me that I cannot complete this learning. I have added the following code to my config file.

'components' => [
    'authManager' => [
        'class' => 'yii\rbac\DbManager',
    ],
],

I have create the database tables with the following sql script.

drop table [auth_assignment];
drop table [auth_item_child];
drop table [auth_item];
drop table [auth_rule];

create table [auth_rule]
(
    [name]  varchar(64) not null,
    [data]  text,
    [created_at]           integer,
    [updated_at]           integer,
    primary key ([name])
);

create table [auth_item]
(
   [name]                 varchar(64) not null,
   [type]                 integer not null,
   [description]          text,
   [rule_name]            varchar(64),
   [data]                 text,
   [created_at]           integer,
   [updated_at]           integer,
   primary key ([name]),
   foreign key ([rule_name]) references [auth_rule] ([name]) on delete set null on update    cascade
);

create index [idx-auth_item-type] on [auth_item] ([type]);

create table [auth_item_child]
(
   [parent]               varchar(64) not null,
   [child]                varchar(64) not null,
   primary key ([parent],[child]),
   foreign key ([parent]) references [auth_item] ([name]) on delete cascade on update cascade,
   foreign key ([child]) references [auth_item] ([name]) on delete cascade on update cascade
);

create table [auth_assignment]
(
   [item_name]            varchar(64) not null,
   [user_id]              varchar(64) not null,
   [created_at]           integer,
   primary key ([item_name], [user_id]),
   foreign key ([item_name]) references [auth_item] ([name]) on delete cascade on update cascade
);

I have built the authentication data with the following.

class RbacController extends Controller
{
    public function actionInit()
    {
        $auth = Yii::$app->authManager;

        // add "createPost" permission
        $createPost = $auth->createPermission('createPost');
        $createPost->description = 'Create a post';
        $auth->add($createPost);

        // add "updatePost" permission
        $updatePost = $auth->createPermission('updatePost');
        $updatePost->description = 'Update post';
        $auth->add($updatePost);

        // add "author" role and give this role the "createPost" permission
        $author = $auth->createRole('author');
        $auth->add($author);
        $auth->addChild($author, $createPost);

        // add "admin" role and give this role the "updatePost" permission
        // as well as the permissions of the "author" role
        $admin = $auth->createRole('admin');
        $auth->add($admin);
        $auth->addChild($admin, $updatePost);
        $auth->addChild($admin, $author);

        // Assign roles to users. 1 and 2 are IDs returned by IdentityInterface::getId()
        // usually implemented in your User model.
        $auth->assign($author, 2);
        $auth->assign($admin, 1);
    }
}

When access to this actionInit() method via this controller, the above database tables have been filled with the data based on the above code. Furthermore, in my user's table I have two users, admin user has the id number 1 and author user has the id number 2. I use the following code to create a user.

public function create()
{
    if ($this->validate()) {
        $user = new User();
        $user->username = $this->username;
        $user->email = $this->email;
        $user->setPassword($this->password);
        $user->generateAuthKey();
        $user->save(false);

        // the following three lines were added:
        $auth = Yii::$app->authManager;
        $authorRole = $auth->getRole('author');
        $auth->assign($authorRole, $user->getId());

        return $user;
    }

    return null;
}

With the above code all new inserted users will be author. With the if-statements below I can grant or deny access.

if (\Yii::$app->user->can('createPost')) {
    // create post
}

if (\Yii::$app->user->can('updatePost')) {
    // update post
}

So far so good. Everything works fine. The scenario of the above code is that normal author can create post, but cannot update post. Admin can update post and can do everything author can do. Now I want normal author to be able to update his/her own post. I don't know how to go further from here. I have followed the Yii Guide Documentation/Secury/Authorization paragraph Role based access control (RBAC). I have never used Yii 1. That's why I could not figure out with such short explanation of Yii 2.0 documentation RBAC.

O Connor
  • 3,676
  • 12
  • 38
  • 75
  • what is hard to understand? – Stefano Mtangoo Oct 30 '14 at 14:51
  • Following the documentation http://www.yiiframework.com/doc-2.0/guide-security-authorization.html I could not get it work with updateOwnPost and don't know what would be the value of the variable $post under the section Access Check if you visit the link. Really appreciate that if you can help! – O Connor Oct 30 '14 at 15:51
  • Well that is the $post model. can you edit your question to ask something you want in specific terms, explaining what you have done so that specific solution will be given? – Stefano Mtangoo Oct 31 '14 at 06:43
  • I just edited my question with what I have done so far and where I stuck. – O Connor Oct 31 '14 at 19:18

2 Answers2

5

You need access rule and the docs are clear so create it like

namespace app\rbac;

use yii\rbac\Rule;

/**
 * Checks if authorID matches user passed via params
 */
class AuthorRule extends Rule
{
    public $name = 'isAuthor';

    /**
     * @param string|integer $user the user ID.
     * @param Item $item the role or permission that this rule is associated with
     * @param array $params parameters passed to ManagerInterface::checkAccess().
     * @return boolean a value indicating whether the rule permits the role or permission it is associated with.
     */
    public function execute($user, $item, $params)
    {
        return isset($params['post']) ? $params['post']->createdBy == $user : false;
    }
}

then, add it in your RBAC role

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

// add the rule
$rule = new \app\rbac\AuthorRule;
$auth->add($rule);

// add the "updateOwnPost" permission and associate the rule with it.
$updateOwnPost = $auth->createPermission('updateOwnPost');
$updateOwnPost->description = 'Update own post';
$updateOwnPost->ruleName = $rule->name;
$auth->add($updateOwnPost);

// "updateOwnPost" will be used from "updatePost"
$auth->addChild($updateOwnPost, $updatePost);

// allow "author" to update their own posts
$auth->addChild($author, $updateOwnPost);

Finally asign role to user in signup

$auth = Yii::$app->authManager;
$authorRole = $auth->getRole('author');
$auth->assign($authorRole, $userid_here);

to check if user have ability to edit use code below where $post is Model for posts

if (\Yii::$app->user->can('updatePost', ['post' => $post])) {
    // update post
}

All these are taken from guide. Let me know if you have any problem

Stefano Mtangoo
  • 4,986
  • 4
  • 35
  • 83
  • The code under your text: 'add it in your RBAC role', put it in my RbacController in the actionInit() method? – O Connor Nov 01 '14 at 11:08
  • Can I use the above check for post of any model? I still don't understand how it knows which model this check is about. – O Connor Jul 14 '15 at 14:55
  • You don't want to check in models. Checks should be done in Controller actions, not in models – Stefano Mtangoo Jul 14 '15 at 15:27
  • If its controller-wide use beforeAction/init methods and if its for single action, check at the top before you execute any of protected instrcutions – Stefano Mtangoo Jul 14 '15 at 15:30
  • Does the name 'post' matter here (key inside $param[]), or it will work for any other controller besides postController? I assume the associated array for updatePost must match the name given to key, but it will work for any model other than Post. Am I wrong? – jarvan Jan 23 '16 at 00:04
0
if (\Yii::$app->user->can('updatePost', ['post' => $post])) {
    // update post }

in my script, i change $post with $model

if (\Yii::$app->user->can('updatePost', ['post' => $model])) {
    // update post }

then it works.