2015-04-01 2 views
7

Мое приложение технически имеет две области: глобальную область (обратная связь, профиль пользователя, пользовательские настройки и т. Д.) И область группы (контакты, проекты, групповой профиль, групповые настройки и т. Д.).).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.

Мой DB настроен как:

  1. пользователей (status_id, имя пользователя, AUTH_KEY, password_hash, электронная почта и т.д.)

  2. групп (status_id, название, описание и т.д.)

  3. 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 может претендовать на идентификатор_пользователь, и я могу даже использовать более короткую «можно» версию, которая прекрасно работает для зарегистрированного пользователя, но мне нужно, чтобы проверить доступ на основании пользовательского параметра, как показано ниже:

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

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

Было бы неплохо, если бы я мог найти функцию, которая работает так же, как и native 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, чтобы сделать все объекты (свойства), checkAccessFromCache (function) и checkAccessRecursive (function) общедоступными, чтобы к ним можно было получить доступ за пределами класс. Основным недостатком является возможность обновления ...

В любом случае?

Спасибо.

+0

Я отправил рабочее решение. –

ответ

2

Это рабочее окончательное решение.

Итак, еще один день, больше рефакторинг.

Мое окончательное решение использует функцию checkAccess в исходных файлах DbManager/ManagerInterface, но я добавил параметр $ assignments, который должен быть передан. Основная проблема заключается в том, что мне пришлось создать собственный список назначений для проверки. Убедитесь, что вы закомментируете строки, в которых задана переменная $ assignments.

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

<?php 

namespace app\models; 

use Yii; 

class Access 
{ 

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

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

    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 = $auth->checkAccess($userID, $permissionName, $assignments, $params); 

     return $result; 

     break; 

     case 'user': 

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

     $queryAll = UserAccess::find() 
     ->where('user_id = :user_id', [':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 = $auth->checkAccess($userID, $permissionName, $assignments, $params); 

     return $result; 

     break; 

    } 

} 

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; 

    } 

} 

} 

А вот модифицированная функция CheckAccess в DBManager:

public function checkAccess($userId, $permissionName, $assignments, $params = []) 
{ 
    //$assignments = $this->getAssignments($userId); 
    $this->loadFromCache(); 
    if ($this->items !== null) { 
     return $this->checkAccessFromCache($userId, $permissionName, $params, $assignments); 
    } else { 
     return $this->checkAccessRecursive($userId, $permissionName, $params, $assignments); 
    } 
} 

А вот модифицированная функция CheckAccess в ManagerInterface.php:

public function checkAccess($userId, $permissionName, $assignments, $params = []); 

Я не изменял $ items, checkAccessFromCache и checkAccessRecursiv e функции для общественности из защищенных.

А вот моя UserAccess модель:

<?php 

namespace app\models; 

use Yii; 
use yii\db\ActiveRecord; 

/** 
* This is the model class for table "app_user_access". 
* 
* @property integer $id 
* @property integer $user_id 
* @property string $item_name 
* @property integer $created_date 
* 
* @property AppAuthItem $itemName 
* @property AppUsers $user 
*/ 
class UserAccess extends ActiveRecord 
{ 
/** 
* @inheritdoc 
*/ 
public static function tableName() 
{ 
    return 'app_user_access'; 
} 

/** 
* @inheritdoc 
*/ 
public function rules() 
{ 
    return [ 
     [['user_id', 'item_name', 'created_date'], 'required'], 
     [['user_id', 'created_date'], 'integer'], 
     [['item_name'], 'string', 'max' => 64] 
    ]; 
} 

/** 
* @inheritdoc 
*/ 
public function attributeLabels() 
{ 
    return [ 
     'id' => 'ID', 
     'user_id' => 'User ID', 
     'item_name' => 'Item Name', 
     'created_date' => 'Created Date', 
    ]; 
} 

/** 
* @return \yii\db\ActiveQuery 
*/ 
public function getItemName() 
{ 
    return $this->hasOne(AppAuthItem::className(), ['name' => 'item_name']); 
} 

/** 
* @return \yii\db\ActiveQuery 
*/ 
public function getUser() 
{ 
    return $this->hasOne(AppUsers::className(), ['id' => 'user_id']); 
} 
} 

А вот GroupAccess Модель:

<?php 

namespace app\models; 

use Yii; 
use yii\db\ActiveRecord; 

/** 
* This is the model class for table "app_group_access". 
* 
* @property integer $id 
* @property integer $group_id 
* @property integer $user_id 
* @property string $item_name 
* @property integer $created_date 
* 
* @property AppUsers $user 
* @property AppAuthItem $itemName 
* @property AppGroups $group 
*/ 
class GroupAccess extends ActiveRecord 
{ 
/** 
* @inheritdoc 
*/ 
public static function tableName() 
{ 
    return 'app_group_access'; 
} 

/** 
* @inheritdoc 
*/ 
public function rules() 
{ 
    return [ 
     [['group_id', 'user_id', 'item_name', 'created_date'], 'required'], 
     [['group_id', 'user_id', 'created_date'], 'integer'], 
     [['item_name'], 'string', 'max' => 64] 
    ]; 
} 

/** 
* @inheritdoc 
*/ 
public function attributeLabels() 
{ 
    return [ 
     'id' => 'ID', 
     'group_id' => 'Group ID', 
     'user_id' => 'User ID', 
     'item_name' => 'Item Name', 
     'created_date' => 'Created Date', 
    ]; 
} 

/** 
* @return \yii\db\ActiveQuery 
*/ 
public function getUser() 
{ 
    return $this->hasOne(AppUsers::className(), ['id' => 'user_id']); 
} 

/** 
* @return \yii\db\ActiveQuery 
*/ 
public function getItemName() 
{ 
    return $this->hasOne(AppAuthItem::className(), ['name' => 'item_name']); 
} 

/** 
* @return \yii\db\ActiveQuery 
*/ 
public function getGroup() 
{ 
    return $this->hasOne(AppGroups::className(), ['id' => 'group_id']); 
} 
} 

И еще раз, некоторые полезные образцы:

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

// revoke group 
Yii::$app->access->revoke('group', 22, 18); 
// revoke user 
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')); 
Смежные вопросы