mirror of
https://github.com/nocobase/nocobase
synced 2024-11-15 08:26:21 +00:00
fix: append acl resource params (#1923)
* test: append acl resource params * fix: test
This commit is contained in:
parent
7080db72eb
commit
710bd2dfd2
@ -1,5 +1,6 @@
|
||||
import { ACLRole, RoleActionParams } from './acl-role';
|
||||
import { ACL, ListenerContext } from './acl';
|
||||
import lodash from 'lodash';
|
||||
|
||||
export type ResourceActions = { [key: string]: RoleActionParams };
|
||||
|
||||
@ -35,7 +36,15 @@ export class ACLResource {
|
||||
}
|
||||
|
||||
getAction(name: string) {
|
||||
return this.actions.get(name) || this.actions.get(this.acl.resolveActionAlias(name));
|
||||
const result = this.actions.get(name) || this.actions.get(this.acl.resolveActionAlias(name));
|
||||
if (!result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Array.isArray(result.fields) && result.fields.length > 0) {
|
||||
result.fields = lodash.uniq(result.fields);
|
||||
}
|
||||
return lodash.cloneDeep(result);
|
||||
}
|
||||
|
||||
setAction(name: string, params: RoleActionParams) {
|
||||
|
@ -45,20 +45,15 @@ interface CanArgs {
|
||||
}
|
||||
|
||||
export class ACL extends EventEmitter {
|
||||
protected availableActions = new Map<string, ACLAvailableAction>();
|
||||
public availableStrategy = new Map<string, ACLAvailableStrategy>();
|
||||
protected fixedParamsManager = new FixedParamsManager();
|
||||
|
||||
protected middlewares: Toposort<any>;
|
||||
|
||||
public allowManager = new AllowManager(this);
|
||||
public snippetManager = new SnippetManager();
|
||||
|
||||
roles = new Map<string, ACLRole>();
|
||||
|
||||
actionAlias = new Map<string, string>();
|
||||
|
||||
configResources: string[] = [];
|
||||
protected availableActions = new Map<string, ACLAvailableAction>();
|
||||
protected fixedParamsManager = new FixedParamsManager();
|
||||
protected middlewares: Toposort<any>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
@ -100,68 +95,6 @@ export class ACL extends EventEmitter {
|
||||
this.addCoreMiddleware();
|
||||
}
|
||||
|
||||
protected addCoreMiddleware() {
|
||||
const acl = this;
|
||||
|
||||
const filterParams = (ctx, resourceName, params) => {
|
||||
if (params?.filter?.createdById) {
|
||||
const collection = ctx.db.getCollection(resourceName);
|
||||
if (!collection || !collection.getField('createdById')) {
|
||||
return lodash.omit(params, 'filter.createdById');
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
this.middlewares.add(
|
||||
async (ctx, next) => {
|
||||
const resourcerAction: Action = ctx.action;
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
|
||||
const permission = ctx.permission;
|
||||
|
||||
ctx.log?.info && ctx.log.info('ctx permission', permission);
|
||||
|
||||
if ((!permission.can || typeof permission.can !== 'object') && !permission.skip) {
|
||||
ctx.throw(403, 'No permissions');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = permission.can?.params || acl.fixedParamsManager.getParams(resourceName, actionName);
|
||||
|
||||
ctx.log?.info && ctx.log.info('acl params', params);
|
||||
|
||||
if (params && resourcerAction.mergeParams) {
|
||||
const filteredParams = filterParams(ctx, resourceName, params);
|
||||
const parsedParams = await acl.parseJsonTemplate(filteredParams, ctx);
|
||||
|
||||
ctx.permission.parsedParams = parsedParams;
|
||||
ctx.log?.info && ctx.log.info('acl parsedParams', parsedParams);
|
||||
ctx.permission.rawParams = lodash.cloneDeep(resourcerAction.params);
|
||||
resourcerAction.mergeParams(parsedParams, {
|
||||
appends: (x, y) => {
|
||||
if (!x) {
|
||||
return [];
|
||||
}
|
||||
if (!y) {
|
||||
return x;
|
||||
}
|
||||
return (x as any[]).filter((i) => y.includes(i.split('.').shift()));
|
||||
},
|
||||
});
|
||||
ctx.permission.mergedParams = lodash.cloneDeep(resourcerAction.params);
|
||||
}
|
||||
|
||||
await next();
|
||||
},
|
||||
{
|
||||
tag: 'core',
|
||||
group: 'core',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
define(options: DefineOptions): ACLRole {
|
||||
const roleName = options.role;
|
||||
const role = new ACLRole(this, roleName);
|
||||
@ -302,10 +235,6 @@ export class ACL extends EventEmitter {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected isAvailableAction(actionName: string) {
|
||||
return this.availableActions.has(this.resolveActionAlias(actionName));
|
||||
}
|
||||
|
||||
public resolveActionAlias(action: string) {
|
||||
return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;
|
||||
}
|
||||
@ -360,7 +289,9 @@ export class ACL extends EventEmitter {
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
|
||||
ctx.can = (options: Omit<CanArgs, 'role'>) => {
|
||||
return acl.can({ role: roleName, ...options });
|
||||
const canResult = acl.can({ role: roleName, ...options });
|
||||
|
||||
return canResult;
|
||||
};
|
||||
|
||||
ctx.permission = {
|
||||
@ -397,4 +328,70 @@ export class ACL extends EventEmitter {
|
||||
registerSnippet(snippet: SnippetOptions) {
|
||||
this.snippetManager.register(snippet);
|
||||
}
|
||||
|
||||
protected addCoreMiddleware() {
|
||||
const acl = this;
|
||||
|
||||
const filterParams = (ctx, resourceName, params) => {
|
||||
if (params?.filter?.createdById) {
|
||||
const collection = ctx.db.getCollection(resourceName);
|
||||
if (!collection || !collection.getField('createdById')) {
|
||||
return lodash.omit(params, 'filter.createdById');
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
this.middlewares.add(
|
||||
async (ctx, next) => {
|
||||
const resourcerAction: Action = ctx.action;
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
|
||||
const permission = ctx.permission;
|
||||
|
||||
ctx.log?.info && ctx.log.info('ctx permission', permission);
|
||||
|
||||
if ((!permission.can || typeof permission.can !== 'object') && !permission.skip) {
|
||||
ctx.throw(403, 'No permissions');
|
||||
return;
|
||||
}
|
||||
|
||||
const params = permission.can?.params || acl.fixedParamsManager.getParams(resourceName, actionName);
|
||||
|
||||
ctx.log?.info && ctx.log.info('acl params', params);
|
||||
|
||||
if (params && resourcerAction.mergeParams) {
|
||||
const filteredParams = filterParams(ctx, resourceName, params);
|
||||
const parsedParams = await acl.parseJsonTemplate(filteredParams, ctx);
|
||||
|
||||
ctx.permission.parsedParams = parsedParams;
|
||||
ctx.log?.info && ctx.log.info('acl parsedParams', parsedParams);
|
||||
ctx.permission.rawParams = lodash.cloneDeep(resourcerAction.params);
|
||||
resourcerAction.mergeParams(parsedParams, {
|
||||
appends: (x, y) => {
|
||||
if (!x) {
|
||||
return [];
|
||||
}
|
||||
if (!y) {
|
||||
return x;
|
||||
}
|
||||
return (x as any[]).filter((i) => y.includes(i.split('.').shift()));
|
||||
},
|
||||
});
|
||||
ctx.permission.mergedParams = lodash.cloneDeep(resourcerAction.params);
|
||||
}
|
||||
|
||||
await next();
|
||||
},
|
||||
{
|
||||
tag: 'core',
|
||||
group: 'core',
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
protected isAvailableAction(actionName: string) {
|
||||
return this.availableActions.has(this.resolveActionAlias(actionName));
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,8 @@ describe('acl', () => {
|
||||
let admin;
|
||||
let adminAgent;
|
||||
|
||||
let userPlugin;
|
||||
|
||||
let uiSchemaRepository: UiSchemaRepository;
|
||||
|
||||
afterEach(async () => {
|
||||
@ -30,7 +32,7 @@ describe('acl', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const userPlugin = app.getPlugin('users') as UsersPlugin;
|
||||
userPlugin = app.getPlugin('users') as UsersPlugin;
|
||||
|
||||
adminAgent = app.agent().auth(
|
||||
userPlugin.jwtService.sign({
|
||||
@ -42,6 +44,131 @@ describe('acl', () => {
|
||||
uiSchemaRepository = db.getRepository('uiSchemas');
|
||||
});
|
||||
|
||||
test('append createById', async () => {
|
||||
const Company = await db.getRepository('collections').create({
|
||||
context: {},
|
||||
values: {
|
||||
name: 'companies',
|
||||
fields: [
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
},
|
||||
{
|
||||
type: 'hasMany',
|
||||
name: 'users',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const Repair = await db.getRepository('collections').create({
|
||||
context: {},
|
||||
values: {
|
||||
name: 'repairs',
|
||||
createdBy: true,
|
||||
fields: [
|
||||
{
|
||||
type: 'belongsTo',
|
||||
name: 'company',
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
name: 'name',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const c1 = await db.getRepository('companies').create({
|
||||
values: {
|
||||
name: 'c1',
|
||||
},
|
||||
});
|
||||
|
||||
await db.getRepository('roles').create({
|
||||
values: {
|
||||
name: 'test-role',
|
||||
},
|
||||
});
|
||||
|
||||
await adminAgent.resource('roles.resources', 'test-role').create({
|
||||
values: {
|
||||
name: 'repairs',
|
||||
usingActionsConfig: true,
|
||||
actions: [
|
||||
{
|
||||
name: 'list',
|
||||
fields: ['id'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const u1 = await db.getRepository('users').create({
|
||||
values: {
|
||||
name: 'u1',
|
||||
company: { id: c1.get('id') },
|
||||
roles: ['test-role'],
|
||||
},
|
||||
});
|
||||
|
||||
const r1 = await db.getRepository('repairs').create({
|
||||
values: {
|
||||
name: 'r1',
|
||||
company: { id: c1.get('id') },
|
||||
},
|
||||
});
|
||||
|
||||
userPlugin = app.getPlugin('users') as UsersPlugin;
|
||||
|
||||
const testAgent = app.agent().auth(
|
||||
userPlugin.jwtService.sign({
|
||||
userId: u1.get('id'),
|
||||
}),
|
||||
{ type: 'bearer' },
|
||||
);
|
||||
|
||||
// @ts-ignore
|
||||
const response1 = await testAgent.resource('repairs').list({
|
||||
filter: {
|
||||
company: {
|
||||
id: {
|
||||
$isVar: 'currentUser.company.id',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
const response2 = await testAgent.resource('repairs').list({
|
||||
filter: {
|
||||
company: {
|
||||
id: {
|
||||
$isVar: 'currentUser.company.id',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
const response3 = await testAgent.resource('repairs').list({
|
||||
filter: {
|
||||
company: {
|
||||
id: {
|
||||
$isVar: 'currentUser.company.id',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const acl = app.acl;
|
||||
const canResult = acl.can({ role: 'test-role', resource: 'repairs', action: 'list' });
|
||||
const params = canResult['params'];
|
||||
|
||||
expect(params['fields']).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('should not have permission to list comments', async () => {
|
||||
await db.getCollection('collections').repository.create({
|
||||
values: {
|
||||
|
Loading…
Reference in New Issue
Block a user