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 { prepareApp } from './prepare';
|
||||
import { MockServer } from '@nocobase/test';
|
||||
|
||||
describe('list action with acl', () => {
|
||||
let app;
|
||||
let app: MockServer;
|
||||
|
||||
let Post;
|
||||
|
||||
@ -17,6 +18,11 @@ describe('list action with acl', () => {
|
||||
type: 'bigInt',
|
||||
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 data = response.body;
|
||||
@ -93,6 +100,48 @@ describe('list action with acl', () => {
|
||||
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 () => {
|
||||
const userRole = app.acl.define({
|
||||
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 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({
|
||||
filterByTk: 1,
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { mockServer } from '@nocobase/test';
|
||||
import { mockServer, MockServer } from '@nocobase/test';
|
||||
import PluginACL from '../server';
|
||||
|
||||
export async function prepareApp() {
|
||||
export async function prepareApp(): Promise<MockServer> {
|
||||
const app = mockServer({
|
||||
registerActions: true,
|
||||
acl: true,
|
||||
|
@ -585,20 +585,12 @@ export class PluginACL extends Plugin {
|
||||
const withACLMeta = async (ctx: any, next) => {
|
||||
await next();
|
||||
|
||||
if (!ctx.action) {
|
||||
if (!ctx.action || !ctx.get('X-With-ACL-Meta') || ctx.status !== 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { resourceName, actionName } = ctx.action;
|
||||
|
||||
if (!ctx.get('X-With-ACL-Meta')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.status !== 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!['list', 'get'].includes(actionName)) {
|
||||
return;
|
||||
}
|
||||
@ -620,11 +612,11 @@ export class PluginACL extends Plugin {
|
||||
listData = lodash.castArray(listData);
|
||||
}
|
||||
|
||||
const actions = ['view', 'update', 'destroy'];
|
||||
const inspectActions = ['view', 'update', 'destroy'];
|
||||
|
||||
const actionsParams = [];
|
||||
|
||||
for (const action of actions) {
|
||||
for (const action of inspectActions) {
|
||||
const actionCtx: any = {
|
||||
db: ctx.db,
|
||||
action: {
|
||||
@ -707,10 +699,16 @@ export class PluginACL extends Plugin {
|
||||
{
|
||||
where: (() => {
|
||||
const filterObj = queryParams.where;
|
||||
|
||||
if (!this.db.options.underscored) {
|
||||
return filterObj;
|
||||
}
|
||||
|
||||
const isAssociationKey = (key) => {
|
||||
return key.startsWith('$') && key.endsWith('$');
|
||||
};
|
||||
|
||||
// change camelCase to snake_case
|
||||
const iterate = (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)) {
|
||||
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.set(rootObj, [...path, setKey], setValue);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -776,7 +787,7 @@ export class PluginACL extends Plugin {
|
||||
include: conditions.map((condition) => condition.include).flat(),
|
||||
});
|
||||
|
||||
const allowedActions = actions
|
||||
const allowedActions = inspectActions
|
||||
.map((action) => {
|
||||
if (allAllowed.includes(action)) {
|
||||
return [action, ids];
|
||||
@ -831,4 +842,4 @@ export class PluginACL extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginACL;
|
||||
export default PluginACL;
|
||||
|
Loading…
Reference in New Issue
Block a user