fix: sync scope changes to all related roles immediately

This commit is contained in:
aaaaaajie 2025-06-13 17:58:47 +08:00
parent 2fa26828d2
commit 09e84bafaf
4 changed files with 139 additions and 7 deletions

View File

@ -8,11 +8,11 @@
*/
import { ACL, ACLRole } from '@nocobase/acl';
import { Model } from '@nocobase/database';
import { Model, transaction } from '@nocobase/database';
export class RoleResourceActionModel extends Model {
async writeToACL(options: { acl: ACL; role: ACLRole; resourceName: string }) {
const { resourceName, role } = options;
async writeToACL(options: { acl: ACL; role: ACLRole; resourceName: string; transaction?: any }) {
const { resourceName, role, transaction } = options;
const actionName = this.get('name') as string;
@ -23,8 +23,7 @@ export class RoleResourceActionModel extends Model {
fields,
};
// @ts-ignore
const scope = await this.getScope();
const scope = await this.getScope({ transaction });
if (scope) {
actionParams['own'] = scope.get('key') === 'own';

View File

@ -18,7 +18,7 @@ export class RoleResourceModel extends Model {
}
async writeToACL(options: { acl: ACL; transaction?: any }) {
const { acl } = options;
const { acl, transaction } = options;
const resourceName = this.get('name') as string;
const roleName = this.get('roleName') as string;
const role = acl.getRole(roleName);
@ -45,7 +45,7 @@ export class RoleResourceModel extends Model {
// @ts-ignore
const actions: RoleResourceActionModel[] = await this.getActions({
transaction: options.transaction,
transaction,
});
for (const action of actions) {
@ -53,6 +53,7 @@ export class RoleResourceModel extends Model {
acl,
role,
resourceName,
transaction,
});
}
}

View File

@ -518,4 +518,111 @@ describe('data source with acl', () => {
expect(checkData.meta.dataSources.mockInstance1).exist;
expect(checkData.meta.dataSources.mockInstance1.strategy).toEqual({ actions: ['view'] });
});
it(`should update data sources`, async () => {
const adminUser = await app.db.getRepository('users').create({
values: {
roles: ['root'],
},
});
const adminAgent: any = await app.agent().login(adminUser);
await adminAgent.resource('roles').create({
values: {
name: 'testRole',
snippets: ['!ui.*', '!pm', '!pm.*'],
title: 'testRole',
},
});
const testUser = await app.db.getRepository('users').create({
values: {
roles: ['testRole'],
},
});
await app.db.getCollection('collections').repository.create({
values: {
name: 'posts',
fields: [
{
type: 'string',
name: 'title',
},
],
},
context: {},
});
const createScopeResp = await adminAgent
.post('/dataSources/main/rolesResourcesScopes:create')
.send({ scope: { $and: [{ title: { $includes: '456' } }] }, resourceName: 'posts', name: 't2' });
expect(createScopeResp.status).toBe(200);
const scope = createScopeResp.body.data;
const createRoleScopeResp = await adminAgent
.post('/roles/testRole/dataSourceResources:create')
.query({
filterByTk: 'posts',
filter: {
dataSourceKey: 'main',
name: 'posts',
},
})
.send({
usingActionsConfig: true,
actions: [
{
name: 'view',
fields: ['title'],
scope: {
id: scope.id,
createdAt: '2025-06-13T09:19:38.000Z',
updatedAt: '2025-06-13T09:19:38.000Z',
key: 'i50ffsy0aky',
dataSourceKey: 'main',
name: 't2',
resourceName: 'posts',
scope: { $and: [{ title: { $includes: '456' } }] },
},
},
],
name: 'posts',
dataSourceKey: 'main',
});
expect(createRoleScopeResp.status).toBe(200);
await app.db.getRepository('posts').create({
values: [{ title: '123' }, { title: '123456' }],
});
const testUserAgent: any = await app.agent().login(testUser, 'testRole');
const listRes1 = await testUserAgent.resource('posts').list({
filter: {},
pageSize: 10,
});
expect(listRes1.status).toBe(200);
expect(listRes1.body.data).toHaveLength(1);
const updateScopeResp = await adminAgent
.post('/dataSources/main/rolesResourcesScopes:update')
.query({
filterByTk: scope.id,
})
.send({ scope: { $and: [{ title: { $includes: '123' } }] }, resourceName: 'posts', name: 't2' });
expect(updateScopeResp.status).toBe(200);
const listRes2 = await testUserAgent.resource('posts').list({
filter: {},
pageSize: 10,
});
expect(listRes2.status).toBe(200);
expect(listRes2.body.data).toHaveLength(2);
});
});

View File

@ -593,6 +593,31 @@ export class PluginDataSourceManagerServer extends Plugin {
},
);
this.app.db.on('dataSourcesRolesResourcesScopes.afterSaveWithAssociations', async (model, options) => {
const { transaction } = options;
const dataSourcesRolesResourcesActions: DataSourcesRolesResourcesActionModel[] = await this.app.db
.getRepository('dataSourcesRolesResourcesActions')
.find({
filter: { scopeId: model.get('id') },
transaction,
});
const rolesRolesResourceIds = dataSourcesRolesResourcesActions.map((x) => x.get('rolesResourceId'));
const dataSourcesRolesResources: DataSourcesRolesResourcesModel[] = await this.app.db
.getRepository('dataSourcesRolesResources')
.find({
filter: {
id: rolesRolesResourceIds,
},
transaction,
});
for (const instance of dataSourcesRolesResources) {
await this.app.db.emitAsync(`dataSourcesRolesResources.afterSaveWithAssociations`, instance, {
...options,
transaction,
});
}
});
this.app.db.on(
'dataSourcesRolesResourcesActions.afterUpdateWithAssociations',
async (model: DataSourcesRolesResourcesActionModel, options) => {