This commit is contained in:
2020-02-01 16:47:12 +07:00
commit 4c619ad6e6
16739 changed files with 3329179 additions and 0 deletions

View File

@@ -0,0 +1,119 @@
<?php
namespace yii2mod\rbac\models;
use Yii;
use yii\base\BaseObject;
use yii\base\InvalidConfigException;
use yii\web\IdentityInterface;
/**
* Class AssignmentModel
*
* @package yii2mod\rbac\models
*/
class AssignmentModel extends BaseObject
{
/**
* @var IdentityInterface
*/
public $user;
/**
* @var int User id
*/
public $userId;
/**
* @var \yii\rbac\ManagerInterface
*/
protected $manager;
/**
* AssignmentModel constructor.
*
* @param IdentityInterface $user
* @param array $config
*
* @throws InvalidConfigException
*/
public function __construct(IdentityInterface $user, $config = [])
{
$this->user = $user;
$this->userId = $user->getId();
$this->manager = Yii::$app->authManager;
if ($this->userId === null) {
throw new InvalidConfigException('The "userId" property must be set.');
}
parent::__construct($config);
}
/**
* Assign a roles and permissions to the user.
*
* @param array $items
*
* @return bool
*/
public function assign(array $items): bool
{
foreach ($items as $name) {
$item = $this->manager->getRole($name);
$item = $item ?: $this->manager->getPermission($name);
$this->manager->assign($item, $this->userId);
}
return true;
}
/**
* Revokes a roles and permissions from the user.
*
* @param array $items
*
* @return bool
*/
public function revoke(array $items): bool
{
foreach ($items as $name) {
$item = $this->manager->getRole($name);
$item = $item ?: $this->manager->getPermission($name);
$this->manager->revoke($item, $this->userId);
}
return true;
}
/**
* Get all available and assigned roles and permissions
*
* @return array
*/
public function getItems(): array
{
$available = [];
$assigned = [];
foreach (array_keys($this->manager->getRoles()) as $name) {
$available[$name] = 'role';
}
foreach (array_keys($this->manager->getPermissions()) as $name) {
if ($name[0] != '/') {
$available[$name] = 'permission';
}
}
foreach ($this->manager->getAssignments($this->userId) as $item) {
$assigned[$item->roleName] = $available[$item->roleName];
unset($available[$item->roleName]);
}
return [
'available' => $available,
'assigned' => $assigned,
];
}
}

View File

@@ -0,0 +1,320 @@
<?php
namespace yii2mod\rbac\models;
use Yii;
use yii\base\Model;
use yii\helpers\Json;
use yii\rbac\Item;
use yii\rbac\Rule;
/**
* Class AuthItemModel
*
* @property string $name
* @property int $type
* @property string $description
* @property string $ruleName
* @property string $data
* @property Item $item
*/
class AuthItemModel extends Model
{
/**
* @var string auth item name
*/
public $name;
/**
* @var int auth item type
*/
public $type;
/**
* @var string auth item description
*/
public $description;
/**
* @var string biz rule name
*/
public $ruleName;
/**
* @var null|string additional data
*/
public $data;
/**
* @var \yii\rbac\ManagerInterface
*/
protected $manager;
/**
* @var Item
*/
private $_item;
/**
* AuthItemModel constructor.
*
* @param Item|null $item
* @param array $config
*/
public function __construct($item = null, $config = [])
{
$this->_item = $item;
$this->manager = Yii::$app->authManager;
if ($item !== null) {
$this->name = $item->name;
$this->type = $item->type;
$this->description = $item->description;
$this->ruleName = $item->ruleName;
$this->data = $item->data === null ? null : Json::encode($item->data);
}
parent::__construct($config);
}
/**
* @inheritdoc
*/
public function rules(): array
{
return [
[['name', 'description', 'data', 'ruleName'], 'trim'],
[['name', 'type'], 'required'],
['ruleName', 'checkRule'],
['name', 'validateName', 'when' => function () {
return $this->getIsNewRecord() || ($this->_item->name != $this->name);
}],
['type', 'integer'],
[['description', 'data', 'ruleName'], 'default'],
['name', 'string', 'max' => 64],
];
}
/**
* Validate item name
*/
public function validateName()
{
$value = $this->name;
if ($this->manager->getRole($value) !== null || $this->manager->getPermission($value) !== null) {
$message = Yii::t('yii', '{attribute} "{value}" has already been taken.');
$params = [
'attribute' => $this->getAttributeLabel('name'),
'value' => $value,
];
$this->addError('name', Yii::$app->getI18n()->format($message, $params, Yii::$app->language));
}
}
/**
* Check for rule
*/
public function checkRule()
{
$name = $this->ruleName;
if (!$this->manager->getRule($name)) {
try {
$rule = Yii::createObject($name);
if ($rule instanceof Rule) {
$rule->name = $name;
$this->manager->add($rule);
} else {
$this->addError('ruleName', Yii::t('yii2mod.rbac', 'Invalid rule "{value}"', ['value' => $name]));
}
} catch (\Exception $exc) {
$this->addError('ruleName', Yii::t('yii2mod.rbac', 'Rule "{value}" does not exists', ['value' => $name]));
}
}
}
/**
* @inheritdoc
*/
public function attributeLabels(): array
{
return [
'name' => Yii::t('yii2mod.rbac', 'Name'),
'type' => Yii::t('yii2mod.rbac', 'Type'),
'description' => Yii::t('yii2mod.rbac', 'Description'),
'ruleName' => Yii::t('yii2mod.rbac', 'Rule Name'),
'data' => Yii::t('yii2mod.rbac', 'Data'),
];
}
/**
* Check if is new record.
*
* @return bool
*/
public function getIsNewRecord(): bool
{
return $this->_item === null;
}
/**
* Find role
*
* @param string $id
*
* @return null|\self
*/
public static function find(string $id)
{
$item = Yii::$app->authManager->getRole($id);
if ($item !== null) {
return new self($item);
}
return null;
}
/**
* Save role to [[\yii\rbac\authManager]]
*
* @return bool
*/
public function save(): bool
{
if ($this->validate()) {
if ($this->_item === null) {
if ($this->type == Item::TYPE_ROLE) {
$this->_item = $this->manager->createRole($this->name);
} else {
$this->_item = $this->manager->createPermission($this->name);
}
$isNew = true;
$oldName = false;
} else {
$isNew = false;
$oldName = $this->_item->name;
}
$this->_item->name = $this->name;
$this->_item->description = $this->description;
$this->_item->ruleName = $this->ruleName;
$this->_item->data = Json::decode($this->data);
if ($isNew) {
$this->manager->add($this->_item);
} else {
$this->manager->update($oldName, $this->_item);
}
return true;
}
return false;
}
/**
* Add child to Item
*
* @param array $items
*
* @return bool
*/
public function addChildren(array $items): bool
{
if ($this->_item) {
foreach ($items as $name) {
$child = $this->manager->getPermission($name);
if (empty($child) && $this->type == Item::TYPE_ROLE) {
$child = $this->manager->getRole($name);
}
$this->manager->addChild($this->_item, $child);
}
}
return true;
}
/**
* Remove child from an item
*
* @param array $items
*
* @return bool
*/
public function removeChildren(array $items): bool
{
if ($this->_item !== null) {
foreach ($items as $name) {
$child = $this->manager->getPermission($name);
if (empty($child) && $this->type == Item::TYPE_ROLE) {
$child = $this->manager->getRole($name);
}
$this->manager->removeChild($this->_item, $child);
}
}
return true;
}
/**
* Get all available and assigned roles, permission and routes
*
* @return array
*/
public function getItems(): array
{
$available = [];
$assigned = [];
if ($this->type == Item::TYPE_ROLE) {
foreach (array_keys($this->manager->getRoles()) as $name) {
$available[$name] = 'role';
}
}
foreach (array_keys($this->manager->getPermissions()) as $name) {
$available[$name] = $name[0] == '/' ? 'route' : 'permission';
}
foreach ($this->manager->getChildren($this->_item->name) as $item) {
$assigned[$item->name] = $item->type == 1 ? 'role' : ($item->name[0] == '/' ? 'route' : 'permission');
unset($available[$item->name]);
}
unset($available[$this->name]);
return [
'available' => $available,
'assigned' => $assigned,
];
}
/**
* @return null|Item
*/
public function getItem()
{
return $this->_item;
}
/**
* Get type name
*
* @param mixed $type
*
* @return string|array
*/
public static function getTypeName($type = null)
{
$result = [
Item::TYPE_PERMISSION => 'Permission',
Item::TYPE_ROLE => 'Role',
];
if ($type === null) {
return $result;
}
return $result[$type];
}
}

View File

@@ -0,0 +1,176 @@
<?php
namespace yii2mod\rbac\models;
use Yii;
use yii\base\Model;
use yii\rbac\Rule;
/**
* Class BizRuleModel
*
* @package yii2mod\rbac\models
*/
class BizRuleModel extends Model
{
/**
* @var string name of the rule
*/
public $name;
/**
* @var int UNIX timestamp representing the rule creation time
*/
public $createdAt;
/**
* @var int UNIX timestamp representing the rule updating time
*/
public $updatedAt;
/**
* @var string Rule className
*/
public $className;
/**
* @var \yii\rbac\ManagerInterface
*/
protected $manager;
/**
* @var Rule
*/
private $_item;
/**
* BizRuleModel constructor.
*
* @param \yii\rbac\Rule $item
* @param array $config
*/
public function __construct($item = null, $config = [])
{
$this->_item = $item;
$this->manager = Yii::$app->authManager;
if ($item !== null) {
$this->name = $item->name;
$this->className = get_class($item);
}
parent::__construct($config);
}
/**
* @inheritdoc
*/
public function rules(): array
{
return [
[['name', 'className'], 'trim'],
[['name', 'className'], 'required'],
['className', 'string'],
['name', 'string', 'max' => 64],
['className', 'classExists'],
];
}
/**
* Validate className
*/
public function classExists()
{
if (!class_exists($this->className)) {
$message = Yii::t('yii2mod.rbac', "Unknown class '{class}'", ['class' => $this->className]);
$this->addError('className', $message);
return;
}
if (!is_subclass_of($this->className, Rule::class)) {
$message = Yii::t('yii2mod.rbac', "'{class}' must extend from 'yii\\rbac\\Rule' or its child class", [
'class' => $this->className, ]);
$this->addError('className', $message);
}
}
/**
* @inheritdoc
*/
public function attributeLabels(): array
{
return [
'name' => Yii::t('yii2mod.rbac', 'Name'),
'className' => Yii::t('yii2mod.rbac', 'Class Name'),
];
}
/**
* Check if record is new
*
* @return bool
*/
public function getIsNewRecord(): bool
{
return $this->_item === null;
}
/**
* Create object
*
* @param $id
*
* @return BizRuleModel|null
*/
public static function find(int $id)
{
$item = Yii::$app->authManager->getRule($id);
if ($item !== null) {
return new static($item);
}
return null;
}
/**
* Save rule
*
* @return bool
*/
public function save(): bool
{
if ($this->validate()) {
$class = $this->className;
if ($this->_item === null) {
$this->_item = new $class();
$isNew = true;
$oldName = false;
} else {
$isNew = false;
$oldName = $this->_item->name;
}
$this->_item->name = $this->name;
if ($isNew) {
$this->manager->add($this->_item);
} else {
$this->manager->update($oldName, $this->_item);
}
return true;
}
return false;
}
/**
* @return null|Rule
*/
public function getItem()
{
return $this->_item;
}
}

View File

@@ -0,0 +1,293 @@
<?php
namespace yii2mod\rbac\models;
use Yii;
use yii\base\BaseObject;
use yii\base\Controller;
use yii\base\Module;
use yii\caching\TagDependency;
use yii\helpers\VarDumper;
/**
* Class RouteModel
*
* @package yii2mod\rbac\models
*/
class RouteModel extends BaseObject
{
/**
* @var string cache tag
*/
const CACHE_TAG = 'yii2mod.rbac.route';
/**
* @var \yii\caching\Cache
*/
public $cache;
/**
* @var int cache duration
*/
public $cacheDuration = 3600;
/**
* @var array list of module IDs that will be excluded
*/
public $excludeModules = [];
/**
* @var \yii\rbac\ManagerInterface
*/
protected $manager;
/**
* RouteModel constructor.
*
* @param array $config
*/
public function __construct(array $config = [])
{
$this->cache = Yii::$app->cache;
$this->manager = Yii::$app->authManager;
parent::__construct($config);
}
/**
* Assign items
*
* @param array $routes
*
* @return bool
*/
public function addNew(array $routes): bool
{
foreach ($routes as $route) {
$this->manager->add($this->manager->createPermission('/' . trim($route, ' /')));
}
$this->invalidate();
return true;
}
/**
* Remove items
*
* @param array $routes
*
* @return bool
*/
public function remove(array $routes): bool
{
foreach ($routes as $route) {
$item = $this->manager->createPermission('/' . trim($route, '/'));
$this->manager->remove($item);
}
$this->invalidate();
return true;
}
/**
* Get available and assigned routes
*
* @return array
*/
public function getAvailableAndAssignedRoutes(): array
{
$routes = $this->getAppRoutes();
$exists = [];
foreach (array_keys($this->manager->getPermissions()) as $name) {
if ($name[0] !== '/') {
continue;
}
$exists[] = $name;
unset($routes[$name]);
}
return [
'available' => array_keys($routes),
'assigned' => $exists,
];
}
/**
* Get list of application routes
*
* @param null|string $module
*
* @return array
*/
public function getAppRoutes(string $module = null): array
{
if ($module === null) {
$module = Yii::$app;
} else {
$module = Yii::$app->getModule($module);
}
$key = [__METHOD__, $module->getUniqueId()];
$result = (($this->cache !== null) ? $this->cache->get($key) : false);
if ($result === false) {
$result = [];
$this->getRouteRecursive($module, $result);
if ($this->cache !== null) {
$this->cache->set($key, $result, $this->cacheDuration, new TagDependency([
'tags' => self::CACHE_TAG,
]));
}
}
return $result;
}
/**
* Invalidate the cache
*/
public function invalidate()
{
if ($this->cache !== null) {
TagDependency::invalidate($this->cache, self::CACHE_TAG);
}
}
/**
* Get route(s) recursive
*
* @param Module $module
* @param array $result
*/
protected function getRouteRecursive(Module $module, &$result)
{
if (!in_array($module->id, $this->excludeModules)) {
$token = "Get Route of '" . get_class($module) . "' with id '" . $module->uniqueId . "'";
Yii::beginProfile($token, __METHOD__);
try {
foreach ($module->getModules() as $id => $child) {
if (($child = $module->getModule($id)) !== null) {
$this->getRouteRecursive($child, $result);
}
}
foreach ($module->controllerMap as $id => $type) {
$this->getControllerActions($type, $id, $module, $result);
}
$namespace = trim($module->controllerNamespace, '\\') . '\\';
$this->getControllerFiles($module, $namespace, '', $result);
$all = '/' . ltrim($module->uniqueId . '/*', '/');
$result[$all] = $all;
} catch (\Exception $exc) {
Yii::error($exc->getMessage(), __METHOD__);
}
Yii::endProfile($token, __METHOD__);
}
}
/**
* Get list controllers under module
*
* @param Module $module
* @param string $namespace
* @param string $prefix
* @param mixed $result
*/
protected function getControllerFiles(Module $module, string $namespace, string $prefix, &$result)
{
$path = Yii::getAlias('@' . str_replace('\\', '/', $namespace), false);
$token = "Get controllers from '$path'";
Yii::beginProfile($token, __METHOD__);
try {
if (!is_dir($path)) {
return;
}
foreach (scandir($path) as $file) {
if ($file == '.' || $file == '..') {
continue;
}
if (is_dir($path . '/' . $file) && preg_match('%^[a-z0-9_/]+$%i', $file . '/')) {
$this->getControllerFiles($module, $namespace . $file . '\\', $prefix . $file . '/', $result);
} elseif (strcmp(substr($file, -14), 'Controller.php') === 0) {
$baseName = substr(basename($file), 0, -14);
$name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', $baseName));
$id = ltrim(str_replace(' ', '-', $name), '-');
$className = $namespace . $baseName . 'Controller';
if (strpos($className, '-') === false && class_exists($className) && is_subclass_of($className, 'yii\base\Controller')) {
$this->getControllerActions($className, $prefix . $id, $module, $result);
}
}
}
} catch (\Exception $exc) {
Yii::error($exc->getMessage(), __METHOD__);
}
Yii::endProfile($token, __METHOD__);
}
/**
* Get list actions of controller
*
* @param mixed $type
* @param string $id
* @param Module $module
* @param mixed $result
*/
protected function getControllerActions($type, $id, Module $module, &$result)
{
$token = 'Create controller with config=' . VarDumper::dumpAsString($type) . " and id='$id'";
Yii::beginProfile($token, __METHOD__);
try {
/* @var $controller Controller */
$controller = Yii::createObject($type, [$id, $module]);
$this->getActionRoutes($controller, $result);
$all = "/{$controller->uniqueId}/*";
$result[$all] = $all;
} catch (\Exception $exc) {
Yii::error($exc->getMessage(), __METHOD__);
}
Yii::endProfile($token, __METHOD__);
}
/**
* Get route of action
*
* @param Controller $controller
* @param array $result all controller action
*/
protected function getActionRoutes(Controller $controller, &$result)
{
$token = "Get actions of controller '" . $controller->uniqueId . "'";
Yii::beginProfile($token, __METHOD__);
try {
$prefix = '/' . $controller->uniqueId . '/';
foreach ($controller->actions() as $id => $value) {
$result[$prefix . $id] = $prefix . $id;
}
$class = new \ReflectionClass($controller);
foreach ($class->getMethods() as $method) {
$name = $method->getName();
if ($method->isPublic() && !$method->isStatic() && strpos($name, 'action') === 0 && $name !== 'actions') {
$name = strtolower(preg_replace('/(?<![A-Z])[A-Z]/', ' \0', substr($name, 6)));
$id = $prefix . ltrim(str_replace(' ', '-', $name), '-');
$result[$id] = $id;
}
}
} catch (\Exception $exc) {
Yii::error($exc->getMessage(), __METHOD__);
}
Yii::endProfile($token, __METHOD__);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace yii2mod\rbac\models\search;
use yii\base\Model;
use yii\data\ActiveDataProvider;
/**
* Class AssignmentSearch
*
* @package yii2mod\rbac\models\search
*/
class AssignmentSearch extends Model
{
/**
* @var string user id
*/
public $id;
/**
* @var string username
*/
public $username;
/**
* @var int the default page size
*/
public $pageSize = 25;
/**
* @inheritdoc
*/
public function rules(): array
{
return [
[['id', 'username'], 'safe'],
];
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
* @param \yii\db\ActiveRecord $class
* @param $idField
* @param string $usernameField
*
* @return ActiveDataProvider
*/
public function search(array $params, $class, string $idField, string $usernameField): ActiveDataProvider
{
$query = $class::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => $this->pageSize,
],
]);
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
$query->andFilterWhere([$idField => $this->id]);
$query->andFilterWhere(['like', $usernameField, $this->username]);
return $dataProvider;
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace yii2mod\rbac\models\search;
use dosamigos\arrayquery\ArrayQuery;
use Yii;
use yii\base\Model;
use yii\data\ArrayDataProvider;
use yii\rbac\Item;
/**
* Class AuthItemSearch
*
* @package yii2mod\rbac\models\search
*/
class AuthItemSearch extends Model
{
/**
* @var string auth item name
*/
public $name;
/**
* @var int auth item type
*/
public $type;
/**
* @var string auth item description
*/
public $description;
/**
* @var string auth item rule name
*/
public $ruleName;
/**
* @var int the default page size
*/
public $pageSize = 25;
/**
* @inheritdoc
*/
public function rules(): array
{
return [
[['name', 'ruleName', 'description'], 'trim'],
[['type'], 'integer'],
[['name', 'ruleName', 'description'], 'safe'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels(): array
{
return [
'name' => Yii::t('yii2mod.rbac', 'Name'),
'type' => Yii::t('yii2mod.rbac', 'Type'),
'description' => Yii::t('yii2mod.rbac', 'Description'),
'rule' => Yii::t('yii2mod.rbac', 'Rule'),
'data' => Yii::t('yii2mod.rbac', 'Data'),
];
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return \yii\data\ArrayDataProvider
*/
public function search(array $params): ArrayDataProvider
{
$authManager = Yii::$app->getAuthManager();
if ($this->type == Item::TYPE_ROLE) {
$items = $authManager->getRoles();
} else {
$items = array_filter($authManager->getPermissions(), function ($item) {
return strpos($item->name, '/') !== 0;
});
}
$query = new ArrayQuery($items);
$this->load($params);
if ($this->validate()) {
$query->addCondition('name', $this->name ? "~{$this->name}" : null)
->addCondition('ruleName', $this->ruleName ? "~{$this->ruleName}" : null)
->addCondition('description', $this->description ? "~{$this->description}" : null);
}
return new ArrayDataProvider([
'allModels' => $query->find(),
'sort' => [
'attributes' => ['name'],
],
'pagination' => [
'pageSize' => $this->pageSize,
],
]);
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace yii2mod\rbac\models\search;
use dosamigos\arrayquery\ArrayQuery;
use Yii;
use yii\base\Model;
use yii\data\ArrayDataProvider;
/**
* Class BizRuleSearch
*
* @package yii2mod\rbac\models\search
*/
class BizRuleSearch extends Model
{
/**
* @var string name of the rule
*/
public $name;
/**
* @var int the default page size
*/
public $pageSize = 25;
/**
* @inheritdoc
*/
public function rules(): array
{
return [
['name', 'trim'],
['name', 'safe'],
];
}
/**
* Creates data provider instance with search query applied
*
* @param array $params
*
* @return ArrayDataProvider
*/
public function search(array $params): ArrayDataProvider
{
$query = new ArrayQuery(Yii::$app->authManager->getRules());
if ($this->load($params) && $this->validate()) {
$query->addCondition('name', $this->name ? "~{$this->name}" : null);
}
return new ArrayDataProvider([
'allModels' => $query->find(),
'sort' => [
'attributes' => ['name'],
],
'pagination' => [
'pageSize' => $this->pageSize,
],
]);
}
}