diff --git a/packages/plugin-acl/src/__tests__/acl.test.ts b/packages/plugin-acl/src/__tests__/acl.test.ts index 28e338d90f..346c2ff312 100644 --- a/packages/plugin-acl/src/__tests__/acl.test.ts +++ b/packages/plugin-acl/src/__tests__/acl.test.ts @@ -313,4 +313,69 @@ describe('acl', () => { action: 'create', }); }); + + it('should add fields when field created', async () => { + const role = await db.getRepository('roles').create({ + values: { + name: 'admin', + title: 'Admin User', + allowConfigure: true, + }, + }); + + await db.getRepository('collections').create({ + values: { + name: 'posts', + }, + }); + + await db.getRepository('fields').create({ + values: { + collectionName: 'posts', + type: 'string', + name: 'title', + }, + }); + + await app + .agent() + .resource('roles.resources') + .create({ + associatedIndex: role.get('name') as string, + values: { + name: 'posts', + usingActionsConfig: true, + actions: [ + { + name: 'view', + fields: ['title'], + }, + ], + }, + }); + + const allowFields = acl.can({ + role: 'admin', + resource: 'posts', + action: 'view', + })['params']['fields']; + + expect(allowFields.includes('title')).toBeTruthy(); + + await db.getRepository('fields').create({ + values: { + collectionName: 'posts', + type: 'string', + name: 'description', + }, + }); + + const newAllowFields = acl.can({ + role: 'admin', + resource: 'posts', + action: 'view', + })['params']['fields']; + + expect(newAllowFields.includes('description')).toBeTruthy(); + }); }); diff --git a/packages/plugin-acl/src/model/RoleResourceActionModel.ts b/packages/plugin-acl/src/model/RoleResourceActionModel.ts index 482226e356..654e3df22b 100644 --- a/packages/plugin-acl/src/model/RoleResourceActionModel.ts +++ b/packages/plugin-acl/src/model/RoleResourceActionModel.ts @@ -17,6 +17,7 @@ export class RoleResourceActionModel extends Model { const actionName = this.get('name') as string; const fields = this.get('fields') as any; + const actionPath = `${resourceName}:${actionName}`; const actionParams = { fields, diff --git a/packages/plugin-acl/src/server.ts b/packages/plugin-acl/src/server.ts index 9bf3fc4602..43acbcd17f 100644 --- a/packages/plugin-acl/src/server.ts +++ b/packages/plugin-acl/src/server.ts @@ -82,6 +82,27 @@ export class PluginACL extends Plugin { }); } + async writeResourceToACL(resourceModel: RoleResourceModel, transaction) { + await resourceModel.writeToACL({ + acl: this.acl, + associationFieldsActions: this.associationFieldsActions, + transaction: transaction, + grantHelper: this.grantHelper, + }); + } + + async writeActionToACL(actionModel: RoleResourceActionModel, transaction) { + const resource = actionModel.get('resource') as RoleResourceModel; + const role = this.acl.getRole(resource.get('roleName') as string); + await actionModel.writeToACL({ + acl: this.acl, + role, + resourceName: resource.get('name') as string, + associationFieldsActions: this.associationFieldsActions, + grantHelper: this.grantHelper, + }); + } + async beforeLoad() { const acl = createACL(); this.acl = acl; @@ -136,12 +157,7 @@ export class PluginACL extends Plugin { }); this.app.db.on('rolesResources.afterSaveWithAssociations', async (model: RoleResourceModel, options) => { - await model.writeToACL({ - acl: this.acl, - associationFieldsActions: this.associationFieldsActions, - transaction: options.transaction, - grantHelper: this.grantHelper, - }); + await this.writeResourceToACL(model, options.transaction); }); this.app.db.on('rolesResourcesActions.afterUpdateWithAssociations', async (model, options) => { @@ -150,12 +166,35 @@ export class PluginACL extends Plugin { transaction, }); - await resource.writeToACL({ - acl: this.acl, - associationFieldsActions: this.associationFieldsActions, - transaction: options.transaction, - grantHelper: this.grantHelper, - }); + await this.writeResourceToACL(resource, transaction); + }); + + this.app.db.on('fields.afterCreate', async (model, options) => { + const { transaction } = options; + + const collectionName = model.get('collectionName'); + const fieldName = model.get('name'); + + const resourceActions = (await this.app.db.getRepository('rolesResourcesActions').find({ + filter: { + 'resource.name': collectionName, + }, + transaction, + appends: ['resource'], + })) as RoleResourceActionModel[]; + + for (const resourceAction of resourceActions) { + const fields = resourceAction.get('fields') as string[]; + const newFields = [...fields, fieldName]; + + await this.app.db.getRepository('rolesResourcesActions').update({ + filterByTk: resourceAction.get('id') as number, + values: { + fields: newFields, + }, + transaction, + }); + } }); this.app.db.on('fields.afterDestroy', async (model, options) => {