mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-24 16:02:20 +08:00
fix: resolve permission error for many-to-many(many) fields in data t… (#7028)
* fix: resolve permission error for many-to-many(many) fields in data tables
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
import lodash from 'lodash';
|
||||
import { snakeCase } from '@nocobase/database';
|
||||
import { Model, snakeCase } from '@nocobase/database';
|
||||
import { NoPermissionError } from '@nocobase/acl';
|
||||
|
||||
function createWithACLMetaMiddleware() {
|
||||
@@ -165,6 +165,13 @@ function createWithACLMetaMiddleware() {
|
||||
...params,
|
||||
context: actionCtx,
|
||||
});
|
||||
if (ctx.action?.params?.appends || queryParams?.appends) {
|
||||
const queryParamsWithAppends = queryParams?.appends || [];
|
||||
const ctxActionParamsWithAppends = ctx.action?.params?.appends || [];
|
||||
const appends = queryParamsWithAppends.filter((x) => ctxActionParamsWithAppends.includes(x));
|
||||
queryParams.include = appends;
|
||||
queryParams.appends = appends;
|
||||
}
|
||||
|
||||
const actionSql = ctx.db.sequelize.queryInterface.queryGenerator.selectQuery(
|
||||
Model.getTableName(),
|
||||
@@ -254,7 +261,10 @@ function createWithACLMetaMiddleware() {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const includes = conditions
|
||||
.map((c) => c.include)
|
||||
.flat()
|
||||
.map((x) => processIncludes(collection.model, x || []));
|
||||
const results = await collection.model.findAll({
|
||||
where: {
|
||||
[primaryKeyField]: ids,
|
||||
@@ -265,7 +275,7 @@ function createWithACLMetaMiddleware() {
|
||||
return [ctx.db.sequelize.literal(`CASE WHEN ${condition.whereCase} THEN 1 ELSE 0 END`), condition.action];
|
||||
}),
|
||||
],
|
||||
include: conditions.map((condition) => condition.include).flat(),
|
||||
include: includes,
|
||||
raw: true,
|
||||
});
|
||||
|
||||
@@ -297,4 +307,17 @@ function createWithACLMetaMiddleware() {
|
||||
};
|
||||
}
|
||||
|
||||
function processIncludes(model: typeof Model, include: string) {
|
||||
const association = model.associations[include];
|
||||
if (association?.['generateInclude']) {
|
||||
const newInclude = {
|
||||
association: include,
|
||||
attributes: [], // out put empty fields by default
|
||||
...association['generateInclude'](),
|
||||
};
|
||||
return newInclude;
|
||||
}
|
||||
return include;
|
||||
}
|
||||
|
||||
export { createWithACLMetaMiddleware };
|
||||
|
||||
@@ -620,6 +620,7 @@ export class PluginACLServer extends Plugin {
|
||||
await withACLMeta(ctx, next);
|
||||
} catch (error) {
|
||||
ctx.logger.error(error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
{ after: 'dataSource', group: 'with-acl-meta' },
|
||||
|
||||
+98
-8
@@ -17,7 +17,19 @@ describe('belongs to array field', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await createMockServer({
|
||||
plugins: ['field-m2m-array', 'data-source-manager', 'field-sort', 'data-source-main', 'error-handler'],
|
||||
plugins: [
|
||||
'acl',
|
||||
'users',
|
||||
'auth',
|
||||
'data-source-main',
|
||||
'field-m2m-array',
|
||||
'data-source-manager',
|
||||
'field-sort',
|
||||
'error-handler',
|
||||
'system-settings',
|
||||
],
|
||||
registerActions: true,
|
||||
acl: true,
|
||||
});
|
||||
db = app.db;
|
||||
fieldRepo = db.getRepository('fields');
|
||||
@@ -44,7 +56,7 @@ describe('belongs to array field', () => {
|
||||
});
|
||||
await db.getRepository('collections').create({
|
||||
values: {
|
||||
name: 'users',
|
||||
name: 'users1',
|
||||
fields: [
|
||||
{
|
||||
name: 'id',
|
||||
@@ -83,7 +95,7 @@ describe('belongs to array field', () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users',
|
||||
collectionName: 'users1',
|
||||
name: 'tags',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'tag_ids',
|
||||
@@ -102,7 +114,7 @@ describe('belongs to array field', () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users',
|
||||
collectionName: 'users1',
|
||||
name: 'tags',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'username',
|
||||
@@ -113,7 +125,7 @@ describe('belongs to array field', () => {
|
||||
});
|
||||
await field.load({ transaction });
|
||||
}),
|
||||
).rejects.toThrow(/The type of foreign key "username" in collection "users" must be ARRAY, JSON or JSONB/);
|
||||
).rejects.toThrow(/The type of foreign key "username" in collection "users1" must be ARRAY, JSON or JSONB/);
|
||||
});
|
||||
|
||||
it('element type of foreign field must be match the type of target field', async () => {
|
||||
@@ -125,7 +137,7 @@ describe('belongs to array field', () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users',
|
||||
collectionName: 'users1',
|
||||
name: 'tags',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'tag_ids',
|
||||
@@ -145,7 +157,7 @@ describe('belongs to array field', () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users',
|
||||
collectionName: 'users1',
|
||||
name: 'tags',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'tag_ids',
|
||||
@@ -165,7 +177,7 @@ describe('belongs to array field', () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users',
|
||||
collectionName: 'users1',
|
||||
name: 'tag_ids_same',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'tag_ids_same',
|
||||
@@ -179,4 +191,82 @@ describe('belongs to array field', () => {
|
||||
).rejects.toThrow(/Naming collision/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('many-to-many acl test', async () => {
|
||||
it('should list allowedActions when include many-to-many', async () => {
|
||||
const field = await fieldRepo.create({
|
||||
values: {
|
||||
interface: 'mbm',
|
||||
collectionName: 'users1',
|
||||
name: 'tags',
|
||||
type: 'belongsToArray',
|
||||
foreignKey: 'tag_ids',
|
||||
target: 'tags',
|
||||
targetKey: 'stringCode',
|
||||
},
|
||||
});
|
||||
await field.load();
|
||||
await app.db.sync();
|
||||
|
||||
await app.db.getCollection('tags').repository.create({
|
||||
values: {
|
||||
id: 1,
|
||||
stringCode: '1',
|
||||
title: 'Tag 1',
|
||||
},
|
||||
});
|
||||
await app.db.getCollection('tags').repository.create({
|
||||
values: {
|
||||
id: 2,
|
||||
stringCode: '2',
|
||||
title: 'Tag 2',
|
||||
},
|
||||
});
|
||||
|
||||
await app.db.getCollection('users1').repository.create({
|
||||
values: {
|
||||
username: 'testuser',
|
||||
tag_ids: ['1', '2'],
|
||||
},
|
||||
});
|
||||
await db.getRepository('roles').create({
|
||||
values: {
|
||||
name: 'newRole',
|
||||
},
|
||||
});
|
||||
const user = await db.getRepository('users').create({
|
||||
values: {
|
||||
roles: ['newRole'],
|
||||
},
|
||||
});
|
||||
|
||||
const result = await db.getRepository('roles.resources', 'newRole').create({
|
||||
values: {
|
||||
name: 'users1',
|
||||
usingActionConfig: true,
|
||||
actions: [
|
||||
{
|
||||
name: 'view',
|
||||
fields: ['id', 'tags'],
|
||||
scope: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const userAgent = (await app.agent().login(user, 'newRole')) as any;
|
||||
const listResp = await userAgent
|
||||
.set('X-With-ACL-Meta', true)
|
||||
.resource('users1')
|
||||
.list({
|
||||
pageSize: 2,
|
||||
appends: ['tags'],
|
||||
filter: {
|
||||
'tags.id': '1',
|
||||
},
|
||||
});
|
||||
expect(listResp.statusCode).toEqual(200);
|
||||
expect(listResp.body.meta.allowedActions).exist;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user