@nocobase/plugin-acl 0.7.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/esm/actions/available-actions.d.ts +7 -0
  3. package/esm/actions/available-actions.js +26 -0
  4. package/esm/actions/available-actions.js.map +1 -0
  5. package/esm/actions/role-check.d.ts +1 -0
  6. package/esm/actions/role-check.js +38 -0
  7. package/esm/actions/role-check.js.map +1 -0
  8. package/esm/actions/role-collections.d.ts +7 -0
  9. package/esm/actions/role-collections.js +54 -0
  10. package/esm/actions/role-collections.js.map +1 -0
  11. package/esm/collections/roles.d.ts +3 -0
  12. package/esm/collections/roles.js +78 -0
  13. package/esm/collections/roles.js.map +1 -0
  14. package/esm/collections/rolesResources.d.ts +3 -0
  15. package/esm/collections/rolesResources.js +30 -0
  16. package/esm/collections/rolesResources.js.map +1 -0
  17. package/esm/collections/rolesResourcesActions.d.ts +3 -0
  18. package/esm/collections/rolesResourcesActions.js +27 -0
  19. package/esm/collections/rolesResourcesActions.js.map +1 -0
  20. package/esm/collections/rolesResourcesScopes.d.ts +3 -0
  21. package/esm/collections/rolesResourcesScopes.js +22 -0
  22. package/esm/collections/rolesResourcesScopes.js.map +1 -0
  23. package/esm/index.d.ts +1 -0
  24. package/esm/index.js +2 -0
  25. package/esm/index.js.map +1 -0
  26. package/esm/model/RoleModel.d.ts +7 -0
  27. package/esm/model/RoleModel.js +15 -0
  28. package/esm/model/RoleModel.js.map +1 -0
  29. package/esm/model/RoleResourceActionModel.d.ts +12 -0
  30. package/esm/model/RoleResourceActionModel.js +65 -0
  31. package/esm/model/RoleResourceActionModel.js.map +1 -0
  32. package/esm/model/RoleResourceModel.d.ts +16 -0
  33. package/esm/model/RoleResourceModel.js +55 -0
  34. package/esm/model/RoleResourceModel.js.map +1 -0
  35. package/esm/server.d.ts +33 -0
  36. package/esm/server.js +366 -0
  37. package/esm/server.js.map +1 -0
  38. package/lib/actions/available-actions.d.ts +7 -0
  39. package/lib/actions/available-actions.js +29 -0
  40. package/lib/actions/available-actions.js.map +1 -0
  41. package/lib/actions/role-check.d.ts +1 -0
  42. package/lib/actions/role-check.js +42 -0
  43. package/lib/actions/role-check.js.map +1 -0
  44. package/lib/actions/role-collections.d.ts +7 -0
  45. package/lib/actions/role-collections.js +57 -0
  46. package/lib/actions/role-collections.js.map +1 -0
  47. package/lib/collections/roles.d.ts +3 -0
  48. package/lib/collections/roles.js +80 -0
  49. package/lib/collections/roles.js.map +1 -0
  50. package/lib/collections/rolesResources.d.ts +3 -0
  51. package/lib/collections/rolesResources.js +32 -0
  52. package/lib/collections/rolesResources.js.map +1 -0
  53. package/lib/collections/rolesResourcesActions.d.ts +3 -0
  54. package/lib/collections/rolesResourcesActions.js +29 -0
  55. package/lib/collections/rolesResourcesActions.js.map +1 -0
  56. package/lib/collections/rolesResourcesScopes.d.ts +3 -0
  57. package/lib/collections/rolesResourcesScopes.js +24 -0
  58. package/lib/collections/rolesResourcesScopes.js.map +1 -0
  59. package/lib/index.d.ts +1 -0
  60. package/lib/index.js +9 -0
  61. package/lib/index.js.map +1 -0
  62. package/lib/model/RoleModel.d.ts +7 -0
  63. package/lib/model/RoleModel.js +19 -0
  64. package/lib/model/RoleModel.js.map +1 -0
  65. package/lib/model/RoleResourceActionModel.d.ts +12 -0
  66. package/lib/model/RoleResourceActionModel.js +69 -0
  67. package/lib/model/RoleResourceActionModel.js.map +1 -0
  68. package/lib/model/RoleResourceModel.d.ts +16 -0
  69. package/lib/model/RoleResourceModel.js +59 -0
  70. package/lib/model/RoleResourceModel.js.map +1 -0
  71. package/lib/server.d.ts +33 -0
  72. package/lib/server.js +371 -0
  73. package/lib/server.js.map +1 -0
  74. package/package.json +30 -0
  75. package/src/__tests__/acl.test.ts +533 -0
  76. package/src/__tests__/association-field.test.ts +297 -0
  77. package/src/__tests__/configuration.test.ts +45 -0
  78. package/src/__tests__/middleware.test.ts +233 -0
  79. package/src/__tests__/own.test.ts +140 -0
  80. package/src/__tests__/prepare.ts +39 -0
  81. package/src/__tests__/role-check.test.ts +35 -0
  82. package/src/__tests__/role-resource.test.ts +187 -0
  83. package/src/__tests__/role.test.ts +92 -0
  84. package/src/__tests__/scope.test.ts +51 -0
  85. package/src/actions/available-actions.ts +18 -0
  86. package/src/actions/role-check.ts +39 -0
  87. package/src/actions/role-collections.ts +55 -0
  88. package/src/collections/roles.ts +79 -0
  89. package/src/collections/rolesResources.ts +31 -0
  90. package/src/collections/rolesResourcesActions.ts +28 -0
  91. package/src/collections/rolesResourcesScopes.ts +23 -0
  92. package/src/index.ts +2 -0
  93. package/src/model/RoleModel.ts +21 -0
  94. package/src/model/RoleResourceActionModel.ts +80 -0
  95. package/src/model/RoleResourceModel.ts +61 -0
  96. package/src/server.ts +398 -0
  97. package/tsconfig.build.json +9 -0
  98. package/tsconfig.json +5 -0
@@ -0,0 +1,21 @@
1
+ import { Model } from '@nocobase/database';
2
+ import { ACL } from '@nocobase/acl';
3
+
4
+ export class RoleModel extends Model {
5
+ writeToAcl(options: { acl: ACL }) {
6
+ const { acl } = options;
7
+ const roleName = this.get('name') as string;
8
+ let role = acl.getRole(roleName);
9
+
10
+ if (!role) {
11
+ role = acl.define({
12
+ role: roleName,
13
+ });
14
+ }
15
+
16
+ role.setStrategy({
17
+ ...((this.get('strategy') as object) || {}),
18
+ allowConfigure: this.get('allowConfigure') as boolean,
19
+ });
20
+ }
21
+ }
@@ -0,0 +1,80 @@
1
+ import { ACL, ACLRole } from '@nocobase/acl';
2
+ import { Database, Model } from '@nocobase/database';
3
+ import { AssociationFieldAction, AssociationFieldsActions, GrantHelper } from '../server';
4
+
5
+ export class RoleResourceActionModel extends Model {
6
+ async writeToACL(options: {
7
+ acl: ACL;
8
+ role: ACLRole;
9
+ resourceName: string;
10
+ associationFieldsActions: AssociationFieldsActions;
11
+ grantHelper: GrantHelper;
12
+ }) {
13
+ // @ts-ignore
14
+ const db: Database = this.constructor.database;
15
+
16
+ const { resourceName, role, acl, associationFieldsActions, grantHelper } = options;
17
+
18
+ const actionName = this.get('name') as string;
19
+
20
+ const fields = this.get('fields') as any;
21
+
22
+ const actionPath = `${resourceName}:${actionName}`;
23
+ const actionParams = {
24
+ fields,
25
+ };
26
+
27
+ // @ts-ignore
28
+ const scope = await this.getScope();
29
+
30
+ if (scope) {
31
+ actionParams['own'] = scope.get('key') === 'own';
32
+ actionParams['filter'] = scope.get('scope');
33
+ }
34
+
35
+ role.grantAction(actionPath, actionParams);
36
+
37
+ const collection = db.getCollection(resourceName);
38
+
39
+ if (!collection) {
40
+ return;
41
+ }
42
+
43
+ const availableAction = acl.resolveActionAlias(actionName);
44
+
45
+ for (const field of fields) {
46
+ const collectionField = collection.getField(field);
47
+ const fieldType = collectionField.get('interface') as string;
48
+
49
+ const fieldActions: AssociationFieldAction = associationFieldsActions?.[fieldType]?.[availableAction];
50
+
51
+ const fieldTarget = collectionField.get('target');
52
+
53
+ if (fieldActions) {
54
+ const associationActions = fieldActions.associationActions || [];
55
+ associationActions.forEach((associationAction) => {
56
+ const actionName = `${resourceName}.${fieldTarget}:${associationAction}`;
57
+ role.grantAction(actionName);
58
+ });
59
+
60
+ const targetActions = fieldActions.targetActions || [];
61
+
62
+ targetActions.forEach((targetAction) => {
63
+ const targetActionPath = `${fieldTarget}:${targetAction}`;
64
+
65
+ grantHelper.resourceTargetActionMap.set(resourceName, [
66
+ ...(grantHelper.resourceTargetActionMap.get(resourceName) || []),
67
+ targetActionPath,
68
+ ]);
69
+
70
+ grantHelper.targetActionResourceMap.set(targetActionPath, [
71
+ ...(grantHelper.targetActionResourceMap.get(targetActionPath) || []),
72
+ resourceName,
73
+ ]);
74
+
75
+ role.grantAction(targetActionPath);
76
+ });
77
+ }
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,61 @@
1
+ import { Database, Model } from '@nocobase/database';
2
+ import { ACL, ACLRole } from '@nocobase/acl';
3
+ import { RoleResourceActionModel } from './RoleResourceActionModel';
4
+ import { AssociationFieldsActions, GrantHelper } from '../server';
5
+
6
+ export class RoleResourceModel extends Model {
7
+ async revoke(options: { role: ACLRole; resourceName: string; grantHelper: GrantHelper }) {
8
+ const { role, resourceName, grantHelper } = options;
9
+ role.revokeResource(resourceName);
10
+
11
+ const targetActions = grantHelper.resourceTargetActionMap.get(resourceName) || [];
12
+
13
+ for (const targetAction of targetActions) {
14
+ const targetActionResource = (grantHelper.targetActionResourceMap.get(targetAction) || []).filter(
15
+ (item) => resourceName !== item,
16
+ );
17
+
18
+ grantHelper.targetActionResourceMap.set(targetAction, targetActionResource);
19
+ if (targetActionResource.length == 0) {
20
+ role.revokeAction(targetAction);
21
+ }
22
+ }
23
+
24
+ grantHelper.resourceTargetActionMap.set(resourceName, []);
25
+ }
26
+
27
+ async writeToACL(options: {
28
+ acl: ACL;
29
+ associationFieldsActions: AssociationFieldsActions;
30
+ grantHelper: GrantHelper;
31
+ transaction: any;
32
+ }) {
33
+ const { acl, associationFieldsActions, grantHelper } = options;
34
+ const resourceName = this.get('name') as string;
35
+ const roleName = this.get('roleName') as string;
36
+ const role = acl.getRole(roleName);
37
+
38
+ // revoke resource of role
39
+ await this.revoke({ role, resourceName, grantHelper });
40
+
41
+ // @ts-ignore
42
+ if (this.usingActionsConfig === false) {
43
+ return;
44
+ }
45
+
46
+ // @ts-ignore
47
+ const actions: RoleResourceActionModel[] = await this.getActions({
48
+ transaction: options.transaction,
49
+ });
50
+
51
+ for (const action of actions) {
52
+ await action.writeToACL({
53
+ acl,
54
+ role,
55
+ resourceName,
56
+ associationFieldsActions,
57
+ grantHelper: options.grantHelper,
58
+ });
59
+ }
60
+ }
61
+ }
package/src/server.ts ADDED
@@ -0,0 +1,398 @@
1
+ import { Context } from '@nocobase/actions';
2
+ import { Collection } from '@nocobase/database';
3
+ import { Plugin } from '@nocobase/server';
4
+ import { resolve } from 'path';
5
+ import { availableActionResource } from './actions/available-actions';
6
+ import { checkAction } from './actions/role-check';
7
+ import { roleCollectionsResource } from './actions/role-collections';
8
+ import { RoleModel } from './model/RoleModel';
9
+ import { RoleResourceActionModel } from './model/RoleResourceActionModel';
10
+ import { RoleResourceModel } from './model/RoleResourceModel';
11
+
12
+ export interface AssociationFieldAction {
13
+ associationActions: string[];
14
+ targetActions?: string[];
15
+ }
16
+
17
+ interface AssociationFieldActions {
18
+ [availableActionName: string]: AssociationFieldAction;
19
+ }
20
+
21
+ export interface AssociationFieldsActions {
22
+ [associationType: string]: AssociationFieldActions;
23
+ }
24
+
25
+ export class GrantHelper {
26
+ resourceTargetActionMap = new Map<string, string[]>();
27
+ targetActionResourceMap = new Map<string, string[]>();
28
+
29
+ constructor() {}
30
+ }
31
+
32
+ export class PluginACL extends Plugin {
33
+ associationFieldsActions: AssociationFieldsActions = {};
34
+
35
+ grantHelper = new GrantHelper();
36
+
37
+ get acl() {
38
+ return this.app.acl;
39
+ }
40
+
41
+ registerAssociationFieldAction(associationType: string, value: AssociationFieldActions) {
42
+ this.associationFieldsActions[associationType] = value;
43
+ }
44
+
45
+ registerAssociationFieldsActions() {
46
+ this.registerAssociationFieldAction('linkTo', {
47
+ view: {
48
+ associationActions: ['list', 'get'],
49
+ },
50
+ create: {
51
+ associationActions: ['add'],
52
+ targetActions: ['view'],
53
+ },
54
+ update: {
55
+ associationActions: ['add', 'remove', 'toggle'],
56
+ targetActions: ['view'],
57
+ },
58
+ });
59
+
60
+ this.registerAssociationFieldAction('attachments', {
61
+ view: {
62
+ associationActions: ['list', 'get'],
63
+ },
64
+ add: {
65
+ associationActions: ['upload', 'add'],
66
+ },
67
+ update: {
68
+ associationActions: ['update', 'add', 'remove', 'toggle'],
69
+ },
70
+ });
71
+
72
+ this.registerAssociationFieldAction('subTable', {
73
+ view: {
74
+ associationActions: ['list', 'get'],
75
+ },
76
+ create: {
77
+ associationActions: ['create'],
78
+ },
79
+ update: {
80
+ associationActions: ['update', 'destroy'],
81
+ },
82
+ });
83
+ }
84
+
85
+ async writeResourceToACL(resourceModel: RoleResourceModel, transaction) {
86
+ await resourceModel.writeToACL({
87
+ acl: this.acl,
88
+ associationFieldsActions: this.associationFieldsActions,
89
+ transaction: transaction,
90
+ grantHelper: this.grantHelper,
91
+ });
92
+ }
93
+
94
+ async writeActionToACL(actionModel: RoleResourceActionModel, transaction) {
95
+ const resource = actionModel.get('resource') as RoleResourceModel;
96
+ const role = this.acl.getRole(resource.get('roleName') as string);
97
+ await actionModel.writeToACL({
98
+ acl: this.acl,
99
+ role,
100
+ resourceName: resource.get('name') as string,
101
+ associationFieldsActions: this.associationFieldsActions,
102
+ grantHelper: this.grantHelper,
103
+ });
104
+ }
105
+
106
+ async writeRolesToACL() {
107
+ const roles = (await this.app.db.getRepository('roles').find({
108
+ appends: ['resources', 'resources.actions'],
109
+ })) as RoleModel[];
110
+ for (const role of roles) {
111
+ role.writeToAcl({ acl: this.acl });
112
+ for (const resource of role.get('resources') as RoleResourceModel[]) {
113
+ await this.writeResourceToACL(resource, null);
114
+ }
115
+ }
116
+ }
117
+
118
+ async beforeLoad() {
119
+ this.app.db.registerModels({
120
+ RoleResourceActionModel,
121
+ RoleResourceModel,
122
+ RoleModel,
123
+ });
124
+
125
+ this.registerAssociationFieldsActions();
126
+
127
+ this.app.resourcer.define(availableActionResource);
128
+ this.app.resourcer.define(roleCollectionsResource);
129
+
130
+ this.app.resourcer.registerActionHandler('roles:check', checkAction);
131
+
132
+ this.app.db.on('roles.afterSaveWithAssociations', async (model, options) => {
133
+ const { transaction } = options;
134
+
135
+ model.writeToAcl({
136
+ acl: this.acl,
137
+ });
138
+
139
+ for (const resource of (await model.getResources({ transaction })) as RoleResourceModel[]) {
140
+ await this.writeResourceToACL(resource, transaction);
141
+ }
142
+
143
+ // model is default
144
+ if (model.get('default')) {
145
+ await this.app.db.getRepository('roles').update({
146
+ values: {
147
+ default: false,
148
+ },
149
+ filter: {
150
+ 'name.$ne': model.get('name'),
151
+ },
152
+ hooks: false,
153
+ transaction,
154
+ });
155
+ }
156
+ });
157
+
158
+ this.app.db.on('roles.afterDestroy', (model) => {
159
+ const roleName = model.get('name');
160
+ this.acl.removeRole(roleName);
161
+ });
162
+
163
+ this.app.db.on('rolesResources.afterSaveWithAssociations', async (model: RoleResourceModel, options) => {
164
+ await this.writeResourceToACL(model, options.transaction);
165
+ });
166
+
167
+ this.app.db.on('rolesResourcesActions.afterUpdateWithAssociations', async (model, options) => {
168
+ const { transaction } = options;
169
+ const resource = await model.getResource({
170
+ transaction,
171
+ });
172
+
173
+ await this.writeResourceToACL(resource, transaction);
174
+ });
175
+
176
+ this.app.db.on('rolesResources.afterDestroy', async (model, options) => {
177
+ const role = this.acl.getRole(model.get('roleName'));
178
+ role.revokeResource(model.get('name'));
179
+ });
180
+
181
+ this.app.db.on('collections.afterDestroy', async (model, options) => {
182
+ const { transaction } = options;
183
+ await this.app.db.getRepository('rolesResources').destroy({
184
+ filter: {
185
+ name: model.get('name'),
186
+ },
187
+ transaction,
188
+ });
189
+ });
190
+
191
+ this.app.db.on('fields.afterCreate', async (model, options) => {
192
+ const { transaction } = options;
193
+
194
+ const collectionName = model.get('collectionName');
195
+ const fieldName = model.get('name');
196
+
197
+ const resourceActions = (await this.app.db.getRepository('rolesResourcesActions').find({
198
+ filter: {
199
+ 'resource.name': collectionName,
200
+ },
201
+ transaction,
202
+ appends: ['resource'],
203
+ })) as RoleResourceActionModel[];
204
+
205
+ for (const resourceAction of resourceActions) {
206
+ const fields = resourceAction.get('fields') as string[];
207
+ const newFields = [...fields, fieldName];
208
+
209
+ await this.app.db.getRepository('rolesResourcesActions').update({
210
+ filterByTk: resourceAction.get('id') as number,
211
+ values: {
212
+ fields: newFields,
213
+ },
214
+ transaction,
215
+ });
216
+ }
217
+ });
218
+
219
+ this.app.db.on('fields.afterDestroy', async (model, options) => {
220
+ const collectionName = model.get('collectionName');
221
+ const fieldName = model.get('name');
222
+
223
+ const resourceActions = await this.app.db.getRepository('rolesResourcesActions').find({
224
+ filter: {
225
+ 'resource.name': collectionName,
226
+ 'fields.$anyOf': [fieldName],
227
+ },
228
+ transaction: options.transaction,
229
+ });
230
+
231
+ for (const resourceAction of resourceActions) {
232
+ const fields = resourceAction.get('fields') as string[];
233
+ const newFields = fields.filter((field) => field != fieldName);
234
+
235
+ await this.app.db.getRepository('rolesResourcesActions').update({
236
+ filterByTk: resourceAction.get('id') as number,
237
+ values: {
238
+ fields: newFields,
239
+ },
240
+ transaction: options.transaction,
241
+ });
242
+ }
243
+ });
244
+
245
+ // sync database role data to acl
246
+ this.app.on('beforeStart', async () => {
247
+ await this.writeRolesToACL();
248
+ });
249
+
250
+ this.app.on('beforeInstallPlugin', async (plugin) => {
251
+ if (plugin.constructor.name !== 'UsersPlugin') {
252
+ return;
253
+ }
254
+ const roles = this.app.db.getRepository('roles');
255
+ await roles.createMany({
256
+ records: [
257
+ {
258
+ name: 'root',
259
+ title: '{{t("Root")}}',
260
+ hidden: true,
261
+ },
262
+ {
263
+ name: 'admin',
264
+ title: '{{t("Admin")}}',
265
+ allowConfigure: true,
266
+ allowNewMenu: true,
267
+ strategy: { actions: ['create', 'export', 'view', 'update', 'destroy'] },
268
+ },
269
+ {
270
+ name: 'member',
271
+ title: '{{t("Member")}}',
272
+ allowNewMenu: true,
273
+ strategy: { actions: ['view', 'update:own', 'destroy:own', 'create'] },
274
+ default: true,
275
+ },
276
+ ],
277
+ });
278
+ const rolesResourcesScopes = this.app.db.getRepository('rolesResourcesScopes');
279
+ await rolesResourcesScopes.createMany({
280
+ records: [
281
+ {
282
+ key: 'all',
283
+ name: '{{t("All records")}}',
284
+ scope: {},
285
+ },
286
+ {
287
+ key: 'own',
288
+ name: '{{t("Own records")}}',
289
+ scope: {
290
+ createdById: '{{ ctx.state.currentUser.id }}',
291
+ },
292
+ },
293
+ ],
294
+ });
295
+ });
296
+
297
+ this.app.acl.allow('roles', 'check', 'loggedIn');
298
+ this.app.acl.allow('roles', ['create', 'update', 'destroy'], 'allowConfigure');
299
+
300
+ this.app.acl.allow('roles.menuUiSchemas', ['set', 'toggle', 'list'], 'allowConfigure');
301
+
302
+ this.app.acl.allow('*', '*', (ctx) => {
303
+ return ctx.state.currentRole === 'root';
304
+ });
305
+
306
+ this.app.resourcer.use(async (ctx, next) => {
307
+ const { actionName, resourceName, params } = ctx.action;
308
+ const { showAnonymous } = params || {};
309
+ if (actionName === 'list' && resourceName === 'roles') {
310
+ if (!showAnonymous) {
311
+ ctx.action.mergeParams({
312
+ filter: {
313
+ 'name.$ne': 'anonymous',
314
+ },
315
+ });
316
+ }
317
+ }
318
+ await next();
319
+ });
320
+
321
+ this.app.acl.use(async (ctx: Context, next) => {
322
+ const { actionName, resourceName } = ctx.action;
323
+ if (actionName === 'get' || actionName === 'list') {
324
+ if (!Array.isArray(ctx?.permission?.can?.params?.fields)) {
325
+ return next();
326
+ }
327
+ let collection: Collection;
328
+ if (resourceName.includes('.')) {
329
+ const [collectionName, associationName] = resourceName.split('.');
330
+ const field = ctx.db.getCollection(collectionName)?.getField?.(associationName);
331
+ if (field.target) {
332
+ collection = ctx.db.getCollection(field.target);
333
+ }
334
+ } else {
335
+ collection = ctx.db.getCollection(resourceName);
336
+ }
337
+ if (collection && collection.hasField('createdById')) {
338
+ ctx.permission.can.params.fields.push('createdById');
339
+ }
340
+ }
341
+ return next();
342
+ });
343
+
344
+ const parseJsonTemplate = this.app.acl.parseJsonTemplate;
345
+ this.app.acl.use(async (ctx: Context, next) => {
346
+ const { actionName, resourceName, resourceOf } = ctx.action;
347
+ if (resourceName.includes('.') && resourceOf) {
348
+ if (!ctx?.permission?.can?.params) {
349
+ return next();
350
+ }
351
+ // 关联数据去掉 filter
352
+ delete ctx.permission.can.params.filter;
353
+ // 关联数据能不能处理取决于 source 是否有权限
354
+ const [collectionName] = resourceName.split('.');
355
+ const action = ctx.can({ resource: collectionName, action: actionName });
356
+ const availableAction = this.app.acl.getAvailableAction(actionName);
357
+ if (availableAction?.options?.onNewRecord) {
358
+ if (action) {
359
+ ctx.permission.skip = true;
360
+ } else {
361
+ ctx.permission.can = false;
362
+ }
363
+ } else {
364
+ const filter = parseJsonTemplate(action?.params?.filter || {}, ctx);
365
+ const sourceInstance = await ctx.db.getRepository(collectionName).findOne({
366
+ filterByTk: resourceOf,
367
+ filter,
368
+ });
369
+ if (!sourceInstance) {
370
+ ctx.permission.can = false;
371
+ }
372
+ }
373
+ }
374
+ await next();
375
+ });
376
+ }
377
+
378
+ async install() {
379
+ const repo = this.db.getRepository<any>('collections');
380
+ if (repo) {
381
+ await repo.db2cm('roles');
382
+ }
383
+ }
384
+
385
+ async load() {
386
+ await this.app.db.import({
387
+ directory: resolve(__dirname, 'collections'),
388
+ });
389
+
390
+ this.app.resourcer.use(this.acl.middleware());
391
+ }
392
+
393
+ getName(): string {
394
+ return this.getPackageName(__dirname);
395
+ }
396
+ }
397
+
398
+ export default PluginACL;
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../../tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "declaration": true
6
+ },
7
+ "include": ["./src/**/*.ts", "./src/**/*.tsx"],
8
+ "exclude": ["./src/__tests__/*", "./esm/*", "./lib/*"]
9
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "../../../tsconfig.json",
3
+ "include": ["./src/**/*.ts", "./src/**/*.tsx"],
4
+ "exclude": ["./esm/*", "./lib/*"]
5
+ }