András
András

Reputation: 175

Yii2 RBAC failure - it either blocks everything or guest is allowed everything as well

I have been struggling with Yii2 RBAC for a week now gathering info from all sorts of sites but I wasn't able to get it to work properly. My current situation consists of two ways: the RBAC blocks everything or the guest can access everything.

My main problem is getting RBAC to allow the users to access the pages allocated to them. The weird thing is that the Yii::$app->user->can("/" . Yii::$app->controller->id . "/" . Yii::$app->controller->action->id); function returns true properly for authenticated users, however if I embed it into the access control rules in the controller, it fails to work (gives 403).

return [
    'access' => [
        'class' => AccessControl::className(),
        'rules' => [
            [
                'actions' => ['index', 'about', 'error', 'test'],
                'allow' => true,
            ],
            [
                'actions' => ['login'],
                'allow' => true,
                'roles' => ['?'],
            ],
            [
                'actions' => ['logout'],
                'allow' => true,
                'roles' => ['@'],
            ],
            [
                'allow' => Yii::$app->user->can("/" . Yii::$app->controller->id . "/" . Yii::$app->controller->action->id),
                'actions' => [Yii::$app->controller->action->id],
            ]
        ],
    ],
    'verbs' => [
        'class' => VerbFilter::className(),
        'actions' => [
            'logout' => ['post'],
        ],
    ],
];

I have found a way around this by instead of hardcoding that condition into the rules array, I am inserting a short snippet before the return command which adds the current action as an allowed one through array_merge if the user can indeed access that location. The authenticated users rights are now applied correctly, however, this always returns true for the guests (they have rights to everything basically).

$rules = [];
if (!Yii::$app->user->isGuest) {
    if (Yii::$app->user->can("/" . Yii::$app->controller->id . "/" . Yii::$app->controller->action->id)) {
        $rules[] = [
            'allow' => true,
            'actions' => [Yii::$app->controller->action->id],
        ];
    }
}
return [
    'access' => [
        'class' => AccessControl::className(),
        'rules' => array_merge($rules, [
            [
                'actions' => ['index', 'about', 'error', 'test'],
                'allow' => true,
            ],
            [
                'actions' => ['login'],
                'allow' => true,
                'roles' => ['?'],
            ],
            [
                'actions' => ['logout'],
                'allow' => true,
                'roles' => ['@'],
            ],
        ]),
    ],
    'verbs' => [
        'class' => VerbFilter::className(),
        'actions' => [
            'logout' => ['post'],
        ],
    ],
];

I've tested the guest rights like this:

var_dump(Yii::$app->user->can("/site/dashboard"));
die;

It returns bool(true) for any action I give it. If I'm logged in, it functions as it is intended in the auth database tables.

Any help is appreciated, either to spot the error in my solution or to point out how to use RBAC properly with guest and with behaviors. Or possibly to help me understand why would it return true when there is no authenticated user.

Thank you.

Upvotes: 0

Views: 257

Answers (1)

Serghei Leonenco
Serghei Leonenco

Reputation: 3517

The problem in your code is that you trying to pass in User function can() wrong parameter. In documentation it is state that:

can() public method
Checks if the user can perform the operation as specified by the given permission.

But you trying to pass an url.

You have to assign permission in RBAC rules something like that: admin has permission accessAdminDash console/controllers/RbacController

$auth = Yii::$app->authManager;
$dashboard = $auth->createPermission('accessAdminDash');
$dashboard->description = 'Admin manager panel';
$auth->add($dashboard);
//It can be more permissions
$admin = $auth->createRole('admin');
$admin->description = 'Administrator';
$admin->ruleName = $rule->name;
$auth->add($admin);
//Can be more Roles
//assign Permission to a role
$auth->addChild($admin, $dashboard);

And after that you can check your permission for specific user like this:

Yii::$app->user->can('accessAdminDash'); //here is your permission

and at the time of your controller action, you can check or create a private method which will check current user:

if(Yii::$app->user->can('accessAdminDash'))
{
   //Ok this user CAN access
}
throw new ForbiddenHttpException();

or in AccessControl check role like this:

public function behaviors()
    {
        return [
            'access' => [
                'class' => AccessControl::className(),
                'rules' => [
                    [
                      'actions' => ['admin-page'],
                      'allow' => true,
                      'roles' => ['admin'], //Here is your role
                    ]
                ],
            ],
        ];
    }

Hope this will give you a right direction.

Upvotes: 0

Related Questions