fix: meta acl with association query (#1695)
* fix: meta acl with association query * fix: test
This commit is contained in:
parent
2ed3565d43
commit
86de0733ad
@ -1,8 +1,9 @@
|
|||||||
import { Database } from '@nocobase/database';
|
import { Database } from '@nocobase/database';
|
||||||
import { prepareApp } from './prepare';
|
import { prepareApp } from './prepare';
|
||||||
|
import { MockServer } from '@nocobase/test';
|
||||||
|
|
||||||
describe('list action with acl', () => {
|
describe('list action with acl', () => {
|
||||||
let app;
|
let app: MockServer;
|
||||||
|
|
||||||
let Post;
|
let Post;
|
||||||
|
|
||||||
@ -17,6 +18,11 @@ describe('list action with acl', () => {
|
|||||||
type: 'bigInt',
|
type: 'bigInt',
|
||||||
name: 'createdById',
|
name: 'createdById',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'belongsTo',
|
||||||
|
name: 'createdBy',
|
||||||
|
target: 'users',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,6 +91,7 @@ describe('list action with acl', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
const response = await app.agent().set('X-With-ACL-Meta', true).resource('tests').list({});
|
const response = await app.agent().set('X-With-ACL-Meta', true).resource('tests').list({});
|
||||||
|
|
||||||
const data = response.body;
|
const data = response.body;
|
||||||
@ -93,6 +100,48 @@ describe('list action with acl', () => {
|
|||||||
expect(data.meta.allowedActions.destroy).toEqual(['t3']);
|
expect(data.meta.allowedActions.destroy).toEqual(['t3']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should list items meta permissions by association field', async () => {
|
||||||
|
const userRole = app.acl.define({
|
||||||
|
role: 'user',
|
||||||
|
});
|
||||||
|
|
||||||
|
userRole.grantAction('posts:view', {});
|
||||||
|
|
||||||
|
userRole.grantAction('posts:update', {
|
||||||
|
filter: {
|
||||||
|
'createdBy.id': '{{ ctx.state.currentUser.id }}',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Post.repository.create({
|
||||||
|
values: [
|
||||||
|
{ title: 'p1', createdById: 1 },
|
||||||
|
{ title: 'p2', createdById: 1 },
|
||||||
|
{ title: 'p3', createdById: 2 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.resourcer.use(
|
||||||
|
(ctx, next) => {
|
||||||
|
ctx.state.currentRole = 'user';
|
||||||
|
ctx.state.currentUser = {
|
||||||
|
id: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return next();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
before: 'acl',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const response = await (app as any).agent().set('X-With-ACL-Meta', true).resource('posts').list();
|
||||||
|
const data = response.body;
|
||||||
|
expect(data.meta.allowedActions.view).toEqual([1, 2, 3]);
|
||||||
|
expect(data.meta.allowedActions.update).toEqual([1, 2]);
|
||||||
|
expect(data.meta.allowedActions.destroy).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should list items with meta permission', async () => {
|
it('should list items with meta permission', async () => {
|
||||||
const userRole = app.acl.define({
|
const userRole = app.acl.define({
|
||||||
role: 'user',
|
role: 'user',
|
||||||
@ -126,6 +175,7 @@ describe('list action with acl', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const response = await app.agent().set('X-With-ACL-Meta', true).resource('posts').list({});
|
const response = await app.agent().set('X-With-ACL-Meta', true).resource('posts').list({});
|
||||||
|
|
||||||
const data = response.body;
|
const data = response.body;
|
||||||
@ -167,6 +217,7 @@ describe('list action with acl', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const getResponse = await app.agent().set('X-With-ACL-Meta', true).resource('posts').get({
|
const getResponse = await app.agent().set('X-With-ACL-Meta', true).resource('posts').get({
|
||||||
filterByTk: 1,
|
filterByTk: 1,
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { mockServer } from '@nocobase/test';
|
import { mockServer, MockServer } from '@nocobase/test';
|
||||||
import PluginACL from '../server';
|
import PluginACL from '../server';
|
||||||
|
|
||||||
export async function prepareApp() {
|
export async function prepareApp(): Promise<MockServer> {
|
||||||
const app = mockServer({
|
const app = mockServer({
|
||||||
registerActions: true,
|
registerActions: true,
|
||||||
acl: true,
|
acl: true,
|
||||||
|
@ -585,20 +585,12 @@ export class PluginACL extends Plugin {
|
|||||||
const withACLMeta = async (ctx: any, next) => {
|
const withACLMeta = async (ctx: any, next) => {
|
||||||
await next();
|
await next();
|
||||||
|
|
||||||
if (!ctx.action) {
|
if (!ctx.action || !ctx.get('X-With-ACL-Meta') || ctx.status !== 200) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { resourceName, actionName } = ctx.action;
|
const { resourceName, actionName } = ctx.action;
|
||||||
|
|
||||||
if (!ctx.get('X-With-ACL-Meta')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctx.status !== 200) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!['list', 'get'].includes(actionName)) {
|
if (!['list', 'get'].includes(actionName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -620,11 +612,11 @@ export class PluginACL extends Plugin {
|
|||||||
listData = lodash.castArray(listData);
|
listData = lodash.castArray(listData);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = ['view', 'update', 'destroy'];
|
const inspectActions = ['view', 'update', 'destroy'];
|
||||||
|
|
||||||
const actionsParams = [];
|
const actionsParams = [];
|
||||||
|
|
||||||
for (const action of actions) {
|
for (const action of inspectActions) {
|
||||||
const actionCtx: any = {
|
const actionCtx: any = {
|
||||||
db: ctx.db,
|
db: ctx.db,
|
||||||
action: {
|
action: {
|
||||||
@ -707,10 +699,16 @@ export class PluginACL extends Plugin {
|
|||||||
{
|
{
|
||||||
where: (() => {
|
where: (() => {
|
||||||
const filterObj = queryParams.where;
|
const filterObj = queryParams.where;
|
||||||
|
|
||||||
if (!this.db.options.underscored) {
|
if (!this.db.options.underscored) {
|
||||||
return filterObj;
|
return filterObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isAssociationKey = (key) => {
|
||||||
|
return key.startsWith('$') && key.endsWith('$');
|
||||||
|
};
|
||||||
|
|
||||||
|
// change camelCase to snake_case
|
||||||
const iterate = (rootObj, path = []) => {
|
const iterate = (rootObj, path = []) => {
|
||||||
const obj = path.length == 0 ? rootObj : lodash.get(rootObj, path);
|
const obj = path.length == 0 ? rootObj : lodash.get(rootObj, path);
|
||||||
|
|
||||||
@ -738,8 +736,21 @@ export class PluginACL extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof key === 'string' && key !== snakeCase(key)) {
|
if (typeof key === 'string' && key !== snakeCase(key)) {
|
||||||
lodash.set(rootObj, [...path, snakeCase(key)], lodash.cloneDeep(obj[key]));
|
const setKey = isAssociationKey(key)
|
||||||
|
? (() => {
|
||||||
|
const parts = key.split('.');
|
||||||
|
|
||||||
|
parts[parts.length - 1] = lodash.snakeCase(parts[parts.length - 1]);
|
||||||
|
|
||||||
|
const result = parts.join('.');
|
||||||
|
|
||||||
|
return result.endsWith('$') ? result : `${result}$`;
|
||||||
|
})()
|
||||||
|
: snakeCase(key);
|
||||||
|
const setValue = lodash.cloneDeep(obj[key]);
|
||||||
lodash.unset(rootObj, [...path, key]);
|
lodash.unset(rootObj, [...path, key]);
|
||||||
|
|
||||||
|
lodash.set(rootObj, [...path, setKey], setValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -776,7 +787,7 @@ export class PluginACL extends Plugin {
|
|||||||
include: conditions.map((condition) => condition.include).flat(),
|
include: conditions.map((condition) => condition.include).flat(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const allowedActions = actions
|
const allowedActions = inspectActions
|
||||||
.map((action) => {
|
.map((action) => {
|
||||||
if (allAllowed.includes(action)) {
|
if (allAllowed.includes(action)) {
|
||||||
return [action, ids];
|
return [action, ids];
|
||||||
|
Loading…
Reference in New Issue
Block a user