mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 07:25:15 +00:00
feat: route permissions (#58)
* feat: routes permissions * fix: try to fix roles.pages action * 多态关联 * bugfix * fix: auto generate when option value is undefined or null * feat: add pages' permissions saving/listing * feat: add permission filter for getRoutes * roles description * feat: get root permissions all true and create user with default role * feat: roles.collections list output with permission * add permissions description * fix: add context to parseApiJson * fix: typo * 小细节补充 Co-authored-by: mytharcher <mytharcher@gmail.com>
This commit is contained in:
parent
bd756a6a5c
commit
301229ef88
@ -1,3 +1,11 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
global.sync = {
|
||||||
|
force: true,
|
||||||
|
alter: {
|
||||||
|
drop: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
import api from '../app';
|
import api from '../app';
|
||||||
import Database from '@nocobase/database';
|
import Database from '@nocobase/database';
|
||||||
|
|
||||||
@ -135,6 +143,9 @@ const data = [
|
|||||||
const tables = database.getTables([]);
|
const tables = database.getTables([]);
|
||||||
for (let table of tables) {
|
for (let table of tables) {
|
||||||
console.log(table.getName());
|
console.log(table.getName());
|
||||||
|
if (table.getName() === 'roles') {
|
||||||
|
console.log('roles', table.getOptions())
|
||||||
|
}
|
||||||
await Collection.import(table.getOptions(), { update: true, migrate: false });
|
await Collection.import(table.getOptions(), { update: true, migrate: false });
|
||||||
}
|
}
|
||||||
await Page.import(data);
|
await Page.import(data);
|
||||||
@ -182,6 +193,7 @@ const data = [
|
|||||||
const roles = await Role.bulkCreate([
|
const roles = await Role.bulkCreate([
|
||||||
{ title: '系统开发组', type: -1 },
|
{ title: '系统开发组', type: -1 },
|
||||||
{ title: '匿名用户组', type: 0 },
|
{ title: '匿名用户组', type: 0 },
|
||||||
|
{ title: '普通用户组', default: true },
|
||||||
]);
|
]);
|
||||||
await roles[0].updateAssociations({
|
await roles[0].updateAssociations({
|
||||||
users: user
|
users: user
|
||||||
|
@ -8,6 +8,8 @@ import get from 'lodash/get';
|
|||||||
import './style.less';
|
import './style.less';
|
||||||
import Field from '../Field';
|
import Field from '../Field';
|
||||||
import cloneDeep from 'lodash/cloneDeep';
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
import { Checkbox, message } from 'antd';
|
||||||
|
import api from '@/api-client';
|
||||||
|
|
||||||
export const SortableItem = sortableElement(props => <tr {...props} />);
|
export const SortableItem = sortableElement(props => <tr {...props} />);
|
||||||
export const SortableContainer = sortableContainer(props => <tbody {...props} />);
|
export const SortableContainer = sortableContainer(props => <tbody {...props} />);
|
||||||
@ -58,11 +60,38 @@ export const components = ({data = {}, rowKey, mutate, onMoved, isFieldComponent
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function fields2columns(fields) {
|
export function fields2columns(fields, ctx: any = {}) {
|
||||||
const columns: any[] = fields.map(item => {
|
const columns: any[] = fields.map(item => {
|
||||||
const field = cloneDeep(item);
|
const field = cloneDeep(item);
|
||||||
field.render = (value, record) => field.interface === 'sort' ? <DragHandle/> : <Field data={record} viewType={'table'} schema={field} value={value}/>;
|
field.render = (value, record) => field.interface === 'sort' ? <DragHandle/> : <Field data={record} viewType={'table'} schema={field} value={value}/>;
|
||||||
field.className = `${field.className||''} noco-field-${field.interface}`;
|
field.className = `${field.className||''} noco-field-${field.interface}`;
|
||||||
|
if (field.editable && field.interface === 'boolean') {
|
||||||
|
field.title = (
|
||||||
|
<span>
|
||||||
|
<Checkbox onChange={async (e) => {
|
||||||
|
try {
|
||||||
|
await api.resource(field.resource).update({
|
||||||
|
associatedKey: ctx.associatedKey,
|
||||||
|
// resourceKey: data.id,
|
||||||
|
// tableName: data.tableName||'pages',
|
||||||
|
values: {
|
||||||
|
accessible: e.target.checked,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
message.success('保存成功');
|
||||||
|
if (ctx.refresh) {
|
||||||
|
ctx.refresh();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error('保存失败');
|
||||||
|
}
|
||||||
|
|
||||||
|
}}/>
|
||||||
|
{' '}
|
||||||
|
{field.title}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
...(field.component||{}),
|
...(field.component||{}),
|
||||||
|
@ -154,7 +154,7 @@ export function Table(props: TableProps) {
|
|||||||
indicator: icon,
|
indicator: icon,
|
||||||
// className: 'spinning--absolute m32',
|
// className: 'spinning--absolute m32',
|
||||||
}}
|
}}
|
||||||
columns={fields2columns(fields)}
|
columns={fields2columns(fields, {associatedKey, refresh})}
|
||||||
dataSource={data?.list||(data as any)}
|
dataSource={data?.list||(data as any)}
|
||||||
onChange={(pagination, filters, sorter, extra) => {
|
onChange={(pagination, filters, sorter, extra) => {
|
||||||
run({...params[0], sorter});
|
run({...params[0], sorter});
|
||||||
|
@ -68,6 +68,8 @@ export default class Database {
|
|||||||
|
|
||||||
protected hooks = {};
|
protected hooks = {};
|
||||||
|
|
||||||
|
protected extTableOptions = new Map<string, any>();
|
||||||
|
|
||||||
constructor(options: DatabaseOptions) {
|
constructor(options: DatabaseOptions) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.sequelize = new Sequelize(options);
|
this.sequelize = new Sequelize(options);
|
||||||
@ -94,10 +96,21 @@ export default class Database {
|
|||||||
files.forEach((file: string) => {
|
files.forEach((file: string) => {
|
||||||
const result = requireModule(file);
|
const result = requireModule(file);
|
||||||
if (result instanceof Extend) {
|
if (result instanceof Extend) {
|
||||||
const table = this.extend(result.tableOptions, result.mergeOptions);
|
// 如果还没初始化,extend 的先暂存起来,后续处理
|
||||||
tables.set(table.getName(), table);
|
if (!this.tables.has(result.tableOptions.name)) {
|
||||||
|
this.extTableOptions.set(result.tableOptions.name, result);
|
||||||
|
} else {
|
||||||
|
const table = this.extend(result.tableOptions, result.mergeOptions);
|
||||||
|
tables.set(table.getName(), table);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const table = this.extend(typeof result === 'function' ? result(this) : result);
|
let table = this.extend(typeof result === 'function' ? result(this) : result);
|
||||||
|
// 如果有未处理的 extend 取回来合并
|
||||||
|
if (this.extTableOptions.has(table.getName())) {
|
||||||
|
const result = this.extTableOptions.get(table.getName());
|
||||||
|
table = this.extend(result.tableOptions, result.mergeOptions);
|
||||||
|
this.extTableOptions.delete(table.getName());
|
||||||
|
}
|
||||||
tables.set(table.getName(), table);
|
tables.set(table.getName(), table);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -18,7 +18,7 @@ export default async function (model: FieldModel, options) {
|
|||||||
const dataSource = model.get('dataSource');
|
const dataSource = model.get('dataSource');
|
||||||
if (Array.isArray(dataSource)) {
|
if (Array.isArray(dataSource)) {
|
||||||
model.set('dataSource', dataSource.map(item => {
|
model.set('dataSource', dataSource.map(item => {
|
||||||
if (!item.value) {
|
if (item.value === null || typeof item.value === 'undefined') {
|
||||||
item.value = generateValueName();
|
item.value = generateValueName();
|
||||||
}
|
}
|
||||||
return {...item};
|
return {...item};
|
||||||
|
@ -40,6 +40,21 @@ export default async function getRoutes(ctx, next) {
|
|||||||
const database: Database = ctx.database;
|
const database: Database = ctx.database;
|
||||||
const Page = database.getModel('pages');
|
const Page = database.getModel('pages');
|
||||||
const Collection = database.getModel('collections');
|
const Collection = database.getModel('collections');
|
||||||
|
const RoutePermission = database.getModel('routes_permissions');
|
||||||
|
const roles = await ctx.ac.getRoles();
|
||||||
|
// TODO(optimize): isRoot 的判断需要在内部完成,尽量不要交给调用者
|
||||||
|
const isRoot = ctx.ac.constructor.isRoot(roles);
|
||||||
|
const routesPermissionsMap = new Map();
|
||||||
|
if (!isRoot) {
|
||||||
|
const routesPermissions = await RoutePermission.findAll({
|
||||||
|
where: {
|
||||||
|
role_id: roles.map(({ id }) => id)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
routesPermissions.forEach(permission => {
|
||||||
|
routesPermissionsMap.set(`${permission.routable_type}:${permission.routable_id}`, permission);
|
||||||
|
});
|
||||||
|
}
|
||||||
let pages = await Page.findAll(Page.parseApiJson(ctx.state.developerMode ? {
|
let pages = await Page.findAll(Page.parseApiJson(ctx.state.developerMode ? {
|
||||||
filter: {
|
filter: {
|
||||||
},
|
},
|
||||||
@ -52,6 +67,15 @@ export default async function getRoutes(ctx, next) {
|
|||||||
}));
|
}));
|
||||||
const items = [];
|
const items = [];
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
|
if (!isRoot
|
||||||
|
&& !routesPermissionsMap.has(`pages:${page.id}`)
|
||||||
|
// 以下路径先临时处理
|
||||||
|
&& page.get('path') !== '/'
|
||||||
|
&& page.get('path') !== '/register'
|
||||||
|
&& page.get('path') !== '/login'
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
items.push(page.toJSON());
|
items.push(page.toJSON());
|
||||||
if (page.get('path') === '/collections') {
|
if (page.get('path') === '/collections') {
|
||||||
const collections = await Collection.findAll(Collection.parseApiJson(ctx.state.developerMode ? {
|
const collections = await Collection.findAll(Collection.parseApiJson(ctx.state.developerMode ? {
|
||||||
@ -67,6 +91,9 @@ export default async function getRoutes(ctx, next) {
|
|||||||
sort: ['sort'],
|
sort: ['sort'],
|
||||||
}));
|
}));
|
||||||
for (const collection of collections) {
|
for (const collection of collections) {
|
||||||
|
if (!isRoot && !routesPermissionsMap.has(`collections:${collection.id}`)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const pageId = `collection-${collection.id}`;
|
const pageId = `collection-${collection.id}`;
|
||||||
items.push({
|
items.push({
|
||||||
id: pageId,
|
id: pageId,
|
||||||
@ -90,6 +117,9 @@ export default async function getRoutes(ctx, next) {
|
|||||||
});
|
});
|
||||||
if (views.length > 1) {
|
if (views.length > 1) {
|
||||||
for (const view of views) {
|
for (const view of views) {
|
||||||
|
if (!isRoot && !routesPermissionsMap.has(`views:${view.id}`)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
items.push({
|
items.push({
|
||||||
id: `view-${view.get('id')}`,
|
id: `view-${view.get('id')}`,
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
|
@ -201,7 +201,7 @@ export default async (ctx, next) => {
|
|||||||
}));
|
}));
|
||||||
let throughName;
|
let throughName;
|
||||||
const { resourceKey: resourceKey2, associatedName, resourceFieldName, associatedKey } = values;
|
const { resourceKey: resourceKey2, associatedName, resourceFieldName, associatedKey } = values;
|
||||||
const permissions = await ctx.can(resourceName).permissions();
|
const permissions = await ctx.ac.can(resourceName).permissions();
|
||||||
ctx.listFields = [];
|
ctx.listFields = [];
|
||||||
ctx.createFields = [];
|
ctx.createFields = [];
|
||||||
ctx.updateFields = [];
|
ctx.updateFields = [];
|
||||||
@ -209,7 +209,7 @@ export default async (ctx, next) => {
|
|||||||
for (const action of permissions.actions) {
|
for (const action of permissions.actions) {
|
||||||
ctx.allowedActions.push(action.name);
|
ctx.allowedActions.push(action.name);
|
||||||
}
|
}
|
||||||
console.log(ctx.allowedActions);
|
// console.log(ctx.allowedActions);
|
||||||
for (const permissionField of permissions.fields) {
|
for (const permissionField of permissions.fields) {
|
||||||
const pfc = permissionField.actions;
|
const pfc = permissionField.actions;
|
||||||
if (pfc.includes(`${resourceName}:list`)) {
|
if (pfc.includes(`${resourceName}:list`)) {
|
||||||
@ -222,11 +222,11 @@ export default async (ctx, next) => {
|
|||||||
ctx.updateFields.push(permissionField.field_id);
|
ctx.updateFields.push(permissionField.field_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log({
|
// console.log({
|
||||||
listFields: ctx.listFields,
|
// listFields: ctx.listFields,
|
||||||
createFields: ctx.createFields,
|
// createFields: ctx.createFields,
|
||||||
updateFields: ctx.updateFields,
|
// updateFields: ctx.updateFields,
|
||||||
})
|
// })
|
||||||
if (associatedName) {
|
if (associatedName) {
|
||||||
const table = ctx.db.getTable(associatedName);
|
const table = ctx.db.getTable(associatedName);
|
||||||
const resourceField = table.getField(resourceFieldName);
|
const resourceField = table.getField(resourceFieldName);
|
||||||
@ -420,6 +420,23 @@ export default async (ctx, next) => {
|
|||||||
"showInDetail": true
|
"showInDetail": true
|
||||||
},
|
},
|
||||||
"dataIndex": ["title"]
|
"dataIndex": ["title"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "描述",
|
||||||
|
"name": "permissions[0].description",
|
||||||
|
"interface": "string",
|
||||||
|
"type": "string",
|
||||||
|
"parent_id": null,
|
||||||
|
"required": true,
|
||||||
|
"developerMode": false,
|
||||||
|
"component": {
|
||||||
|
"type": "string",
|
||||||
|
"className": "drag-visible",
|
||||||
|
"showInForm": true,
|
||||||
|
"showInTable": true,
|
||||||
|
"showInDetail": true
|
||||||
|
},
|
||||||
|
"dataIndex": ["permissions", 0, 'description']
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -496,7 +513,7 @@ export default async (ctx, next) => {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
let allowedUpdate = false;
|
let allowedUpdate = false;
|
||||||
if (view.type === 'details' && await ctx.can(resourceName).act('update').one(resourceKey2)) {
|
if (view.type === 'details' && await ctx.ac.can(resourceName).act('update').one(resourceKey2)) {
|
||||||
allowedUpdate = true;
|
allowedUpdate = true;
|
||||||
}
|
}
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
|
@ -3,9 +3,8 @@ import Database from '@nocobase/database';
|
|||||||
import { flatToTree } from '../utils';
|
import { flatToTree } from '../utils';
|
||||||
import { Op } from 'sequelize';
|
import { Op } from 'sequelize';
|
||||||
|
|
||||||
export async function list(ctx: actions.Context, next: actions.Next) {
|
async function getRoutes(ctx) {
|
||||||
const database: Database = ctx.db;
|
const database: Database = ctx.db;
|
||||||
const { associatedKey } = ctx.action.params;
|
|
||||||
const Page = database.getModel('pages');
|
const Page = database.getModel('pages');
|
||||||
const Collection = database.getModel('collections');
|
const Collection = database.getModel('collections');
|
||||||
let pages = await Page.findAll(Page.parseApiJson(ctx.state.developerMode ? {
|
let pages = await Page.findAll(Page.parseApiJson(ctx.state.developerMode ? {
|
||||||
@ -21,6 +20,80 @@ export async function list(ctx: actions.Context, next: actions.Next) {
|
|||||||
sort: ['sort'],
|
sort: ['sort'],
|
||||||
}));
|
}));
|
||||||
const items = [];
|
const items = [];
|
||||||
|
for (const page of pages) {
|
||||||
|
items.push({
|
||||||
|
routable_type: 'pages',
|
||||||
|
routable_id: page.id,
|
||||||
|
});
|
||||||
|
if (page.get('path') === '/collections') {
|
||||||
|
const collections = await Collection.findAll(Collection.parseApiJson(ctx.state.developerMode ? {
|
||||||
|
filter: {
|
||||||
|
showInDataMenu: true,
|
||||||
|
},
|
||||||
|
sort: ['sort'],
|
||||||
|
}: {
|
||||||
|
filter: {
|
||||||
|
developerMode: {'$isFalsy': true},
|
||||||
|
showInDataMenu: true,
|
||||||
|
},
|
||||||
|
sort: ['sort'],
|
||||||
|
}));
|
||||||
|
for (const collection of collections) {
|
||||||
|
items.push({
|
||||||
|
routable_type: 'collections',
|
||||||
|
routable_id: collection.id,
|
||||||
|
});
|
||||||
|
const views = await collection.getViews({
|
||||||
|
where: {
|
||||||
|
[Op.or]: [
|
||||||
|
{ showInDataMenu: true },
|
||||||
|
{ default: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
order: [['sort', 'asc']]
|
||||||
|
});
|
||||||
|
if (views.length > 1) {
|
||||||
|
for (const view of views) {
|
||||||
|
items.push({
|
||||||
|
routable_id: view.id,
|
||||||
|
routable_type: 'views',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function list(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const database: Database = ctx.db;
|
||||||
|
const { associatedKey, associated } = ctx.action.params;
|
||||||
|
const Page = database.getModel('pages');
|
||||||
|
const Collection = database.getModel('collections');
|
||||||
|
// TODO(optimize): isRoot 的判断需要在内部完成,尽量不要交给调用者
|
||||||
|
const isRoot = ctx.ac.constructor.isRoot(associated);
|
||||||
|
const routesPermissionsMap = new Map();
|
||||||
|
if (!isRoot) {
|
||||||
|
const routesPermissions = await associated.getRoutes();
|
||||||
|
|
||||||
|
routesPermissions.forEach(permission => {
|
||||||
|
routesPermissionsMap.set(`${permission.routable_type}:${permission.routable_id}`, permission);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let pages = await Page.findAll(Page.parseApiJson(ctx.state.developerMode ? {
|
||||||
|
filter: {
|
||||||
|
'parent_id.$notNull': true,
|
||||||
|
},
|
||||||
|
sort: ['sort'],
|
||||||
|
} : {
|
||||||
|
filter: {
|
||||||
|
'parent_id.$notNull': true,
|
||||||
|
developerMode: {'$isFalsy': true},
|
||||||
|
},
|
||||||
|
sort: ['sort'],
|
||||||
|
}));
|
||||||
|
const items = [];
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
items.push({
|
items.push({
|
||||||
id: page.id,
|
id: page.id,
|
||||||
@ -29,7 +102,7 @@ export async function list(ctx: actions.Context, next: actions.Next) {
|
|||||||
tableName: 'pages',
|
tableName: 'pages',
|
||||||
parent_id: `page-${page.parent_id}`,
|
parent_id: `page-${page.parent_id}`,
|
||||||
associatedKey,
|
associatedKey,
|
||||||
accessible: false, // TODO 对接权限
|
accessible: isRoot || routesPermissionsMap.has(`pages:${page.id}`), // TODO 对接权限
|
||||||
});
|
});
|
||||||
if (page.get('path') === '/collections') {
|
if (page.get('path') === '/collections') {
|
||||||
const collections = await Collection.findAll(Collection.parseApiJson(ctx.state.developerMode ? {
|
const collections = await Collection.findAll(Collection.parseApiJson(ctx.state.developerMode ? {
|
||||||
@ -52,7 +125,7 @@ export async function list(ctx: actions.Context, next: actions.Next) {
|
|||||||
tableName: 'collections',
|
tableName: 'collections',
|
||||||
title: collection.get('title'),
|
title: collection.get('title'),
|
||||||
parent_id: `page-${page.id}`,
|
parent_id: `page-${page.id}`,
|
||||||
accessible: false, // TODO 对接权限
|
accessible: isRoot || routesPermissionsMap.has(`collections:${collection.id}`), // TODO 对接权限
|
||||||
});
|
});
|
||||||
const views = await collection.getViews({
|
const views = await collection.getViews({
|
||||||
where: {
|
where: {
|
||||||
@ -72,7 +145,7 @@ export async function list(ctx: actions.Context, next: actions.Next) {
|
|||||||
title: view.title,
|
title: view.title,
|
||||||
key: `view-${view.id}`,
|
key: `view-${view.id}`,
|
||||||
parent_id: `collection-${collection.id}`,
|
parent_id: `collection-${collection.id}`,
|
||||||
accessible: false, // TODO 对接权限
|
accessible: isRoot || routesPermissionsMap.has(`views:${view.id}`), // TODO 对接权限
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,6 +179,46 @@ export async function list(ctx: actions.Context, next: actions.Next) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function update(ctx: actions.Context, next: actions.Next) {
|
export async function update(ctx: actions.Context, next: actions.Next) {
|
||||||
ctx.body = {};
|
const {
|
||||||
|
associated,
|
||||||
|
resourceKey,
|
||||||
|
values: {
|
||||||
|
tableName,
|
||||||
|
accessible
|
||||||
|
}
|
||||||
|
} = ctx.action.params;
|
||||||
|
|
||||||
|
if (!resourceKey) {
|
||||||
|
if (accessible === false) {
|
||||||
|
await associated.updateAssociations({
|
||||||
|
routes: [],
|
||||||
|
});
|
||||||
|
} else if (accessible === true) {
|
||||||
|
const routes = await getRoutes(ctx);
|
||||||
|
// console.log(routes);
|
||||||
|
await associated.updateAssociations({
|
||||||
|
routes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ctx.body = {};
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(ctx.action.params, { routable_type: tableName, routable_id: resourceKey });
|
||||||
|
let [route] = await associated.getRoutes({
|
||||||
|
where: { routable_type: tableName, routable_id: resourceKey },
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
if (accessible) {
|
||||||
|
if (!route) {
|
||||||
|
route = await associated.createRoute({ routable_type: tableName, routable_id: resourceKey });
|
||||||
|
}
|
||||||
|
ctx.body = route;
|
||||||
|
} else {
|
||||||
|
if (route) {
|
||||||
|
await route.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
|
@ -192,6 +192,15 @@ export default {
|
|||||||
type: 'number',
|
type: 'number',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// type: 'belongsToMany',
|
||||||
|
// name: 'roles',
|
||||||
|
// through: 'routes_permissions',
|
||||||
|
// foreignKey: 'routable_id',
|
||||||
|
// otherKey: 'role_id',
|
||||||
|
// morphType: 'routable',
|
||||||
|
// constraints: false,
|
||||||
|
// },
|
||||||
{
|
{
|
||||||
interface: 'json',
|
interface: 'json',
|
||||||
type: 'json',
|
type: 'json',
|
||||||
|
43
packages/plugin-pages/src/collections/roles.ts
Normal file
43
packages/plugin-pages/src/collections/roles.ts
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { extend } from '@nocobase/database';
|
||||||
|
|
||||||
|
export default extend({
|
||||||
|
name: 'roles',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'hasMany',
|
||||||
|
name: 'routes',
|
||||||
|
target: 'routes_permissions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
interface: 'linkTo',
|
||||||
|
type: 'belongsToMany',
|
||||||
|
name: 'pages',
|
||||||
|
title: '可访问的页面',
|
||||||
|
through: 'routes_permissions',
|
||||||
|
foreignKey: 'role_id',
|
||||||
|
otherKey: 'routable_id',
|
||||||
|
morphType: 'routable', // 现在没有多态关联的设置,暂时先这么写了
|
||||||
|
constraints: false, // 多态关联建立外键约束会有问题
|
||||||
|
}
|
||||||
|
],
|
||||||
|
tabs: [
|
||||||
|
{
|
||||||
|
type: 'association',
|
||||||
|
name: 'pages',
|
||||||
|
title: '系统菜单权限',
|
||||||
|
association: 'pages',
|
||||||
|
viewName: 'permissionTable',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
customMerge(key) {
|
||||||
|
if (['tabs'].includes(key)) {
|
||||||
|
return (x = [], y = []) => {
|
||||||
|
const last = x.pop();
|
||||||
|
const tabs = x.concat(y);
|
||||||
|
tabs.push(last);
|
||||||
|
return tabs;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
30
packages/plugin-pages/src/collections/routes_permissions.ts
Normal file
30
packages/plugin-pages/src/collections/routes_permissions.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { TableOptions } from '@nocobase/database';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'routes_permissions',
|
||||||
|
title: '页面权限',
|
||||||
|
developerMode: true,
|
||||||
|
internal: true,
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'id',
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'string',
|
||||||
|
name: 'routable_type',
|
||||||
|
title: '关联的表', // 仅 pages 和 collections
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'integer',
|
||||||
|
name: 'routable_id',
|
||||||
|
title: '关联的对象'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'role'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
} as TableOptions;
|
@ -5,7 +5,7 @@ import { ROLE_TYPE_ANONYMOUS, ROLE_TYPE_ROOT, ROLE_TYPE_USER } from './constants
|
|||||||
|
|
||||||
|
|
||||||
function getPermissions(roles) {
|
function getPermissions(roles) {
|
||||||
return roles.reduce((permissions, role) => permissions.concat(role.get('permissions')), []);
|
return roles.reduce((permissions, role) => permissions.concat(role.permissions), []);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActionPermissions(permissions) {
|
function getActionPermissions(permissions) {
|
||||||
@ -97,10 +97,16 @@ export type PermissionParams = true | null | {
|
|||||||
// ctx.can('collection').act('update').any()
|
// ctx.can('collection').act('update').any()
|
||||||
// ctx.can('collection').act('update').one(resourceKey)
|
// ctx.can('collection').act('update').one(resourceKey)
|
||||||
// ctx.can('collection').act('get').one(resourceKey)
|
// ctx.can('collection').act('get').one(resourceKey)
|
||||||
|
// ctx.as(roles).can('collection').permissions()
|
||||||
|
|
||||||
|
// TODO(optimize): 需要优化链式调用结构和上下文,以避免顺序或重置等问题
|
||||||
|
export default class AccessController<T extends typeof AccessController = typeof AccessController> {
|
||||||
|
static isRoot(roles): boolean {
|
||||||
|
return (Array.isArray(roles) ? roles : [roles]).some(role => role.type === ROLE_TYPE_ROOT);
|
||||||
|
}
|
||||||
|
|
||||||
export default class AccessController {
|
|
||||||
context;
|
context;
|
||||||
|
roles;
|
||||||
resourceName: string | null = null;
|
resourceName: string | null = null;
|
||||||
actionName: string | null = null;
|
actionName: string | null = null;
|
||||||
|
|
||||||
@ -108,11 +114,21 @@ export default class AccessController {
|
|||||||
this.context = ctx;
|
this.context = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
can = (resourceName: string | null) => {
|
/**
|
||||||
|
* 用于临时创建新的身份进行相关操作
|
||||||
|
* @param roles Array
|
||||||
|
*/
|
||||||
|
as(roles) {
|
||||||
|
const instance = new (this.constructor as T)(this.context);
|
||||||
|
instance.roles = Array.isArray(roles) ? roles : [roles];
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
can(resourceName: string | null) {
|
||||||
this.resourceName = resourceName;
|
this.resourceName = resourceName;
|
||||||
this.actionName = null;
|
this.actionName = null;
|
||||||
return this;
|
return this;
|
||||||
};
|
}
|
||||||
|
|
||||||
act(name: string | null) {
|
act(name: string | null) {
|
||||||
this.actionName = name;
|
this.actionName = name;
|
||||||
@ -121,7 +137,7 @@ export default class AccessController {
|
|||||||
|
|
||||||
async permissions(): Promise<CollectionPermissions> {
|
async permissions(): Promise<CollectionPermissions> {
|
||||||
const roles = await this.getRolesWithPermissions();
|
const roles = await this.getRolesWithPermissions();
|
||||||
if (roles.some(role => role.type === ROLE_TYPE_ROOT)) {
|
if ((this.constructor as T).isRoot(roles)) {
|
||||||
return this.getRootPermissions();
|
return this.getRootPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +152,7 @@ export default class AccessController {
|
|||||||
|
|
||||||
async any(): Promise<PermissionParams> {
|
async any(): Promise<PermissionParams> {
|
||||||
const roles = await this.getRolesWithPermissions();
|
const roles = await this.getRolesWithPermissions();
|
||||||
if (roles.some(role => role.type === ROLE_TYPE_ROOT)) {
|
if ((this.constructor as T).isRoot(roles)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// 只处理 actions 表里的权限,其余跳过
|
// 只处理 actions 表里的权限,其余跳过
|
||||||
@ -180,7 +196,7 @@ export default class AccessController {
|
|||||||
const Collection = this.context.db.getModel(this.resourceName);
|
const Collection = this.context.db.getModel(this.resourceName);
|
||||||
const existed = await Collection.count({
|
const existed = await Collection.count({
|
||||||
where: {
|
where: {
|
||||||
...Collection.parseApiJson({ filter }).where,
|
...Collection.parseApiJson({ filter, context: this.context }).where,
|
||||||
[Collection.primaryKeyAttribute]: resourceKey
|
[Collection.primaryKeyAttribute]: resourceKey
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -188,25 +204,78 @@ export default class AccessController {
|
|||||||
return existed ? any : null;
|
return existed ? any : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async isRoot(): Promise<boolean> {
|
||||||
|
const { context } = this;
|
||||||
|
const { currentUser } = context.state;
|
||||||
|
if (!currentUser) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rootRoles = await currentUser.countRoles({
|
||||||
|
where: {
|
||||||
|
type: ROLE_TYPE_ROOT
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!rootRoles.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRoles() {
|
||||||
|
if (this.roles) {
|
||||||
|
return this.roles;
|
||||||
|
}
|
||||||
|
const { context } = this;
|
||||||
|
let userRoles = [];
|
||||||
|
const { currentUser } = context.state;
|
||||||
|
if (currentUser) {
|
||||||
|
const rootRoles = await currentUser.getRoles({
|
||||||
|
where: {
|
||||||
|
type: ROLE_TYPE_ROOT
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (rootRoles.length) {
|
||||||
|
return rootRoles;
|
||||||
|
}
|
||||||
|
|
||||||
|
userRoles = await currentUser.getRoles({
|
||||||
|
where: {
|
||||||
|
type: ROLE_TYPE_USER
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const Role = context.db.getModel('roles');
|
||||||
|
const anonymousRoles = await Role.findAll({
|
||||||
|
where: {
|
||||||
|
type: ROLE_TYPE_ANONYMOUS
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...userRoles, ...anonymousRoles];
|
||||||
|
}
|
||||||
|
|
||||||
async getRolesWithPermissions() {
|
async getRolesWithPermissions() {
|
||||||
const { context, resourceName, actionName = null } = this;
|
const { context, resourceName, actionName = null } = this;
|
||||||
if (!resourceName) {
|
if (!resourceName) {
|
||||||
throw new Error('resource name must be set first by `can(resourceName)`');
|
throw new Error('resource name must be set first by `can(resourceName)`');
|
||||||
}
|
}
|
||||||
const Role = context.db.getModel('roles');
|
|
||||||
const permissionInclusion = {
|
const permissionOptions = {
|
||||||
association: 'permissions',
|
|
||||||
where: {
|
where: {
|
||||||
collection_name: resourceName
|
collection_name: resourceName
|
||||||
},
|
},
|
||||||
required: true,
|
|
||||||
include: [
|
include: [
|
||||||
{
|
{
|
||||||
association: 'actions',
|
association: 'actions',
|
||||||
where: actionName ? {
|
...(actionName ? {
|
||||||
name: `${resourceName}:${actionName}`
|
where: {
|
||||||
} : {},
|
name: `${resourceName}:${actionName}`
|
||||||
required: true,
|
},
|
||||||
|
required: true,
|
||||||
|
} : {}),
|
||||||
// 对 hasMany 关系可以进行拆分查询,避免联表过多标识符超过 PG 的 64 字符限制
|
// 对 hasMany 关系可以进行拆分查询,避免联表过多标识符超过 PG 的 64 字符限制
|
||||||
separate: true,
|
separate: true,
|
||||||
include: [
|
include: [
|
||||||
@ -228,8 +297,24 @@ export default class AccessController {
|
|||||||
association: 'tabs_permissions',
|
association: 'tabs_permissions',
|
||||||
separate: true,
|
separate: true,
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
};
|
};
|
||||||
|
const permissionInclusion = {
|
||||||
|
...permissionOptions,
|
||||||
|
association: 'permissions',
|
||||||
|
required: true
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.roles) {
|
||||||
|
if ((this.constructor as T).isRoot(this.roles)) {
|
||||||
|
return this.roles;
|
||||||
|
}
|
||||||
|
for (const role of this.roles) {
|
||||||
|
role.permissions = await role.getPermissions(permissionOptions);
|
||||||
|
role.set('permissions', role.permissions);
|
||||||
|
}
|
||||||
|
return this.roles;
|
||||||
|
}
|
||||||
|
|
||||||
let userRoles = [];
|
let userRoles = [];
|
||||||
// 获取登入用户的角色及权限
|
// 获取登入用户的角色及权限
|
||||||
@ -255,6 +340,7 @@ export default class AccessController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取匿名用户的角色及权限
|
// 获取匿名用户的角色及权限
|
||||||
|
const Role = context.db.getModel('roles');
|
||||||
const anonymousRoles = await Role.findAll({
|
const anonymousRoles = await Role.findAll({
|
||||||
where: {
|
where: {
|
||||||
type: ROLE_TYPE_ANONYMOUS
|
type: ROLE_TYPE_ANONYMOUS
|
||||||
|
@ -1,10 +1,24 @@
|
|||||||
import { Op } from 'sequelize';
|
import { Op } from 'sequelize';
|
||||||
import { actions } from '@nocobase/actions';
|
import { actions } from '@nocobase/actions';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
export async function list(ctx: actions.Context, next: actions.Next) {
|
export async function list(ctx: actions.Context, next: actions.Next) {
|
||||||
|
const { associated } = ctx.action.params;
|
||||||
// TODO: 暂时 action 中间件就这么写了
|
// TODO: 暂时 action 中间件就这么写了
|
||||||
ctx.action.mergeParams({associated: null});
|
ctx.action.mergeParams({
|
||||||
return actions.common.list(ctx, next);
|
associated: null
|
||||||
|
});
|
||||||
|
await actions.common.list(ctx, async () => {
|
||||||
|
const permissions = await associated.getPermissions();
|
||||||
|
ctx.body.rows.forEach(item => {
|
||||||
|
const permission = permissions.find(p => p.collection_name === item.get('name'));
|
||||||
|
if (permission) {
|
||||||
|
// item.permissions = [permission]; // 不输出
|
||||||
|
item.set('permissions', [permission]); // 输出
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await next();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get(ctx: actions.Context, next: actions.Next) {
|
export async function get(ctx: actions.Context, next: actions.Next) {
|
||||||
@ -13,47 +27,22 @@ export async function get(ctx: actions.Context, next: actions.Next) {
|
|||||||
associated
|
associated
|
||||||
} = ctx.action.params;
|
} = ctx.action.params;
|
||||||
|
|
||||||
const [permission] = await associated.getPermissions({
|
const permissions = await ctx.ac.as(associated).can(resourceKey).permissions();
|
||||||
where: {
|
|
||||||
collection_name: resourceKey
|
|
||||||
},
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
association: 'actions',
|
|
||||||
// 对 hasMany 关系可以进行拆分查询,避免联表过多标识符超过 PG 的 64 字符限制
|
|
||||||
separate: true,
|
|
||||||
include: [
|
|
||||||
{
|
|
||||||
association: 'scope'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
association: 'fields_permissions',
|
|
||||||
separate: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
association: 'tabs_permissions',
|
|
||||||
separate: true,
|
|
||||||
}
|
|
||||||
],
|
|
||||||
distinct: true,
|
|
||||||
limit: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = permission
|
|
||||||
? {
|
|
||||||
actions: permission.actions || [],
|
|
||||||
fields: permission.fields_permissions || [],
|
|
||||||
tabs: (permission.tabs_permissions || []).map(item => item.tab_id),
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
actions: [],
|
|
||||||
fields: [],
|
|
||||||
tabs: []
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.body = result;
|
const permission = await associated.getPermissions({
|
||||||
|
where: {
|
||||||
|
collection_name: resourceKey,
|
||||||
|
},
|
||||||
|
plain: true,
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(permission);
|
||||||
|
|
||||||
|
ctx.body = {
|
||||||
|
...permissions,
|
||||||
|
description: _.get(permission, 'description'),
|
||||||
|
};
|
||||||
|
|
||||||
await next();
|
await next();
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
import { actions } from '@nocobase/actions';
|
|
||||||
|
|
||||||
export async function list(ctx: actions.Context, next: actions.Next) {
|
|
||||||
// TODO: 暂时 action 中间件就这么写了
|
|
||||||
ctx.action.mergeParams({associated: null});
|
|
||||||
const { associatedKey } = ctx.action.params;
|
|
||||||
ctx.action.mergeParams({
|
|
||||||
filter: {
|
|
||||||
'parent_id.$notNull': true,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const done = async () => {
|
|
||||||
ctx.body.rows = ctx.body.rows.map(row => {
|
|
||||||
row.setDataValue('tableName', 'pages');
|
|
||||||
row.setDataValue('associatedKey', parseInt(associatedKey));
|
|
||||||
return row.get();
|
|
||||||
});
|
|
||||||
console.log(ctx.body.rows);
|
|
||||||
await next();
|
|
||||||
}
|
|
||||||
return actions.common.list(ctx, done);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function update(ctx: actions.Context, next: actions.Next) {
|
|
||||||
ctx.body = {};
|
|
||||||
await next();
|
|
||||||
}
|
|
@ -27,6 +27,12 @@ export default extend({
|
|||||||
through: 'permissions',
|
through: 'permissions',
|
||||||
sourceKey: 'name'
|
sourceKey: 'name'
|
||||||
},
|
},
|
||||||
|
// {
|
||||||
|
// type: 'hasMany',
|
||||||
|
// name: 'permissions',
|
||||||
|
// sourceKey: 'name',
|
||||||
|
// foreignKey: 'collection_name'
|
||||||
|
// }
|
||||||
],
|
],
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ export default {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
name: 'desctiption',
|
name: 'description',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
comment: '关联的角色',
|
comment: '关联的角色',
|
||||||
|
@ -24,12 +24,39 @@ export default {
|
|||||||
title: '角色类型',
|
title: '角色类型',
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
name: 'type',
|
name: 'type',
|
||||||
|
developerMode: true,
|
||||||
dataSource: [
|
dataSource: [
|
||||||
{ value: ROLE_TYPE_ROOT, label: '系统角色' },
|
{ value: ROLE_TYPE_ROOT, label: '系统角色' },
|
||||||
{ value: ROLE_TYPE_ANONYMOUS, label: '匿名角色' },
|
{ value: ROLE_TYPE_ANONYMOUS, label: '匿名角色' },
|
||||||
{ value: ROLE_TYPE_USER, label: '自定义角色' },
|
{ value: ROLE_TYPE_USER, label: '自定义角色' },
|
||||||
],
|
],
|
||||||
defaultValue: ROLE_TYPE_USER
|
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): 用户组后续考虑
|
||||||
// TODO(feature): 用户表应通过插件配置关联,考虑到今后会有多账户系统的情况
|
// TODO(feature): 用户表应通过插件配置关联,考虑到今后会有多账户系统的情况
|
||||||
@ -51,12 +78,6 @@ export default {
|
|||||||
through: 'permissions',
|
through: 'permissions',
|
||||||
targetKey: 'name'
|
targetKey: 'name'
|
||||||
},
|
},
|
||||||
{
|
|
||||||
interface: 'linkTo',
|
|
||||||
title: '页面',
|
|
||||||
type: 'belongsToMany',
|
|
||||||
name: 'pages',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
comment: '权限集(方便访问)',
|
comment: '权限集(方便访问)',
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
@ -134,13 +155,6 @@ export default {
|
|||||||
association: 'collections',
|
association: 'collections',
|
||||||
viewName: 'permissionTable',
|
viewName: 'permissionTable',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
type: 'association',
|
|
||||||
name: 'pages',
|
|
||||||
title: '系统菜单权限',
|
|
||||||
association: 'pages',
|
|
||||||
viewName: 'permissionTable',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
type: 'association',
|
type: 'association',
|
||||||
name: 'users',
|
name: 'users',
|
||||||
|
@ -63,6 +63,19 @@ export class Permissions {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 添加特殊的操作符以生成查询条件
|
// 针对“自己创建的” scope 添加特殊的操作符以生成查询条件
|
||||||
if (!Operator.has('$currentUser')) {
|
if (!Operator.has('$currentUser')) {
|
||||||
Operator.register('$currentUser', (value, { ctx }) => {
|
Operator.register('$currentUser', (value, { ctx }) => {
|
||||||
@ -76,7 +89,7 @@ export class Permissions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
injection = async (ctx, next) => {
|
injection = async (ctx, next) => {
|
||||||
ctx.can = new AccessController(ctx).can;
|
ctx.ac = new AccessController(ctx);
|
||||||
|
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
@ -93,9 +106,9 @@ export class Permissions {
|
|||||||
|
|
||||||
// 关系数据的权限
|
// 关系数据的权限
|
||||||
if (associatedName && resourceField) {
|
if (associatedName && resourceField) {
|
||||||
result = await ctx.can(resourceField.options.target).act(actionName).any();
|
result = await ctx.ac.can(resourceField.options.target).act(actionName).any();
|
||||||
} else {
|
} else {
|
||||||
result = await ctx.can(resourceName).act(actionName).any();
|
result = await ctx.ac.can(resourceName).act(actionName).any();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
Loading…
Reference in New Issue
Block a user