S
S
Sergey Doniy2016-10-20 14:14:37
Yii
Sergey Doniy, 2016-10-20 14:14:37

How to organize RBAC in Yii2 to control, in addition to accesses, access by language to the content?

The point is this. There is a multilingual site.
It is necessary to implement differentiation of access rights to content for different countries.
For example, when a manager enters Spanish, only Spanish content can be edited, but there must also be the ability to control the languages ​​available to the manager. Those. it is necessary to provide for the possibility for the same manager to add another language or several (if necessary) that he can edit. To the specific user, but not to group.
My vision for the solution is to extend rbac
I create another table rbac_language which contains the list of available languages
​​and rbac_language_assignment where the particular language is stored.
But here is the question. In this variant, it is very inconvenient to use permission to differentiate rights, since permission can only be tied to the user's group, but not to the user himself.
And here the problem of such an architecture arises - the user has many groups.
On the other hand, would it be better to create a new user group each time for a new set of available languages, or is there a better solution?
I ask in order to get advice or criticism of the design of access control and suggestions for improvement.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
A
Andrew, 2016-10-20
@mhthnz

In this variant, it is very inconvenient to use permission to differentiate rights, since permission can only be tied to the user's group, but not to the user himself.

Weird, I got it working:
$authManager = Yii::$app->authManager;
$userID = 1;
$permission = $authManager->getPermission('language_ru');
$authManager->assign($permission, $userID);

Thus, the user can be given any number of rights.

S
Sergey Doniy, 2016-10-22
@doniys_a

Yes, this is true, but there is one "but", I'm trying to automate the rules check, I took the GlobalAccessBehavior from the wonderful project https://github.com/trntv/yii2-starter-kit , which sets global access rights to actions.
This is what it looks like.
There is a console command that runs through all the admin modules, collects controllers and all actions, also runs through the controllers inside the admin and does the same.
the rules are compiled as follows
backend___
Further, all this is tied to the role.
Everything works fine, but when a situation arises when a specific user (and there may be a dozen of them) needs to disable a specific controller or even an action, then in the case of using permission, AccessControl does not understand it, since access can be set for the role.
In the case of using permission, such a check should be carried out in the controller's beforeAction method

public function beforeAction() {
        $permission = 'backend_' . Yii::$app->controller->module->id . '_' . Yii::$app->controller->id . '_' . Yii::$app->controller->action->id;

        if (!Yii::$app->user->can($permission)) {
            throw new yii\web\ForbiddenHttpException;
        }
    }

In this case, it must be implemented in the base controller.
There is still an option to make it understand AccessRule permission and not just roles by redefining it a little
<?php

namespace common\behaviors;
use yii\base\Action;
use yii\filters\AccessRule;
use yii\helpers\Inflector;


/**
 * @class   AccessRuleCustom
 *
 * @package common\behaviors
 * @author  Sergey Doniy <[email protected]>
 */
class AccessRuleCustom extends AccessRule
{
    public $checkPermissions = true;
    public $prefix = 'backend_';

    public function allows($action, $user, $request)
    {
        return parent::allows($action, $user, $request) || $this->hasPermission($action);
    }

    protected function hasPermission(Action $action) {
        if (!$this->checkPermissions)
            return false;

        $permission = $this->prefix . $action->controller->module->id . '_' . $action->controller->id . '_' . Inflector::id2camel($action->id);

        return \Yii::$app->user->can($permission);

    }

}

In the second option, everything works, in any case, permission passes, and everything is OK. Ultimately, I settled on this option - the user has only one role, to which default permissions are attached, and separate ones are attached directly to the user.
In order not to hardcode with languages ​​and their roles, I separately created two tables
auth_assignment_language_item
which stores a list of languages ​​- i.e. in fact, permission for the language
and the
auth_assignment_language table,
which in turn stores the binding of an individual user to the language
and the auth_assignment_language_child table.
To bind the role to the language
, the RBAC functionality has been expanded to manage languages ​​(since I store rbac in the database, the DbManager is expanded)
I put it into separate tables so that the data logic is transparent, which made it possible to quickly throw in a gui-interface for managing rights to a specific language.
This I described my implementation of the topic raised.
But I'm still not sure that there are no better approaches for implementation.
It will be interesting to read who solved such problems and by what methods - I’ll clarify right away - the system is not written for one project, it’s the company’s internal CMS and hot-fixes and kicking the system for a specific case will complicate the development of the following projects, so it’s necessary to the maximum (as far as time allows ) make it flexible and extensible for most of the tasks that developers face.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question