refactor: permissions plugin
This commit is contained in:
parent
8f58e3ec27
commit
fe12c67ba6
7
packages/plugin-permissions-v04/.npmignore
Normal file
7
packages/plugin-permissions-v04/.npmignore
Normal file
@ -0,0 +1,7 @@
|
||||
node_modules
|
||||
*.log
|
||||
docs
|
||||
__tests__
|
||||
tsconfig.json
|
||||
src
|
||||
.fatherrc.ts
|
13
packages/plugin-permissions-v04/package.json
Normal file
13
packages/plugin-permissions-v04/package.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "@nocobase/plugin-permissions-v04",
|
||||
"version": "0.4.0-alpha.7",
|
||||
"main": "lib/index.js",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@nocobase/actions": "^0.4.0-alpha.7",
|
||||
"@nocobase/database": "^0.4.0-alpha.7",
|
||||
"@nocobase/resourcer": "^0.4.0-alpha.7",
|
||||
"@nocobase/server": "^0.4.0-alpha.7"
|
||||
},
|
||||
"gitHead": "f0b335ac30f29f25c95d7d137655fa64d8d67f1e"
|
||||
}
|
194
packages/plugin-permissions-v04/src/collections/roles.ts
Normal file
194
packages/plugin-permissions-v04/src/collections/roles.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import { TableOptions } from '@nocobase/database';
|
||||
import { ROLE_TYPE_ROOT, ROLE_TYPE_USER, ROLE_TYPE_ANONYMOUS } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'roles',
|
||||
title: '权限组配置',
|
||||
developerMode: true,
|
||||
internal: true,
|
||||
fields: [
|
||||
{
|
||||
interface: 'string',
|
||||
title: '权限组名称',
|
||||
comment: '角色名称',
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'select',
|
||||
title: '角色类型',
|
||||
type: 'integer',
|
||||
name: 'type',
|
||||
developerMode: true,
|
||||
dataSource: [
|
||||
{ value: ROLE_TYPE_ROOT, label: '系统角色' },
|
||||
{ value: ROLE_TYPE_ANONYMOUS, label: '匿名角色' },
|
||||
{ value: ROLE_TYPE_USER, label: '自定义角色' },
|
||||
],
|
||||
defaultValue: ROLE_TYPE_USER,
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
interface: 'boolean',
|
||||
title: '默认权限组',
|
||||
type: 'radio',
|
||||
name: 'default',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
interface: 'textarea',
|
||||
title: '描述',
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
},
|
||||
},
|
||||
// TODO(feature): 用户组后续考虑
|
||||
// TODO(feature): 用户表应通过插件配置关联,考虑到今后会有多账户系统的情况
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '用户',
|
||||
comment: '关联的用户表',
|
||||
type: 'belongsToMany',
|
||||
name: 'users',
|
||||
target: 'users',
|
||||
through: 'users_roles'
|
||||
},
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '数据表',
|
||||
comment: '包含的以数据表分组的权限集',
|
||||
type: 'belongsToMany',
|
||||
name: 'collections',
|
||||
through: 'permissions',
|
||||
targetKey: 'name'
|
||||
},
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '页面',
|
||||
type: 'belongsToMany',
|
||||
name: 'menus',
|
||||
target: 'menus',
|
||||
through: 'menus_permissions',
|
||||
},
|
||||
{
|
||||
comment: '权限集(方便访问)',
|
||||
type: 'hasMany',
|
||||
name: 'permissions'
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'list',
|
||||
title: '查看',
|
||||
},
|
||||
// {
|
||||
// type: 'get',
|
||||
// name: 'get',
|
||||
// title: '详情',
|
||||
// },
|
||||
{
|
||||
type: 'create',
|
||||
name: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
type: 'update',
|
||||
name: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
type: 'destroy',
|
||||
name: 'destroy',
|
||||
title: '删除',
|
||||
filter: {
|
||||
type: ROLE_TYPE_USER,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
],
|
||||
views_v2: [
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
name: 'table',
|
||||
title: '全部数据',
|
||||
labelField: 'title',
|
||||
actions: [
|
||||
{
|
||||
name: 'create',
|
||||
type: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
name: 'destroy',
|
||||
type: 'destroy',
|
||||
title: '删除',
|
||||
},
|
||||
],
|
||||
fields: ['title', 'description', 'default'],
|
||||
detailsOpenMode: 'drawer', // window
|
||||
details: ['descriptions', 'collections', 'pages'],
|
||||
sort: ['id'],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'descriptions',
|
||||
name: 'descriptions',
|
||||
title: '详情',
|
||||
fields: ['title', 'description', 'default'],
|
||||
actions: [
|
||||
{
|
||||
name: 'update',
|
||||
type: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'form',
|
||||
name: 'form',
|
||||
title: '表单',
|
||||
fields: ['title', 'description', 'default'],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
dataSourceType: 'association',
|
||||
name: 'collections',
|
||||
title: '数据操作权限',
|
||||
targetViewName: 'permissions_table',
|
||||
targetFieldName: 'collections',
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
dataSourceType: 'association',
|
||||
name: 'pages',
|
||||
title: '菜单和页面权限',
|
||||
targetViewName: 'permissions_table',
|
||||
targetFieldName: 'menus',
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
166
packages/plugin-permissions-v04/src/server.ts
Normal file
166
packages/plugin-permissions-v04/src/server.ts
Normal file
@ -0,0 +1,166 @@
|
||||
import path from 'path';
|
||||
import { Op } from 'sequelize';
|
||||
import { Application } from '@nocobase/server';
|
||||
import { Operator } from '@nocobase/database';
|
||||
import * as collectionsRolesActions from './actions/collections.roles';
|
||||
import * as rolesCollectionsActions from './actions/roles.collections';
|
||||
import AccessController, { PermissionParams } from './AccessController';
|
||||
|
||||
// API
|
||||
// const permissions = ctx.app.getPluginInstance('permissions');
|
||||
// const result: boolean = permissions.can(key, options);
|
||||
|
||||
|
||||
|
||||
export class Permissions {
|
||||
readonly app: Application;
|
||||
readonly options: any;
|
||||
|
||||
constructor(app: Application, options) {
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
|
||||
const { database, resourcer } = app;
|
||||
|
||||
database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
Object.keys(collectionsRolesActions).forEach(actionName => {
|
||||
resourcer.registerActionHandler(`collections.roles:${actionName}`, collectionsRolesActions[actionName]);
|
||||
});
|
||||
|
||||
Object.keys(rolesCollectionsActions).forEach(actionName => {
|
||||
resourcer.registerActionHandler(`roles.collections:${actionName}`, rolesCollectionsActions[actionName]);
|
||||
});
|
||||
|
||||
database.getModel('collections').addHook('afterCreate', async (model: any, options) => {
|
||||
await model.updateAssociations({
|
||||
scopes: [
|
||||
{
|
||||
title: '全部数据',
|
||||
filter: {},
|
||||
locked: true
|
||||
},
|
||||
{
|
||||
title: '用户自己的数据',
|
||||
filter: {
|
||||
"created_by_id.$currentUser": true,
|
||||
},
|
||||
locked: true
|
||||
},
|
||||
]
|
||||
}, options);
|
||||
});
|
||||
|
||||
database.getModel('users').addHook('afterCreate', async(model, options) => {
|
||||
const { transaction = await database.sequelize.transaction() } = options;
|
||||
const Role = database.getModel('roles');
|
||||
const defaultRole = await Role.findOne({ where: { default: true }, transaction });
|
||||
if (defaultRole) {
|
||||
// @ts-ignore
|
||||
await model.addRole(defaultRole, { transaction });
|
||||
}
|
||||
if (!options.transaction) {
|
||||
await transaction.commit();
|
||||
}
|
||||
});
|
||||
|
||||
// 针对“自己创建的” scope 添加特殊的操作符以生成查询条件
|
||||
if (!Operator.has('$currentUser')) {
|
||||
Operator.register('$currentUser', (value, { ctx }) => {
|
||||
const user = ctx.state.currentUser;
|
||||
return { [Op.eq]: user[user.constructor.primaryKeyAttribute] };
|
||||
});
|
||||
}
|
||||
|
||||
resourcer.use(this.injection);
|
||||
resourcer.use(this.middleware);
|
||||
|
||||
resourcer.use(async (ctx, next) => {
|
||||
const { resourceName } = ctx.action.params;
|
||||
if (resourceName === 'action_logs' && !await ctx.ac.isRoot()) {
|
||||
const collections = await ctx.ac.getCollections();
|
||||
ctx.action.mergeParams({
|
||||
filter: {
|
||||
'collection_name.in': collections
|
||||
},
|
||||
});
|
||||
}
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
injection = async (ctx, next) => {
|
||||
ctx.ac = new AccessController(ctx);
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
middleware = async (ctx, next) => {
|
||||
const {
|
||||
associatedName,
|
||||
resourceField,
|
||||
resourceName,
|
||||
actionName
|
||||
} = ctx.action.params;
|
||||
|
||||
let result: PermissionParams = false;
|
||||
|
||||
// 关系数据的权限
|
||||
if (associatedName && resourceField) {
|
||||
if (resourceField.options.id && resourceField.options.interface === 'subTable') {
|
||||
if (await ctx.ac.isRoot()) {
|
||||
return next();
|
||||
}
|
||||
const permissions = await ctx.ac.getPermissions();
|
||||
const FieldPermission = ctx.db.getModel('fields_permissions');
|
||||
const fps = await FieldPermission.findAll({
|
||||
where: {
|
||||
field_id: resourceField.options.id,
|
||||
permission_id: {
|
||||
[Op.in]: permissions.map(p => p.id),
|
||||
}
|
||||
},
|
||||
});
|
||||
if (fps.length) {
|
||||
for (const fp of fps) {
|
||||
if (Array.isArray(fp.actions) && fp.actions.includes(`${resourceField.options.collection_name}:${actionName}`)) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
return this.reject(ctx);
|
||||
}
|
||||
}
|
||||
result = await ctx.ac.can(resourceField.options.target).act(actionName).any();
|
||||
} else {
|
||||
result = await ctx.ac.can(resourceName).act(actionName).any();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return this.reject(ctx);
|
||||
}
|
||||
|
||||
if (result === true) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ctx.action.mergeParams({
|
||||
filter: result.filter,
|
||||
// TODO: 在 fields 改进之前,先注释掉
|
||||
// fields: result.fields.map(item => item.get('field').get('name'))
|
||||
});
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
reject(ctx) {
|
||||
ctx.throw(404);
|
||||
}
|
||||
}
|
||||
|
||||
export default async function (options = {}) {
|
||||
const instance = new Permissions(this, options);
|
||||
|
||||
return instance;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
import { TableOptions } from '@nocobase/database';
|
||||
|
||||
export default {
|
||||
name: 'action_permissions',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'actionName',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'scope',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'collection',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'role',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'user',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'fieldPermissions',
|
||||
target: 'field_permissions',
|
||||
foreignKey: 'action_permission_id',
|
||||
},
|
||||
{
|
||||
type: 'belongsToMany',
|
||||
name: 'fields',
|
||||
through: 'field_permissions',
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
@ -1,194 +1,24 @@
|
||||
import { TableOptions } from '@nocobase/database';
|
||||
import { ROLE_TYPE_ROOT, ROLE_TYPE_USER, ROLE_TYPE_ANONYMOUS } from '../constants';
|
||||
|
||||
export default {
|
||||
name: 'roles',
|
||||
title: '权限组配置',
|
||||
developerMode: true,
|
||||
internal: true,
|
||||
sortable: 'sort',
|
||||
fields: [
|
||||
{
|
||||
interface: 'string',
|
||||
title: '权限组名称',
|
||||
comment: '角色名称',
|
||||
type: 'uid',
|
||||
name: 'name',
|
||||
primaryKey: true,
|
||||
prefix: 't_',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
interface: 'select',
|
||||
title: '角色类型',
|
||||
type: 'integer',
|
||||
name: 'type',
|
||||
developerMode: true,
|
||||
dataSource: [
|
||||
{ value: ROLE_TYPE_ROOT, label: '系统角色' },
|
||||
{ value: ROLE_TYPE_ANONYMOUS, label: '匿名角色' },
|
||||
{ value: ROLE_TYPE_USER, label: '自定义角色' },
|
||||
],
|
||||
defaultValue: ROLE_TYPE_USER,
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInDetail: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
interface: 'boolean',
|
||||
title: '默认权限组',
|
||||
type: 'radio',
|
||||
name: 'default',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
interface: 'textarea',
|
||||
title: '描述',
|
||||
type: 'text',
|
||||
name: 'description',
|
||||
component: {
|
||||
showInTable: true,
|
||||
showInForm: true,
|
||||
showInDetail: true,
|
||||
},
|
||||
},
|
||||
// TODO(feature): 用户组后续考虑
|
||||
// TODO(feature): 用户表应通过插件配置关联,考虑到今后会有多账户系统的情况
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '用户',
|
||||
comment: '关联的用户表',
|
||||
type: 'belongsToMany',
|
||||
name: 'users',
|
||||
target: 'users',
|
||||
through: 'users_roles'
|
||||
},
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '数据表',
|
||||
comment: '包含的以数据表分组的权限集',
|
||||
type: 'belongsToMany',
|
||||
name: 'collections',
|
||||
through: 'permissions',
|
||||
targetKey: 'name'
|
||||
},
|
||||
{
|
||||
interface: 'linkTo',
|
||||
title: '页面',
|
||||
type: 'belongsToMany',
|
||||
name: 'menus',
|
||||
target: 'menus',
|
||||
through: 'menus_permissions',
|
||||
},
|
||||
{
|
||||
comment: '权限集(方便访问)',
|
||||
type: 'hasMany',
|
||||
name: 'permissions'
|
||||
},
|
||||
],
|
||||
actions: [
|
||||
{
|
||||
type: 'list',
|
||||
name: 'list',
|
||||
title: '查看',
|
||||
},
|
||||
// {
|
||||
// type: 'get',
|
||||
// name: 'get',
|
||||
// title: '详情',
|
||||
// },
|
||||
{
|
||||
type: 'create',
|
||||
name: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
type: 'update',
|
||||
name: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
type: 'destroy',
|
||||
name: 'destroy',
|
||||
title: '删除',
|
||||
filter: {
|
||||
type: ROLE_TYPE_USER,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
],
|
||||
views_v2: [
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
name: 'table',
|
||||
title: '全部数据',
|
||||
labelField: 'title',
|
||||
actions: [
|
||||
{
|
||||
name: 'create',
|
||||
type: 'create',
|
||||
title: '新增',
|
||||
viewName: 'form',
|
||||
},
|
||||
{
|
||||
name: 'destroy',
|
||||
type: 'destroy',
|
||||
title: '删除',
|
||||
},
|
||||
],
|
||||
fields: ['title', 'description', 'default'],
|
||||
detailsOpenMode: 'drawer', // window
|
||||
details: ['descriptions', 'collections', 'pages'],
|
||||
sort: ['id'],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'descriptions',
|
||||
name: 'descriptions',
|
||||
title: '详情',
|
||||
fields: ['title', 'description', 'default'],
|
||||
actions: [
|
||||
{
|
||||
name: 'update',
|
||||
type: 'update',
|
||||
title: '编辑',
|
||||
viewName: 'form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'form',
|
||||
name: 'form',
|
||||
title: '表单',
|
||||
fields: ['title', 'description', 'default'],
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
dataSourceType: 'association',
|
||||
name: 'collections',
|
||||
title: '数据操作权限',
|
||||
targetViewName: 'permissions_table',
|
||||
targetFieldName: 'collections',
|
||||
},
|
||||
{
|
||||
developerMode: true,
|
||||
type: 'table',
|
||||
dataSourceType: 'association',
|
||||
name: 'pages',
|
||||
title: '菜单和页面权限',
|
||||
targetViewName: 'permissions_table',
|
||||
targetFieldName: 'menus',
|
||||
through: 'roles_users'
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
||||
|
20
packages/plugin-permissions/src/collections/scopes.ts
Normal file
20
packages/plugin-permissions/src/collections/scopes.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { TableOptions } from '@nocobase/database';
|
||||
|
||||
export default {
|
||||
name: 'scopes',
|
||||
sortable: 'sort',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'title',
|
||||
},
|
||||
{
|
||||
type: 'json',
|
||||
name: 'filter',
|
||||
},
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'collection',
|
||||
},
|
||||
],
|
||||
} as TableOptions;
|
@ -1,166 +1,12 @@
|
||||
import path from 'path';
|
||||
import { Op } from 'sequelize';
|
||||
import { Application } from '@nocobase/server';
|
||||
import { Operator } from '@nocobase/database';
|
||||
import * as collectionsRolesActions from './actions/collections.roles';
|
||||
import * as rolesCollectionsActions from './actions/roles.collections';
|
||||
import AccessController, { PermissionParams } from './AccessController';
|
||||
import Database from '@nocobase/database';
|
||||
import Resourcer from '@nocobase/resourcer';
|
||||
|
||||
// API
|
||||
// const permissions = ctx.app.getPluginInstance('permissions');
|
||||
// const result: boolean = permissions.can(key, options);
|
||||
export default async function () {
|
||||
const database: Database = this.database;
|
||||
const resourcer: Resourcer = this.resourcer;
|
||||
|
||||
|
||||
|
||||
export class Permissions {
|
||||
readonly app: Application;
|
||||
readonly options: any;
|
||||
|
||||
constructor(app: Application, options) {
|
||||
this.app = app;
|
||||
this.options = options;
|
||||
|
||||
const { database, resourcer } = app;
|
||||
|
||||
database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
|
||||
Object.keys(collectionsRolesActions).forEach(actionName => {
|
||||
resourcer.registerActionHandler(`collections.roles:${actionName}`, collectionsRolesActions[actionName]);
|
||||
});
|
||||
|
||||
Object.keys(rolesCollectionsActions).forEach(actionName => {
|
||||
resourcer.registerActionHandler(`roles.collections:${actionName}`, rolesCollectionsActions[actionName]);
|
||||
});
|
||||
|
||||
database.getModel('collections').addHook('afterCreate', async (model: any, options) => {
|
||||
await model.updateAssociations({
|
||||
scopes: [
|
||||
{
|
||||
title: '全部数据',
|
||||
filter: {},
|
||||
locked: true
|
||||
},
|
||||
{
|
||||
title: '用户自己的数据',
|
||||
filter: {
|
||||
"created_by_id.$currentUser": true,
|
||||
},
|
||||
locked: true
|
||||
},
|
||||
]
|
||||
}, options);
|
||||
});
|
||||
|
||||
database.getModel('users').addHook('afterCreate', async(model, options) => {
|
||||
const { transaction = await database.sequelize.transaction() } = options;
|
||||
const Role = database.getModel('roles');
|
||||
const defaultRole = await Role.findOne({ where: { default: true }, transaction });
|
||||
if (defaultRole) {
|
||||
// @ts-ignore
|
||||
await model.addRole(defaultRole, { transaction });
|
||||
}
|
||||
if (!options.transaction) {
|
||||
await transaction.commit();
|
||||
}
|
||||
});
|
||||
|
||||
// 针对“自己创建的” scope 添加特殊的操作符以生成查询条件
|
||||
if (!Operator.has('$currentUser')) {
|
||||
Operator.register('$currentUser', (value, { ctx }) => {
|
||||
const user = ctx.state.currentUser;
|
||||
return { [Op.eq]: user[user.constructor.primaryKeyAttribute] };
|
||||
});
|
||||
}
|
||||
|
||||
resourcer.use(this.injection);
|
||||
resourcer.use(this.middleware);
|
||||
|
||||
resourcer.use(async (ctx, next) => {
|
||||
const { resourceName } = ctx.action.params;
|
||||
if (resourceName === 'action_logs' && !await ctx.ac.isRoot()) {
|
||||
const collections = await ctx.ac.getCollections();
|
||||
ctx.action.mergeParams({
|
||||
filter: {
|
||||
'collection_name.in': collections
|
||||
},
|
||||
});
|
||||
}
|
||||
await next();
|
||||
});
|
||||
}
|
||||
|
||||
injection = async (ctx, next) => {
|
||||
ctx.ac = new AccessController(ctx);
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
middleware = async (ctx, next) => {
|
||||
const {
|
||||
associatedName,
|
||||
resourceField,
|
||||
resourceName,
|
||||
actionName
|
||||
} = ctx.action.params;
|
||||
|
||||
let result: PermissionParams = false;
|
||||
|
||||
// 关系数据的权限
|
||||
if (associatedName && resourceField) {
|
||||
if (resourceField.options.id && resourceField.options.interface === 'subTable') {
|
||||
if (await ctx.ac.isRoot()) {
|
||||
return next();
|
||||
}
|
||||
const permissions = await ctx.ac.getPermissions();
|
||||
const FieldPermission = ctx.db.getModel('fields_permissions');
|
||||
const fps = await FieldPermission.findAll({
|
||||
where: {
|
||||
field_id: resourceField.options.id,
|
||||
permission_id: {
|
||||
[Op.in]: permissions.map(p => p.id),
|
||||
}
|
||||
},
|
||||
});
|
||||
if (fps.length) {
|
||||
for (const fp of fps) {
|
||||
if (Array.isArray(fp.actions) && fp.actions.includes(`${resourceField.options.collection_name}:${actionName}`)) {
|
||||
return next();
|
||||
}
|
||||
}
|
||||
return this.reject(ctx);
|
||||
}
|
||||
}
|
||||
result = await ctx.ac.can(resourceField.options.target).act(actionName).any();
|
||||
} else {
|
||||
result = await ctx.ac.can(resourceName).act(actionName).any();
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return this.reject(ctx);
|
||||
}
|
||||
|
||||
if (result === true) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ctx.action.mergeParams({
|
||||
filter: result.filter,
|
||||
// TODO: 在 fields 改进之前,先注释掉
|
||||
// fields: result.fields.map(item => item.get('field').get('name'))
|
||||
});
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
reject(ctx) {
|
||||
ctx.throw(404);
|
||||
}
|
||||
}
|
||||
|
||||
export default async function (options = {}) {
|
||||
const instance = new Permissions(this, options);
|
||||
|
||||
return instance;
|
||||
database.import({
|
||||
directory: path.resolve(__dirname, 'collections'),
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user