Yii2 RBAC Несколько назначений для каждого пользователя на основе групп

Технически мое приложение имеет две области: глобальную область (обратная связь, профиль пользователя, настройки пользователя и т. Д.) И область группы (контакты, проекты, профиль группы, настройки группы и т. Д.).

Я использую RBAC DBManager для глобальной области, и он работает просто отлично, но у меня возникают проблемы с реализацией механизма авторизации для области группы.

Причина в том, что группы могут совместно использоваться пользователями, и пользователь может иметь несколько назначений в таблице group_access (id, group_id, user_id, item_name), поскольку они могут быть членами нескольких групп, и они могут иметь разные уровни разрешений для эти группы.

Вот мои настройки аутентификации:

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

    // group permissions
    $manageGroupUsers = $auth->createPermission('manage_group_users');
    $manageGroupUsers->description = 'Manage Group Users';
    $auth->add($manageGroupUsers);

    $manageGroupSettings = $auth->createPermission('manage_group_settings');
    $manageGroupSettings->description = 'Manage Group Settings';
    $auth->add($manageGroupSettings);

    // app permissions
    $manageAppUsers = $auth->createPermission('manage_app_users');
    $manageAppUsers->description = 'Manage App Users';
    $auth->add($manageAppUsers);

    $manageAppGroups = $auth->createPermission('manage_app_groups');
    $manageAppGroups->description = 'Manage App Groups';
    $auth->add($manageAppGroups);

    $manageAppSettings = $auth->createPermission('manage_app_settings');
    $manageAppSettings->description = 'Manage App Settings';
    $auth->add($manageAppSettings);

    $manageAppFeedback = $auth->createPermission('manage_app_feedback');
    $manageAppFeedback->description = 'Manage App Feedback';
    $auth->add($manageAppFeedback);

    // group roles
    // -- create role
    $groupUser = $auth->createRole('group_user');
    $groupUser->description = 'Group Users';
    $auth->add($groupUser);

    // -- create role
    $groupAdmin = $auth->createRole('group_admin');
    $groupAdmin->description = 'Group Administrators';
    $auth->add($groupAdmin);
    // add permissions
    $auth->addChild($groupAdmin, $manageGroupUsers);
    $auth->addChild($groupAdmin, $manageGroupSettings);
    // inherit permissions
    $auth->addChild($groupAdmin, $groupUser);

    // -- create role
    $groupCreator = $auth->createRole('group_creator');
    $groupCreator->description = 'Group Creators';
    $auth->add($groupCreator);
    // inherit permissions
    $auth->addChild($groupCreator, $groupAdmin);

    // app roles
    // -- create role
    $appUser = $auth->createRole('app_user');
    $appUser->description = 'App Users';
    $auth->add($appUser);

    // -- create role
    $appSupport = $auth->createRole('app_support');
    $appSupport->description = 'Support Users';
    $auth->add($appSupport);
    // add permissions
    $auth->addChild($appSupport, $manageAppFeedback);

    // -- create role
    $appAdmin = $auth->createRole('app_admin');
    $appAdmin->description = 'App Administrators';
    $auth->add($appAdmin);
    // add permissions
    $auth->addChild($appAdmin, $manageAppUsers);
    $auth->addChild($appAdmin, $manageAppGroups);
    $auth->addChild($appAdmin, $manageAppSettings);
    // inherit permissions
    $auth->addChild($appAdmin, $appUser);
    $auth->addChild($appAdmin, $appSupport);

    // -- create role
    $appCreator = $auth->createRole('app_creator');
    $appCreator->description = 'App Creators';
    $auth->add($appCreator);
    // inherit permissions
    $auth->addChild($appCreator, $appAdmin);

Моя таблица group_access имеет ту же схему, что и таблица auth_assignment, за исключением того, что в ней есть столбец group_id, а столбец user_id НЕ уникален.

У пользователя будет только одно назначение, касающееся глобальной области, но он может иметь много разных назначений в области группы, поскольку у него могут быть привилегии администратора в группе a, но только привилегии пользователя в группе b.

Моя БД настроена так:

Пользователи (status_id, username, auth_key, password_hash, email и т. Д.)

Группы (status_id, имя, описание и т. Д.)

Group_Access (group_id, user_id, item_name) Каждый пользователь получает одно назначение для каждой группы, к которой у него есть доступ.

sample_group_access_records [['id' => 1, 'user_id' => 35, 'group_id' => 17, 'item_name' => 'group_admin'], ['id' => 2, 'user_id' => 35, ' group_id '=> 356,' item_name '=>' group_user '], [' id '=> 3,' user_id '=> 35,' group_id '=> 211,' item_name '=>' group_creator '],];

Функция checkAccess может определять идентификатор пользователя, и я даже могу использовать более короткую версию «can», которая отлично подходит для вошедшего в систему пользователя, но мне нужно проверить доступ на основе пользовательской опции, как показано ниже:

Option::getOption('user', 'active_group_id')

Это пользовательская функция, которая извлекает идентификатор активной группы из таблицы параметров пользователя. Если пользователь переключает группы, это будет изменено. Моя модель опций имеет три типа «приложение», «пользователь», «группа».

Было бы хорошо, если бы я мог выяснить, что функция, которая работает так же, как и нативная checkAccess, но была вызвана checkGroupAccess, автоматически получала active_group_id, извлекала пользовательские назначения из таблицы group_access и выполняла проверку разрешений.

Я надеюсь это имеет смысл.

Спасибо за ваше время.

Майк

** ОБНОВЛЕНО **

Итак, у меня есть решение, которое использует пользовательские функции checkAccess для проверки правильности разрешений для группы или глобальных областей.

У меня есть две таблицы (user_access, group_access), схема которых аналогична таблице по умолчанию {{auth_assignment}}, которой я сейчас не пользуюсь. Я использую таблицы {{auth_item}}, {{auth_item_child}} и {{auth_rule}}.

У меня есть две модели, по одной для каждой из таблиц доступа GroupAccess => group_access и UserAccess => user_access.

У меня также есть модель для функций доступа, и я сопоставил ее с конфигурацией компонентов.

Вот моя модель доступа:

<?php

namespace app\models;

use Yii;

class Access
{

public function canUser($type, $permissionName, $params = [])
{

    switch ($type) {

        case 'group':

        $userID = Yii::$app->user->identity->id;
        $groupID = Yii::$app->options->getOption('user', 'active_group_id');

        $queryAll = GroupAccess::find()
        ->where('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID])
        ->asArray()
        ->all();

        $assignments = [];
        foreach ($queryAll as $queryItem) {
            $assignments[$queryItem['item_name']] = [
            'userId' => $queryItem['user_id'],
            'roleName' => $queryItem['item_name'],
            'createdAt' => $queryItem['created_date'],
            ];
        }

        $result = self::checkAccess($userID, $permissionName, $assignments, $params);

        return $result;

        break;

        case 'user':

        $userID = Yii::$app->user->identity->id;

        $queryAll = UserAccess::find()
        ->where(['user_id' => $userID])
        ->asArray()
        ->all();

        $assignments = [];
        foreach ($queryAll as $queryItem) {
            $assignments[$queryItem['item_name']] = [
            'userId' => $queryItem['user_id'],
            'roleName' => $queryItem['item_name'],
            'createdAt' => $queryItem['created_date'],
            ];
        }

        $result = self::checkAccess($userID, $permissionName, $assignments, $params);

        return $result;

        break;

    }

}

public function checkAccess($userID, $permissionName, $assignments, $params = [])
{

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

    $auth->loadFromCache();

    if ($auth->items !== null) {
        return $auth->checkAccessFromCache($userID, $permissionName, $params, $assignments);
    } else {
        return $auth->checkAccessRecursive($userID, $permissionName, $params, $assignments);
    }
}

public function assign($type, $role, $userID = null, $groupID = null)
{

    switch ($type) {

        case 'group':

        // clear existing assigments
        self::revoke('group', $userID, $groupID);

        $groupAccess = new GroupAccess();
        $groupAccess->group_id = $groupID;
        $groupAccess->user_id = $userID;
        $groupAccess->item_name = $role;
        $groupAccess->created_date = time();

        return $groupAccess->save();

        break;

        case 'user':

        // clear existing assignments
        self::revoke('user', $userID);

        $userAccess = new UserAccess();
        $userAccess->user_id = $userID;
        $userAccess->item_name = $role;
        $userAccess->created_date = time();

        return $userAccess->save();

        break;

    }

}

public function revoke($type, $userID, $groupID = null)
{

    switch ($type) {

        case 'group':

        GroupAccess::deleteAll('user_id = :user_id and group_id = :group_id', [':user_id' => $userID, ':group_id' => $groupID]);

        break;

        case 'user':

        UserAccess::deleteAll('user_id = :user_id', [':user_id' => $userID]);

        break;

    }

}

}

И вот несколько примеров использования для доступа к функциям:

// get the user option
echo Yii::$app->options->getOption('user', 'active_group_id');

// assign group role
Yii::$app->access->assign('group', 'group_creator', 22, 18);
// assign user role
Yii::$app->access->assign('user', 'app_user', 22);

// revoke group access
Yii::$app->access->revoke('group', 22, 18);
// revoke user access
Yii::$app->access->revoke('user', 22);

// test user permission
var_dump(Yii::$app->access->canUser('user', 'manage_app_settings'));
// test the group permission
var_dump(Yii::$app->access->canUser('group', 'manage_group_settings'));

По сути, я скопировал функцию checkAccess из DbManager и немного переработал ее, чтобы проверить доступ пользователя на основе группы.

Единственная проблема заключается в том, что мне пришлось внести изменения в реальный исходный класс DbManager, чтобы сделать $ items (property), checkAccessFromCache (function) и checkAccessRecursive (function) общедоступными, чтобы к ним можно было обращаться за пределы класса. Основным недостатком является возможность обновления ...

Есть ли способ обойти это?

Благодарю.

Ответы на вопрос(1)

Ваш ответ на вопрос