Skip to content

Commit

Permalink
feat(app): 增加权限相关功能和接口
Browse files Browse the repository at this point in the history
  • Loading branch information
twinh committed Apr 14, 2023
1 parent 8580f56 commit 3f89097
Show file tree
Hide file tree
Showing 15 changed files with 669 additions and 0 deletions.
5 changes: 5 additions & 0 deletions config/errors.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

return [
'很抱歉,您没有权限执行该操作' => 202001,
];
22 changes: 22 additions & 0 deletions src/Metadata/PermissionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Miaoxing\App\Metadata;

/**
* @property string|null $id 编号
* @property string $appId 应用编号
* @property string $name 名称
* @property string $code 标识
* @property string $description 描述
* @property bool $isEnabled 是否启用
* @property string|null $createdAt
* @property string|null $updatedAt
* @property string $createdBy
* @property string $updatedBy
* @property string|null $deletedAt
* @property string $deletedBy
* @internal will change in the future
*/
trait PermissionTrait
{
}
20 changes: 20 additions & 0 deletions src/Metadata/PermissionsRoleTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Miaoxing\App\Metadata;

/**
* @property string|null $id 编号
* @property string $appId 应用编号
* @property string $roleId 角色编号
* @property string $permissionId 权限编号
* @property string|null $createdAt
* @property string|null $updatedAt
* @property string $createdBy
* @property string $updatedBy
* @property string|null $deletedAt
* @property string $deletedBy
* @internal will change in the future
*/
trait PermissionsRoleTrait
{
}
25 changes: 25 additions & 0 deletions src/Metadata/RoleTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Miaoxing\App\Metadata;

/**
* @property string|null $id 编号
* @property string $appId 应用编号
* @property string $parentId 父级角色编号
* @property int $level 层级
* @property string $name 名称
* @property string $code 标识
* @property string $description 描述
* @property bool $isEnabled 是否启用
* @property array $actions 菜单和操作
* @property string|null $createdAt
* @property string|null $updatedAt
* @property string $createdBy
* @property string $updatedBy
* @property string|null $deletedAt
* @property string $deletedBy
* @internal will change in the future
*/
trait RoleTrait
{
}
20 changes: 20 additions & 0 deletions src/Metadata/RolesUserTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Miaoxing\App\Metadata;

/**
* @property string|null $id
* @property string $appId 应用编号
* @property string $userId 用户编号
* @property string $roleId 角色编号
* @property string|null $createdAt
* @property string|null $updatedAt
* @property string $createdBy
* @property string $updatedBy
* @property string|null $deletedAt
* @property string $deletedBy
* @internal will change in the future
*/
trait RolesUserTrait
{
}
23 changes: 23 additions & 0 deletions src/Middleware/CheckPagePermission.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Miaoxing\App\Middleware;

use Miaoxing\Plugin\Service\User;
use Miaoxing\Services\Middleware\BaseMiddleware;
use Wei\BaseController;

class CheckPagePermission extends BaseMiddleware
{
public function __invoke($next, BaseController $controller = null)
{
if (false === $controller->getOption('requireAuth')) {
return $next();
}

$ret = User::cur()->checkPagePermission($this->req->getMethod(), $this->req->getPathInfo());
if ($ret->isErr()) {
return $ret;
}
return $next();
}
}
83 changes: 83 additions & 0 deletions src/Migration/V20220810105803CreatePermissionTables.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Miaoxing\App\Migration;

use Wei\Migration\BaseMigration;

class V20220810105803CreatePermissionTables extends BaseMigration
{
/**
* {@inheritdoc}
*/
public function up()
{
$this->schema->table('permissions')->tableComment('权限')
->bigId()->comment('编号')
->uBigInt('app_id')->comment('应用编号')
->string('name', 32)->comment('名称')
->string('code', 128)->comment('标识')
->string('description')->comment('描述')
->bool('is_enabled')->comment('是否启用')->defaults(true)
->timestamps()
->userstamps()
->softDeletable()
->exec();

$this->schema->table('roles')->tableComment('角色')
->bigId()->comment('编号')
->uBigInt('app_id')->comment('应用编号')
->uBigInt('parent_id')->comment('父级角色编号')
->uTinyInt('level')->comment('层级')->defaults(1)
->string('name', 32)->comment('名称')
->string('code', 128)->comment('标识')
->string('description')->comment('描述')
->bool('is_enabled')->comment('是否启用')->defaults(true)
->json('actions')->comment('菜单和操作')
->timestamps()
->userstamps()
->softDeletable()
->exec();

$this->schema->table('permissions_roles')->tableComment('角色权限')
->bigId()->comment('编号')
->uBigInt('app_id')->comment('应用编号')
->uBigInt('role_id')->comment('角色编号')
->uBigInt('permission_id')->comment('权限编号')
->timestamps()
->userstamps()
->softDeletable()
->exec();

$this->schema->table('permissions_users')->tableComment('用户权限')
->bigId()
->uBigInt('app_id')->comment('应用编号')
->uBigInt('user_id')->comment('用户编号')
->uBigInt('permission_id')->comment('权限编号')
->timestamps()
->userstamps()
->softDeletable()
->exec();

$this->schema->table('roles_users')->tableComment('用户角色')
->bigId()
->uBigInt('app_id')->comment('应用编号')
->uBigInt('user_id')->comment('用户编号')
->uBigInt('role_id')->comment('角色编号')
->timestamps()
->userstamps()
->softDeletable()
->exec();
}

/**
* {@inheritdoc}
*/
public function down()
{
$this->schema->dropIfExists('permissions');
$this->schema->dropIfExists('roles');
$this->schema->dropIfExists('permissions_roles');
$this->schema->dropIfExists('permissions_users');
$this->schema->dropIfExists('roles_users');
}
}
142 changes: 142 additions & 0 deletions src/Model/HasPermissionTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php

namespace Miaoxing\App\Model;

use Wei\Ret;

/**
* @mixin \LoggerPropMixin
* @mixin \PermissionMapPropMixin
*/
trait HasPermissionTrait
{
public function getPermissionCodes(): array
{
return array_unique(array_merge(
$this->getActionPermissionCodes(),
$this->enabledRoles->enabledPermissions->getAll('code'),
$this->enabledPermissions->getAll('code')
));
}

/**
* @return string[]
*/
public function getActionPermissionCodes(): array
{
if ($this->isSuperAdmin()) {
return ['*'];
}

$actions = $this->enabledRoles->getAll('actions');
return array_unique(array_merge(...$actions));
}

/**
* Check if user have the specified permission
*
* @param string $code
* @return Ret
*/
public function checkPermission(string $code): Ret
{
if ($this->hasPermission($code)) {
return suc();
}
return err('很抱歉,您没有权限执行该操作');
}

/**
* Whether the user have the specified permission
*
* @param string $code
* @return bool
*/
public function hasPermission(string $code): bool
{
// TODO 根据场景实现逐级查找,变量查找按需查找
return in_array($code, $this->getPermissionCodes(), true);
}

/**
* @param string $method
* @param string $path
* @return Ret
*/
public function checkPagePermission(string $method, string $path): Ret
{
if ($this->hasPagePermission($method, $path)) {
return suc();
}
return err('很抱歉,您没有权限执行该操作');
}

public function hasPagePermission(string $method, string $path): bool
{
if ($this->isSuperAdmin()) {
return true;
}

// 1. 获取权限
$permissions = $this->getActionPermissionCodes();
$this->logger->debug('Get user menu permissions', $permissions);

// 2. 转换菜单为页面
$map = $this->permissionMap->getMap();
$map = array_intersect_key($map, array_flip($permissions));
$map = array_unique(array_merge(...array_values($map)));
$this->logger->debug('Get user action permissions', $map);

// 3. 检查当前页面是否在里面
$path = ltrim($path, '/');
if ($this->hasPagePermissionIn($method, $path, $map)) {
return true;
}

// Whether has role permission
$rolePermissionCodes = $this->enabledRoles->enabledPermissions->getAll('code');
if ($this->hasPagePermissionIn($method, $path, $rolePermissionCodes)) {
return true;
}

// Whether has direct permission
$permissionCodes = $this->enabledPermissions->getAll('code');
return $this->hasPagePermissionIn($method, $path, $permissionCodes);
}

/**
* Whether has page permission in the specified permission codes
*
* @param string $method
* @param string $path
* @param array $permissions
* @return bool
*/
protected function hasPagePermissionIn(string $method, string $path, array $permissions): bool
{
$path = ltrim($path, '/');
foreach ($permissions as $permission) {
$parts = explode(' ', $permission, 2);
$apiMethod = $parts[0];
$apiPath = $parts[1] ?? null;

if ($method !== $apiMethod) {
continue;
}

if ($apiPath === $path) {
return true;
}

if (false !== strpos($apiPath, '[')) {
$regex = preg_replace('#[.\+*?[^\]${}=!|:-]#', '\\\\$0', $apiPath);
$regex = str_replace(['\[', '\]'], ['(?P<', '>.+?)'], $regex);
$regex = '#^' . $regex . '$#uUD';
if (preg_match($regex, $path)) {
return true;
}
}
}
return false;
}
}
Loading

0 comments on commit 3f89097

Please sign in to comment.