Configuring Yii2 RBAC

  • Tutorial

Task


Configure RBAC usage in Yii2.

Conditions


List of possible roles:
  • guest - not authorized user;
  • BRAND - an authorized user, inherits the permissions of the guest role and has its own unique permissions;
  • TALENT - an authorized user, inherits the permissions of the guest role and has its own unique permissions;
  • admin - an authorized user, inherits the permissions of the guest , BRAND and TALENT roles and has its own unique permissions.
  • The role is determined by the group field in the UserExt model ;
  • Roles have a nested structure - one role can inherit the permissions of another;
  • Used by yii \ rbac \ PhpManager ;
  • Do not use role assignment to a user by his ID - instead use several predefined roles ( defaultRoles );
  • The role-permissions config will be generated by the yii console command ;
  • Extended Rules for permissions will be used .


Preset


app/config/console.php
'components' => [
    // ...
    'authManager' => [
        'class' => 'yii\rbac\PhpManager',
    ],
    // ...
],


app/config/web.php
'components' => [
    // ...
    'authManager' => [
        'class' => 'yii\rbac\PhpManager',
        'defaultRoles' => ['admin', 'BRAND', 'TALENT'], // Здесь нет роли "guest", т.к. эта роль виртуальная и не присутствует в модели UserExt
    ],
    // ...
],


Create a directory @app/rbac- it will contain permissions and rules.

Create Permissions


In the directory, @app/commandscreate a controller that will generate an array of permissions:
app/commands/RbacController.php
authManager;
        // Create roles
        $guest  = $authManager->createRole('guest');
        $brand  = $authManager->createRole('BRAND');
        $talent = $authManager->createRole('TALENT');
        $admin  = $authManager->createRole('admin');
        // Create simple, based on action{$NAME} permissions
        $login  = $authManager->createPermission('login');
        $logout = $authManager->createPermission('logout');
        $error  = $authManager->createPermission('error');
        $signUp = $authManager->createPermission('sign-up');
        $index  = $authManager->createPermission('index');
        $view   = $authManager->createPermission('view');
        $update = $authManager->createPermission('update');
        $delete = $authManager->createPermission('delete');
        // Add permissions in Yii::$app->authManager
        $authManager->add($login);
        $authManager->add($logout);
        $authManager->add($error);
        $authManager->add($signUp);
        $authManager->add($index);
        $authManager->add($view);
        $authManager->add($update);
        $authManager->add($delete);
        // Add rule, based on UserExt->group === $user->group
        $userGroupRule = new UserGroupRule();
        $authManager->add($userGroupRule);
        // Add rule "UserGroupRule" in roles
        $guest->ruleName  = $userGroupRule->name;
        $brand->ruleName  = $userGroupRule->name;
        $talent->ruleName = $userGroupRule->name;
        $admin->ruleName  = $userGroupRule->name;
        // Add roles in Yii::$app->authManager
        $authManager->add($guest);
        $authManager->add($brand);
        $authManager->add($talent);
        $authManager->add($admin);
        // Add permission-per-role in Yii::$app->authManager
        // Guest
        $authManager->addChild($guest, $login);
        $authManager->addChild($guest, $logout);
        $authManager->addChild($guest, $error);
        $authManager->addChild($guest, $signUp);
        $authManager->addChild($guest, $index);
        $authManager->addChild($guest, $view);
        // BRAND
        $authManager->addChild($brand, $update);
        $authManager->addChild($brand, $guest);
        // TALENT
        $authManager->addChild($talent, $update);
        $authManager->addChild($talent, $guest);
        // Admin
        $authManager->addChild($admin, $delete);
        $authManager->addChild($admin, $talent);
        $authManager->addChild($admin, $brand);
    }
}


The UserGroupRule class is responsible for checking the equality of the role of the current user, the role registered in the permissions array. This avoids the problem of assigning a role to a user by his ID.
app/rbac/UserGroupRule.php
user->isGuest) {
            $group = \Yii::$app->user->identity->group;
            if ($item->name === 'admin') {
                return $group == 'admin';
            } elseif ($item->name === 'BRAND') {
                return $group == 'admin' || $group == 'BRAND';
            } elseif ($item->name === 'TALENT') {
                return $group == 'admin' || $group == 'TALENT';
            }
        }
        return true;
    }
}


Now in the controller you can remove the access rule from the behaviors method :
app/controllers/SiteController.php
public function behaviors()
{
    return [
        // ...
        'access' => [
            'class' => AccessControl::className(),
            'only' => ['logout'],
            'rules' => [
                [
                    'actions' => ['logout'],
                    'allow' => true,
                    'roles' => ['@'],
                ],
            ],
        ],
        // ...
    ];
}


Access check


Method 1 - in the controller method:
app/controllers/SiteController.php
public function actionAbout()
{
    if (!\Yii::$app->user->can('about')) {
        throw new ForbiddenHttpException('Access denied');
    }
    return $this->render('about');
}


Method 2 - write beforeAction so as not to write "if !\Yii::$app->user->can"in each method:
app/controllers/SiteController.php
public function beforeAction($action)
{
    if (parent::beforeAction($action)) {
        if (!\Yii::$app->user->can($action->id)) {
            throw new ForbiddenHttpException('Access denied');
        }
        return true;
    } else {
        return false;
    }
}


Generating Permission Files


To generate a file with an array of permissions, you need to run the command in the project root:
Attention!
Before executing this command, you need to delete the files @app/rbac/items.phpand @app/rbac/rules.phpto avoid merge conflicts

./yii rbac/init


@app/rbacTwo files should appear in the directory :
app/rbac/items.php
 [
        'type' => 2,
    ],
    'logout' => [
        'type' => 2,
    ],
    'error' => [
        'type' => 2,
    ],
    'sign-up' => [
        'type' => 2,
    ],
    'index' => [
        'type' => 2,
    ],
    'view' => [
        'type' => 2,
    ],
    'update' => [
        'type' => 2,
    ],
    'delete' => [
        'type' => 2,
    ],
    'guest' => [
        'type' => 1,
        'ruleName' => 'userGroup',
        'children' => [
            'login',
            'logout',
            'error',
            'sign-up',
            'index',
            'view',
        ],
    ],
    'BRAND' => [
        'type' => 1,
        'ruleName' => 'userGroup',
        'children' => [
            'update',
            'guest',
        ],
    ],
    'TALENT' => [
        'type' => 1,
        'ruleName' => 'userGroup',
        'children' => [
            'update',
            'guest',
        ],
    ],
    'admin' => [
        'type' => 1,
        'children' => [
            'delete',
            'TALENT',
            'BRAND',
        ],
    ],
];


app/rbac/rules.php
 'O:22:"app\\rbac\\UserGroupRule":3:{s:4:"name";s:9:"userGroup";s:9:"createdAt";N;s:9:"updatedAt";N;}',
];


Extended Rule for Permissions


For example, you need to prevent users from editing ( update ) not their profile. To do this, you need an extended rule:
app/rbac/UserProfileOwnerRule.php
user->identity->group == 'admin') {
            return true;
        }
        return isset($params['profileId']) ? \Yii::$app->user->id == $params['profileId'] : false;
    }
}


Add to file @app/rbac/RbacController.php:
app/rbac/RbacController.php
use \app\rbac\UserProfileOwnerRule;
// add the rule
$userProfileOwnerRule = new UserProfileOwnerRule();
$authManager->add($userProfileOwnerRule);
$updateOwnProfile = $authManager->createPermission('updateOwnProfile');
$updateOwnProfile->ruleName = $userProfileOwnerRule->name;
$authManager->add($updateOwnProfile);
$authManager->addChild($brand, $updateOwnProfile);
$authManager->addChild($talent, $updateOwnProfile);


Access check in the controller method:
app/controllers/UsersController.php
public function actionUpdate($id)
{
    if (!\Yii::$app->user->can('updateOwnProfile', ['profileId' => \Yii::$app->user->id])) {
        throw new ForbiddenHttpException('Access denied');
    }
    // ...
} 

Also popular now: