Здравствуйте.
Системой могут пользоваться разные фирмы (saas). Одна БД для всех.
Как сделать так, чтобы RBAC был отдельным для конкретной фирмы? Есть идея как-то добавить company_id в таблицы, создать какой-то RbacHelper. Но пока трудно представляю как переопределить стандартную логику...
Разные RBAC в одной БД
Re: Разные RBAC в одной БД
Простой пример:
В одной компании роль admin может удалять юзеров, а в другой - нет. Обе компании должны использовать одну БД.
В одной компании роль admin может удалять юзеров, а в другой - нет. Обе компании должны использовать одну БД.
Re: Разные RBAC в одной БД
Добавьте company_id. Переопределите логику наследованием/пепеписыванием DbManager.
Re: Разные RBAC в одной БД
В общем что мне пришлось сделать:
1) Нaписать миграцию под новую структуру RBAC:
- добавить поле id;
- добавить поле company_id;
- убрать все существующие ключи (было много FK, PK по полям типа "name" и т. п.).
2) В /components добавить новый класс:
- переопределить в нём нужные нам методы;
- добавить поле companyId для инициализации ID компании, с которой работаем.
3) В модель, которая отвечает за создание новой компании, добавить вызов метода RbacComponent::initRbac, чтобы создать RBAC для нужной нам компании:
Теперь, чтобы работать с RBAC конкретной компании, нужно сделать следующее:
1) Нaписать миграцию под новую структуру RBAC:
- добавить поле id;
- добавить поле company_id;
- убрать все существующие ключи (было много FK, PK по полям типа "name" и т. п.).
Код: Выделить всё
public function safeUp()
{
$this->execute("
CREATE TABLE IF NOT EXISTS `auth_assignment` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`item_name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`user_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`created_at` int(11) DEFAULT NULL,
`company_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
");
$this->execute("
CREATE TABLE IF NOT EXISTS `auth_item` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`type` smallint(6) NOT NULL,
`description` text COLLATE utf8_unicode_ci,
`rule_name` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
`data` blob,
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`company_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
");
$this->execute("
CREATE TABLE IF NOT EXISTS `auth_item_child` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`parent` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`child` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`company_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
");
$this->execute("
CREATE TABLE IF NOT EXISTS `auth_rule` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(64) COLLATE utf8_unicode_ci NOT NULL,
`data` blob,
`created_at` int(11) DEFAULT NULL,
`updated_at` int(11) DEFAULT NULL,
`company_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `company_id` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
");
return true;
}
- переопределить в нём нужные нам методы;
- добавить поле companyId для инициализации ID компании, с которой работаем.
Код: Выделить всё
<?php
namespace app\components\rbac;
use Yii;
use yii\rbac\DbManager;
use yii\caching\Cache;
use yii\db\Connection;
use yii\db\Query;
use yii\db\Expression;
use yii\base\InvalidCallException;
use yii\base\InvalidParamException;
use yii\di\Instance;
use yii\rbac\Permission;
use yii\rbac\Role;
use yii\rbac\Item;
use yii\rbac\Rule;
use yii\rbac\Assignment;
/**
* Class RbacComponent.
* We need this class because each company can have own RBAC.
* @package app\components\rbac
*/
class RbacComponent extends DbManager
{
/**
* @var integer
*/
public $companyId;
/**
* @inheritdoc
*/
public function addChild($parent, $child)
{
if (!$this->companyId) {
throw new InvalidParamException("Please set the parameter 'companyId' for this class (RbacComponent).");
}
if ($parent->name === $child->name) {
throw new InvalidParamException("Cannot add '{$parent->name}' as a child of itself.");
}
if ($parent instanceof Permission && $child instanceof Role) {
throw new InvalidParamException('Cannot add a role as a child of a permission.');
}
if ($this->detectLoop($parent, $child)) {
throw new InvalidCallException("Cannot add '{$child->name}' as a child of '{$parent->name}'. A loop has been detected.");
}
$this->db->createCommand()
->insert($this->itemChildTable, [
'parent' => $parent->name,
'child' => $child->name,
'company_id' => $this->companyId,
])
->execute();
$this->invalidateCache();
return true;
}
/**
* @inheritdoc
*/
protected function addRule($rule)
{
if (!$this->companyId) {
throw new InvalidParamException("Please set the parameter 'companyId' for this class (RbacComponent).");
}
$time = time();
if ($rule->createdAt === null) {
$rule->createdAt = $time;
}
if ($rule->updatedAt === null) {
$rule->updatedAt = $time;
}
$this->db->createCommand()
->insert($this->ruleTable, [
'name' => $rule->name,
'data' => serialize($rule),
'created_at' => $rule->createdAt,
'updated_at' => $rule->updatedAt,
'company_id' => $this->companyId,
])->execute();
$this->invalidateCache();
return true;
}
/**
* @inheritdoc
*/
public function assign($role, $userId)
{
if (!$this->companyId) {
throw new InvalidParamException("Please set the parameter 'companyId' for this class (RbacComponent).");
}
$assignment = new Assignment([
'userId' => $userId,
'roleName' => $role->name,
'createdAt' => time(),
]);
$this->db->createCommand()
->insert($this->assignmentTable, [
'user_id' => $assignment->userId,
'item_name' => $assignment->roleName,
'created_at' => $assignment->createdAt,
'company_id' => $this->companyId,
])->execute();
return $assignment;
}
/**
* @inheritdoc
*/
protected function addItem($item)
{
if (!$this->companyId) {
throw new InvalidParamException("Please set the parameter 'companyId' for this class (RbacComponent).");
}
$time = time();
if ($item->createdAt === null) {
$item->createdAt = $time;
}
if ($item->updatedAt === null) {
$item->updatedAt = $time;
}
$this->db->createCommand()
->insert($this->itemTable, [
'name' => $item->name,
'type' => $item->type,
'description' => $item->description,
'rule_name' => $item->ruleName,
'data' => $item->data === null ? null : serialize($item->data),
'created_at' => $item->createdAt,
'updated_at' => $item->updatedAt,
'company_id' => $this->companyId,
])->execute();
$this->invalidateCache();
return true;
}
/**
* Init RBAC for a specific company.
* @param integer $companyId
*/
public static function initRbac($companyId)
{
$auth = Yii::$app->authManager;
$auth->companyId = $companyId;
...
$admin = $auth->createRole('admin');
$auth->add($admin);
$createUsers = $auth->createPermission('create_users');
$viewUsers = $auth->createPermission('view_users');
$editUsers = $auth->createPermission('edit_users');
$deleteUsers = $auth->createPermission('delete_users');
$auth->add($createUsers);
$auth->add($viewUsers);
$auth->add($editUsers);
$auth->add($deleteUsers);
$auth->addChild($admin, $createUsers);
$auth->addChild($admin, $viewUsers);
$auth->addChild($admin, $editUsers);
$auth->addChild($admin, $deleteUsers);
}
}
Код: Выделить всё
// Init RBAC:
RbacComponent::initRbac($id);
Код: Выделить всё
$auth = Yii::$app->authManager;
$auth->companyId = $companyId;
...
$admin = $auth->createRole('admin');
$auth->add($admin);
Re: Разные RBAC в одной БД
Имхо вы перемудрили, так переделав дефолтную работу.
Я бы просто ввел для каждой компании свой набор прав вида
create_users_{company_id}
И в can при проверке просто в фоне этот суфикс динамически устанавливать
Я бы просто ввел для каждой компании свой набор прав вида
create_users_{company_id}
И в can при проверке просто в фоне этот суфикс динамически устанавливать