2015-04-01 61 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功能可以限定用戶ID,而我甚至可以用更短的「可以」版本,它的偉大工程的登錄用戶,但我需要基於類似下面的用戶選項檢查訪問:

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')); 

從本質上說,我複製從DbManager的的checkAccess功能和重新設計它很少檢查基於組的用戶訪問。

唯一的問題是,我必須對實際的源DbManager類進行更改,以使$ items(property),checkAccessFromCache(function)和checkAccessRecursive(function)全部公開,以便它們可以在班上。主要缺點是可更新性...

任何方法?

謝謝。

+0

我已經發佈一個工作解決方案 – 2015-04-05 01:56:51

回答

2

這是一個可行的最終解決方案。

於是,又一天,更多的重構。

我最終的解決方案使用DbManager/ManagerInterface源文件中的checkAccess函數,但我添加了要傳遞的$ 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; 

    } 

} 

} 

這裏是在DbManager修改後的checkAccess功能:

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); 
    } 
} 

這裏是在ManagerInterface.php修改後的checkAccess功能:

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'));