2015-04-01 21 views
7

Moja aplikacja ma dwa obszary, obszar globalny (informacje zwrotne, profil użytkownika, ustawienia użytkownika itp.) Oraz obszar grupy (kontakty, projekty, profil grupowy, ustawienia grup itp.).Yii2 RBAC Wiele zadań dla każdego użytkownika na podstawie grup

Używam RBAC DBManager dla obszaru globalnego i działa dobrze, ale mam problemy z wdrażaniem mechanizmu autoryzacji dla obszaru grupy.

Powodem jest to, że grupy mogą być współużytkowane przez użytkowników, a użytkownik może mieć wiele przypisań w tabeli group_access (id, id_grupy, id_użytkownika, nazwa_podmiotu), ponieważ mogą być członkami wielu grup i mogą mieć różne poziomy uprawnień dla tych grup.

Oto moja konfiguracja auth:

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

Moja tabela group_access ma taki sam schemat jak stół auth_assignment, z tym wyjątkiem, że ma kolumnę group_id, a kolumna user_id nie jest unikalna.

Użytkownik będzie miał tylko jedno przypisanie dotyczące obszaru globalnego, ale może mieć wiele różnych przypisań w obszarze grupy, ponieważ mogą mieć uprawnienia administratora w grupie a, ale tylko prywatne uprawnienia użytkownika w grupie b.

My DB jest skonfigurowany tak:

  1. użytkowników (status_id, podając nazwę użytkownika, auth_key, password_hash, email, etc)

  2. grup (status_id, nazwa, opis, itp)

  3. Group_Access (group_id, user_id, item_name) Każdy użytkownik otrzymuje jedno zadanie dla każdej grupy, do której ma dostęp.

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

Funkcja checkAccess można zakwalifikować identyfikator użytkownika i mogę nawet użyć krótszego „can” wersję, która działa świetnie dla zalogowanego użytkownika, ale muszę sprawdzić dostęp na podstawie opcji określanych przez użytkownika, takich jak poniżej:

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

Jest to niestandardowa funkcja, która pobiera aktywny identyfikator grupy z tabeli opcji użytkownika. Jeśli użytkownik zmieni grupę, zostanie to zmienione. Mój model opcji ma trzy typy: "aplikacja", "użytkownik", "grupa".

Byłoby miło, gdybym mógł wymyślić funkcję, która działa tak samo jak natywny checkAccess, ale nazywać się checkGroupAccess i automatycznie pobierać wartości active_group_id i wyciągać przypisania użytkowników z tabeli group_access i wykonywać sprawdzanie uprawnień.

Mam nadzieję, że to ma sens.

Dziękuję za poświęcony czas.

Mike

** AKTUALIZACJA **

Tak, mam rozwiązanie, które wykorzystuje niestandardowe funkcje checkAccess aby sprawdzić odpowiednie uprawnienia w grupie lub obszarze globalnych.

Mam dwie tabele (user_access, group_access), które mają podobny schemat do domyślnej tabeli {{auth_assignment}}, której teraz nie używam. Korzystam z tabel {{auth_item}}, {{auth_item_child}} i {{auth_rule}}.

Mam dwa modele, po jednym dla każdej z tabel dostępu GroupAccess => group_access i UserAccess => user_access.

Mam również model funkcji dostępu i zmapowałem go do konfiguracji komponentów.

Oto mój model dostępu:

<?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; 

    } 

} 

} 

A oto niektóre próbki wykorzystuje się dostęp do funkcji:

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

w istocie, skopiowałem funkcję checkAccess z DbManager i przerobione to niewiele, aby sprawdzić dostęp użytkownika na podstawie grupy.

Jedynym problemem jest to, że musiałem zmienić rzeczywistą klasę źródłową DbManager tak, aby pozycje $ (właściwość), checkAccessFromCache (funkcja) i checkAccessRecursive (funkcja) były dostępne publicznie, aby można było uzyskać do nich dostęp poza klasa. Główną wadą jest aktualizacja ...

Dookoła tego?

Dzięki.

+0

Pisałem działające rozwiązanie. –

Odpowiedz

2

Oto gotowe rozwiązanie końcowe.

Więc, kolejny dzień, więcej refaktoryzacji.

Moje ostateczne rozwiązanie korzysta z funkcji checkAccess w plikach źródłowych DbManager/ManagerInterface, ale dodałem parametr $ assignments do przekazania. Głównym problemem jest to, że musiałem zbudować własną listę zadań do sprawdzenia. Pamiętaj, aby skomentować linie, w których ustawiona jest zmienna $ assignments.

Oto mój nowy model dostępu:

<?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; 

    } 

} 

} 

I tu jest zmodyfikowana funkcja checkAccess w 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); 
    } 
} 

I tu jest zmodyfikowana funkcja checkAccess w ManagerInterface.php:

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

Nie zmieniłem pozycji $, checkAccessFromCache i checkAccessRecursiv Funkcje publiczne chronione.

I tu jest mój model 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']); 
} 
} 

A oto The GroupAccess Model:

<?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']); 
} 
} 

I znów kilka przydatnych próbki:

// 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')); 
Powiązane problemy