@nocobase/acl 0.7.0-alpha.0

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 (51) hide show
  1. package/LICENSE +201 -0
  2. package/esm/acl-available-action.d.ts +16 -0
  3. package/esm/acl-available-action.js +7 -0
  4. package/esm/acl-available-action.js.map +1 -0
  5. package/esm/acl-available-strategy.d.ts +29 -0
  6. package/esm/acl-available-strategy.js +60 -0
  7. package/esm/acl-available-strategy.js.map +1 -0
  8. package/esm/acl-resource.d.ts +25 -0
  9. package/esm/acl-resource.js +42 -0
  10. package/esm/acl-resource.js.map +1 -0
  11. package/esm/acl-role.d.ts +38 -0
  12. package/esm/acl-role.js +86 -0
  13. package/esm/acl-role.js.map +1 -0
  14. package/esm/acl.d.ts +64 -0
  15. package/esm/acl.js +202 -0
  16. package/esm/acl.js.map +1 -0
  17. package/esm/allow-manager.d.ts +13 -0
  18. package/esm/allow-manager.js +80 -0
  19. package/esm/allow-manager.js.map +1 -0
  20. package/esm/index.d.ts +6 -0
  21. package/esm/index.js +7 -0
  22. package/esm/index.js.map +1 -0
  23. package/esm/skip-middleware.d.ts +6 -0
  24. package/esm/skip-middleware.js +23 -0
  25. package/esm/skip-middleware.js.map +1 -0
  26. package/lib/acl-available-action.d.ts +16 -0
  27. package/lib/acl-available-action.js +11 -0
  28. package/lib/acl-available-action.js.map +1 -0
  29. package/lib/acl-available-strategy.d.ts +29 -0
  30. package/lib/acl-available-strategy.js +68 -0
  31. package/lib/acl-available-strategy.js.map +1 -0
  32. package/lib/acl-resource.d.ts +25 -0
  33. package/lib/acl-resource.js +46 -0
  34. package/lib/acl-resource.js.map +1 -0
  35. package/lib/acl-role.d.ts +38 -0
  36. package/lib/acl-role.js +90 -0
  37. package/lib/acl-role.js.map +1 -0
  38. package/lib/acl.d.ts +64 -0
  39. package/lib/acl.js +209 -0
  40. package/lib/acl.js.map +1 -0
  41. package/lib/allow-manager.d.ts +13 -0
  42. package/lib/allow-manager.js +84 -0
  43. package/lib/allow-manager.js.map +1 -0
  44. package/lib/index.d.ts +6 -0
  45. package/lib/index.js +19 -0
  46. package/lib/index.js.map +1 -0
  47. package/lib/skip-middleware.d.ts +6 -0
  48. package/lib/skip-middleware.js +27 -0
  49. package/lib/skip-middleware.js.map +1 -0
  50. package/package.json +29 -0
  51. package/tsconfig.build.json +9 -0
package/esm/acl.js ADDED
@@ -0,0 +1,202 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import EventEmitter from 'events';
11
+ import compose from 'koa-compose';
12
+ import lodash from 'lodash';
13
+ import { AclAvailableAction } from './acl-available-action';
14
+ import { ACLAvailableStrategy, predicate } from './acl-available-strategy';
15
+ import { ACLRole } from './acl-role';
16
+ import { AllowManager } from './allow-manager';
17
+ const parse = require('json-templates');
18
+ export class ACL extends EventEmitter {
19
+ constructor() {
20
+ super();
21
+ this.availableActions = new Map();
22
+ this.availableStrategy = new Map();
23
+ this.middlewares = [];
24
+ this.allowManager = new AllowManager(this);
25
+ this.roles = new Map();
26
+ this.actionAlias = new Map();
27
+ this.configResources = [];
28
+ this.beforeGrantAction((ctx) => {
29
+ if (lodash.isPlainObject(ctx.params) && ctx.params.own) {
30
+ ctx.params = lodash.merge(ctx.params, predicate.own);
31
+ }
32
+ });
33
+ this.beforeGrantAction((ctx) => {
34
+ const actionName = this.resolveActionAlias(ctx.actionName);
35
+ if (lodash.isPlainObject(ctx.params)) {
36
+ if ((actionName === 'create' || actionName === 'update') && ctx.params.fields) {
37
+ ctx.params = Object.assign(Object.assign({}, lodash.omit(ctx.params, 'fields')), { whitelist: ctx.params.fields });
38
+ }
39
+ if (actionName === 'view' && ctx.params.fields) {
40
+ const appendFields = ['id', 'createdAt', 'updatedAt'];
41
+ ctx.params = Object.assign(Object.assign({}, lodash.omit(ctx.params, 'fields')), { fields: [...ctx.params.fields, ...appendFields] });
42
+ }
43
+ }
44
+ });
45
+ this.middlewares.push(this.allowManager.aclMiddleware());
46
+ }
47
+ define(options) {
48
+ const roleName = options.role;
49
+ const role = new ACLRole(this, roleName);
50
+ if (options.strategy) {
51
+ role.strategy = options.strategy;
52
+ }
53
+ const actions = options.actions || {};
54
+ for (const [actionName, actionParams] of Object.entries(actions)) {
55
+ role.grantAction(actionName, actionParams);
56
+ }
57
+ this.roles.set(roleName, role);
58
+ return role;
59
+ }
60
+ getRole(name) {
61
+ return this.roles.get(name);
62
+ }
63
+ removeRole(name) {
64
+ return this.roles.delete(name);
65
+ }
66
+ registerConfigResources(names) {
67
+ names.forEach((name) => this.registerConfigResource(name));
68
+ }
69
+ registerConfigResource(name) {
70
+ this.configResources.push(name);
71
+ }
72
+ isConfigResource(name) {
73
+ return this.configResources.includes(name);
74
+ }
75
+ setAvailableAction(name, options) {
76
+ this.availableActions.set(name, new AclAvailableAction(name, options));
77
+ if (options.aliases) {
78
+ const aliases = lodash.isArray(options.aliases) ? options.aliases : [options.aliases];
79
+ for (const alias of aliases) {
80
+ this.actionAlias.set(alias, name);
81
+ }
82
+ }
83
+ }
84
+ getAvailableAction(name) {
85
+ const actionName = this.actionAlias.get(name) || name;
86
+ return this.availableActions.get(actionName);
87
+ }
88
+ getAvailableActions() {
89
+ return this.availableActions;
90
+ }
91
+ setAvailableStrategy(name, options) {
92
+ this.availableStrategy.set(name, new ACLAvailableStrategy(this, options));
93
+ }
94
+ beforeGrantAction(listener) {
95
+ this.addListener('beforeGrantAction', listener);
96
+ }
97
+ can({ role, resource, action }) {
98
+ const aclRole = this.roles.get(role);
99
+ if (!aclRole) {
100
+ return null;
101
+ }
102
+ const aclResource = aclRole.getResource(resource);
103
+ if (aclResource) {
104
+ const actionParams = aclResource.getAction(action);
105
+ if (actionParams) {
106
+ // handle single action config
107
+ return {
108
+ role,
109
+ resource,
110
+ action,
111
+ params: actionParams,
112
+ };
113
+ }
114
+ }
115
+ if (!aclRole.strategy) {
116
+ return null;
117
+ }
118
+ const roleStrategy = lodash.isString(aclRole.strategy)
119
+ ? this.availableStrategy.get(aclRole.strategy)
120
+ : new ACLAvailableStrategy(this, aclRole.strategy);
121
+ if (!roleStrategy) {
122
+ return null;
123
+ }
124
+ const roleStrategyParams = roleStrategy.allow(resource, this.resolveActionAlias(action));
125
+ if (roleStrategyParams) {
126
+ const result = { role, resource, action };
127
+ if (lodash.isPlainObject(roleStrategyParams)) {
128
+ result['params'] = roleStrategyParams;
129
+ }
130
+ return result;
131
+ }
132
+ return null;
133
+ }
134
+ isAvailableAction(actionName) {
135
+ return this.availableActions.has(this.resolveActionAlias(actionName));
136
+ }
137
+ resolveActionAlias(action) {
138
+ return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;
139
+ }
140
+ use(fn) {
141
+ this.middlewares.push(fn);
142
+ }
143
+ allow(resourceName, actionNames, condition) {
144
+ if (!Array.isArray(actionNames)) {
145
+ actionNames = [actionNames];
146
+ }
147
+ for (const actionName of actionNames) {
148
+ this.allowManager.allow(resourceName, actionName, condition);
149
+ }
150
+ }
151
+ parseJsonTemplate(json, ctx) {
152
+ return parse(json)({
153
+ ctx: {
154
+ state: JSON.parse(JSON.stringify(ctx.state)),
155
+ },
156
+ });
157
+ }
158
+ middleware() {
159
+ const acl = this;
160
+ const filterParams = (ctx, resourceName, params) => {
161
+ var _a;
162
+ if ((_a = params === null || params === void 0 ? void 0 : params.filter) === null || _a === void 0 ? void 0 : _a.createdById) {
163
+ const collection = ctx.db.getCollection(resourceName);
164
+ if (collection && !collection.getField('createdById')) {
165
+ return lodash.omit(params, 'filter.createdById');
166
+ }
167
+ }
168
+ return params;
169
+ };
170
+ return function ACLMiddleware(ctx, next) {
171
+ return __awaiter(this, void 0, void 0, function* () {
172
+ const roleName = ctx.state.currentRole || 'anonymous';
173
+ const { resourceName, actionName } = ctx.action;
174
+ const resourcerAction = ctx.action;
175
+ ctx.can = (options) => {
176
+ return acl.can(Object.assign({ role: roleName }, options));
177
+ };
178
+ ctx.permission = {
179
+ can: ctx.can({ resource: resourceName, action: actionName }),
180
+ };
181
+ return compose(acl.middlewares)(ctx, () => __awaiter(this, void 0, void 0, function* () {
182
+ const permission = ctx.permission;
183
+ if (permission.skip) {
184
+ return next();
185
+ }
186
+ if (!permission.can || typeof permission.can !== 'object') {
187
+ ctx.throw(403, 'No permissions');
188
+ return;
189
+ }
190
+ const { params } = permission.can;
191
+ if (params) {
192
+ const filteredParams = filterParams(ctx, resourceName, params);
193
+ const parsedParams = acl.parseJsonTemplate(filteredParams, ctx);
194
+ resourcerAction.mergeParams(parsedParams);
195
+ }
196
+ yield next();
197
+ }));
198
+ });
199
+ };
200
+ }
201
+ }
202
+ //# sourceMappingURL=acl.js.map
package/esm/acl.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acl.js","sourceRoot":"","sources":["../src/acl.ts"],"names":[],"mappings":"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","sourcesContent":["import { Action } from '@nocobase/resourcer';\nimport EventEmitter from 'events';\nimport compose from 'koa-compose';\nimport lodash from 'lodash';\nimport { AclAvailableAction, AvailableActionOptions } from './acl-available-action';\nimport { ACLAvailableStrategy, AvailableStrategyOptions, predicate } from './acl-available-strategy';\nimport { ACLRole, RoleActionParams } from './acl-role';\nimport { AllowManager } from './allow-manager';\nconst parse = require('json-templates');\n\ninterface CanResult {\n role: string;\n resource: string;\n action: string;\n params?: any;\n}\n\nexport interface DefineOptions {\n role: string;\n allowConfigure?: boolean;\n strategy?: string | Omit<AvailableStrategyOptions, 'acl'>;\n actions?: {\n [key: string]: RoleActionParams;\n };\n routes?: any;\n}\n\nexport interface ListenerContext {\n acl: ACL;\n role: ACLRole;\n path: string;\n actionName: string;\n resourceName: string;\n params: RoleActionParams;\n}\n\ntype Listener = (ctx: ListenerContext) => void;\n\ninterface CanArgs {\n role: string;\n resource: string;\n action: string;\n}\n\nexport class ACL extends EventEmitter {\n protected availableActions = new Map<string, AclAvailableAction>();\n protected availableStrategy = new Map<string, ACLAvailableStrategy>();\n protected middlewares = [];\n\n public allowManager = new AllowManager(this);\n\n roles = new Map<string, ACLRole>();\n\n actionAlias = new Map<string, string>();\n\n configResources: string[] = [];\n\n constructor() {\n super();\n\n this.beforeGrantAction((ctx) => {\n if (lodash.isPlainObject(ctx.params) && ctx.params.own) {\n ctx.params = lodash.merge(ctx.params, predicate.own);\n }\n });\n\n this.beforeGrantAction((ctx) => {\n const actionName = this.resolveActionAlias(ctx.actionName);\n\n if (lodash.isPlainObject(ctx.params)) {\n if ((actionName === 'create' || actionName === 'update') && ctx.params.fields) {\n ctx.params = {\n ...lodash.omit(ctx.params, 'fields'),\n whitelist: ctx.params.fields,\n };\n }\n\n if (actionName === 'view' && ctx.params.fields) {\n const appendFields = ['id', 'createdAt', 'updatedAt'];\n ctx.params = {\n ...lodash.omit(ctx.params, 'fields'),\n fields: [...ctx.params.fields, ...appendFields],\n };\n }\n }\n });\n\n this.middlewares.push(this.allowManager.aclMiddleware());\n }\n\n define(options: DefineOptions): ACLRole {\n const roleName = options.role;\n const role = new ACLRole(this, roleName);\n\n if (options.strategy) {\n role.strategy = options.strategy;\n }\n\n const actions = options.actions || {};\n\n for (const [actionName, actionParams] of Object.entries(actions)) {\n role.grantAction(actionName, actionParams);\n }\n\n this.roles.set(roleName, role);\n\n return role;\n }\n\n getRole(name: string): ACLRole {\n return this.roles.get(name);\n }\n\n removeRole(name: string) {\n return this.roles.delete(name);\n }\n\n registerConfigResources(names: string[]) {\n names.forEach((name) => this.registerConfigResource(name));\n }\n\n registerConfigResource(name: string) {\n this.configResources.push(name);\n }\n\n isConfigResource(name: string) {\n return this.configResources.includes(name);\n }\n\n setAvailableAction(name: string, options: AvailableActionOptions) {\n this.availableActions.set(name, new AclAvailableAction(name, options));\n\n if (options.aliases) {\n const aliases = lodash.isArray(options.aliases) ? options.aliases : [options.aliases];\n for (const alias of aliases) {\n this.actionAlias.set(alias, name);\n }\n }\n }\n\n getAvailableAction(name: string) {\n const actionName = this.actionAlias.get(name) || name;\n return this.availableActions.get(actionName);\n }\n\n getAvailableActions() {\n return this.availableActions;\n }\n\n setAvailableStrategy(name: string, options: Omit<AvailableStrategyOptions, 'acl'>) {\n this.availableStrategy.set(name, new ACLAvailableStrategy(this, options));\n }\n\n beforeGrantAction(listener?: Listener) {\n this.addListener('beforeGrantAction', listener);\n }\n\n can({ role, resource, action }: CanArgs): CanResult | null {\n const aclRole = this.roles.get(role);\n\n if (!aclRole) {\n return null;\n }\n\n const aclResource = aclRole.getResource(resource);\n\n if (aclResource) {\n const actionParams = aclResource.getAction(action);\n\n if (actionParams) {\n // handle single action config\n return {\n role,\n resource,\n action,\n params: actionParams,\n };\n }\n }\n\n if (!aclRole.strategy) {\n return null;\n }\n\n const roleStrategy = lodash.isString(aclRole.strategy)\n ? this.availableStrategy.get(aclRole.strategy)\n : new ACLAvailableStrategy(this, aclRole.strategy);\n\n if (!roleStrategy) {\n return null;\n }\n\n const roleStrategyParams = roleStrategy.allow(resource, this.resolveActionAlias(action));\n\n if (roleStrategyParams) {\n const result = { role, resource, action };\n\n if (lodash.isPlainObject(roleStrategyParams)) {\n result['params'] = roleStrategyParams;\n }\n\n return result;\n }\n\n return null;\n }\n\n protected isAvailableAction(actionName: string) {\n return this.availableActions.has(this.resolveActionAlias(actionName));\n }\n\n public resolveActionAlias(action: string) {\n return this.actionAlias.get(action) ? this.actionAlias.get(action) : action;\n }\n\n use(fn: any) {\n this.middlewares.push(fn);\n }\n\n allow(resourceName: string, actionNames: string[] | string, condition?: any) {\n if (!Array.isArray(actionNames)) {\n actionNames = [actionNames];\n }\n\n for (const actionName of actionNames) {\n this.allowManager.allow(resourceName, actionName, condition);\n }\n }\n\n parseJsonTemplate(json: any, ctx: any) {\n return parse(json)({\n ctx: {\n state: JSON.parse(JSON.stringify(ctx.state)),\n },\n });\n }\n\n middleware() {\n const acl = this;\n\n const filterParams = (ctx, resourceName, params) => {\n if (params?.filter?.createdById) {\n const collection = ctx.db.getCollection(resourceName);\n if (collection && !collection.getField('createdById')) {\n return lodash.omit(params, 'filter.createdById');\n }\n }\n\n return params;\n };\n\n return async function ACLMiddleware(ctx, next) {\n const roleName = ctx.state.currentRole || 'anonymous';\n const { resourceName, actionName } = ctx.action;\n\n const resourcerAction: Action = ctx.action;\n\n ctx.can = (options: Omit<CanArgs, 'role'>) => {\n return acl.can({ role: roleName, ...options });\n };\n\n ctx.permission = {\n can: ctx.can({ resource: resourceName, action: actionName }),\n };\n\n return compose(acl.middlewares)(ctx, async () => {\n const permission = ctx.permission;\n\n if (permission.skip) {\n return next();\n }\n\n if (!permission.can || typeof permission.can !== 'object') {\n ctx.throw(403, 'No permissions');\n return;\n }\n\n const { params } = permission.can;\n\n if (params) {\n const filteredParams = filterParams(ctx, resourceName, params);\n const parsedParams = acl.parseJsonTemplate(filteredParams, ctx);\n resourcerAction.mergeParams(parsedParams);\n }\n\n await next();\n });\n };\n }\n}\n"]}
@@ -0,0 +1,13 @@
1
+ import { ACL } from './acl';
2
+ declare type ConditionFunc = (ctx: any) => Promise<boolean>;
3
+ export declare class AllowManager {
4
+ acl: ACL;
5
+ protected skipActions: Map<string, Map<string, string | true | ConditionFunc>>;
6
+ protected registeredCondition: Map<string, ConditionFunc>;
7
+ constructor(acl: ACL);
8
+ allow(resourceName: string, actionName: string, condition?: string | ConditionFunc): void;
9
+ getAllowedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true>;
10
+ registerAllowCondition(name: string, condition: ConditionFunc): void;
11
+ aclMiddleware(): (ctx: any, next: any) => Promise<void>;
12
+ }
13
+ export {};
@@ -0,0 +1,80 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ export class AllowManager {
11
+ constructor(acl) {
12
+ this.acl = acl;
13
+ this.skipActions = new Map();
14
+ this.registeredCondition = new Map();
15
+ this.registerAllowCondition('loggedIn', (ctx) => {
16
+ return ctx.state.currentUser;
17
+ });
18
+ this.registerAllowCondition('allowConfigure', (ctx) => __awaiter(this, void 0, void 0, function* () {
19
+ const roleName = ctx.state.currentRole;
20
+ if (!roleName) {
21
+ return false;
22
+ }
23
+ const roleInstance = yield ctx.db.getRepository('roles').findOne({
24
+ name: roleName,
25
+ });
26
+ return roleInstance === null || roleInstance === void 0 ? void 0 : roleInstance.get('allowConfigure');
27
+ }));
28
+ }
29
+ allow(resourceName, actionName, condition) {
30
+ const actionMap = this.skipActions.get(resourceName) || new Map();
31
+ actionMap.set(actionName, condition || true);
32
+ this.skipActions.set(resourceName, actionMap);
33
+ }
34
+ getAllowedConditions(resourceName, actionName) {
35
+ const fetchActionSteps = ['*', resourceName];
36
+ const results = [];
37
+ for (const fetchActionStep of fetchActionSteps) {
38
+ const resource = this.skipActions.get(fetchActionStep);
39
+ if (resource) {
40
+ const condition = resource.get('*') || resource.get(actionName);
41
+ if (condition) {
42
+ results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);
43
+ }
44
+ }
45
+ }
46
+ return results;
47
+ }
48
+ registerAllowCondition(name, condition) {
49
+ this.registeredCondition.set(name, condition);
50
+ }
51
+ aclMiddleware() {
52
+ return (ctx, next) => __awaiter(this, void 0, void 0, function* () {
53
+ const { resourceName, actionName } = ctx.action;
54
+ const skippedConditions = ctx.app.acl.allowManager.getAllowedConditions(resourceName, actionName);
55
+ let skip = false;
56
+ for (const skippedCondition of skippedConditions) {
57
+ if (skippedCondition) {
58
+ let skipResult = false;
59
+ if (typeof skippedCondition === 'function') {
60
+ skipResult = yield skippedCondition(ctx);
61
+ }
62
+ else if (skippedCondition) {
63
+ skipResult = true;
64
+ }
65
+ if (skipResult) {
66
+ skip = true;
67
+ break;
68
+ }
69
+ }
70
+ }
71
+ if (skip) {
72
+ ctx.permission = {
73
+ skip: true,
74
+ };
75
+ }
76
+ yield next();
77
+ });
78
+ }
79
+ }
80
+ //# sourceMappingURL=allow-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"allow-manager.js","sourceRoot":"","sources":["../src/allow-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { ACL } from './acl';\n\ntype ConditionFunc = (ctx: any) => Promise<boolean>;\n\nexport class AllowManager {\n protected skipActions = new Map<string, Map<string, string | ConditionFunc | true>>();\n\n protected registeredCondition = new Map<string, ConditionFunc>();\n\n constructor(public acl: ACL) {\n this.registerAllowCondition('loggedIn', (ctx) => {\n return ctx.state.currentUser;\n });\n\n this.registerAllowCondition('allowConfigure', async (ctx) => {\n const roleName = ctx.state.currentRole;\n if (!roleName) {\n return false;\n }\n\n const roleInstance = await ctx.db.getRepository('roles').findOne({\n name: roleName,\n });\n\n return roleInstance?.get('allowConfigure');\n });\n }\n\n allow(resourceName: string, actionName: string, condition?: string | ConditionFunc) {\n const actionMap = this.skipActions.get(resourceName) || new Map<string, string | ConditionFunc>();\n actionMap.set(actionName, condition || true);\n\n this.skipActions.set(resourceName, actionMap);\n }\n\n getAllowedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true> {\n const fetchActionSteps: string[] = ['*', resourceName];\n\n const results = [];\n\n for (const fetchActionStep of fetchActionSteps) {\n const resource = this.skipActions.get(fetchActionStep);\n if (resource) {\n const condition = resource.get('*') || resource.get(actionName);\n if (condition) {\n results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);\n }\n }\n }\n\n return results;\n }\n\n registerAllowCondition(name: string, condition: ConditionFunc) {\n this.registeredCondition.set(name, condition);\n }\n\n aclMiddleware() {\n return async (ctx, next) => {\n const { resourceName, actionName } = ctx.action;\n const skippedConditions = ctx.app.acl.allowManager.getAllowedConditions(resourceName, actionName);\n let skip = false;\n\n for (const skippedCondition of skippedConditions) {\n if (skippedCondition) {\n let skipResult = false;\n\n if (typeof skippedCondition === 'function') {\n skipResult = await skippedCondition(ctx);\n } else if (skippedCondition) {\n skipResult = true;\n }\n\n if (skipResult) {\n skip = true;\n break;\n }\n }\n }\n\n if (skip) {\n ctx.permission = {\n skip: true,\n };\n }\n await next();\n };\n }\n}\n"]}
package/esm/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from './acl';
2
+ export * from './acl-available-action';
3
+ export * from './acl-available-strategy';
4
+ export * from './acl-resource';
5
+ export * from './acl-role';
6
+ export * from './skip-middleware';
package/esm/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from './acl';
2
+ export * from './acl-available-action';
3
+ export * from './acl-available-strategy';
4
+ export * from './acl-resource';
5
+ export * from './acl-role';
6
+ export * from './skip-middleware';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,OAAO,CAAC;AACtB,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC","sourcesContent":["export * from './acl';\nexport * from './acl-available-action';\nexport * from './acl-available-strategy';\nexport * from './acl-resource';\nexport * from './acl-role';\nexport * from './skip-middleware';\n\n"]}
@@ -0,0 +1,6 @@
1
+ export declare const skip: (options: ACLSkipOptions) => (ctx: any, next: any) => Promise<void>;
2
+ interface ACLSkipOptions {
3
+ resourceName: string;
4
+ actionName: string;
5
+ }
6
+ export {};
@@ -0,0 +1,23 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ export const skip = (options) => {
11
+ return function ACLSkipMiddleware(ctx, next) {
12
+ return __awaiter(this, void 0, void 0, function* () {
13
+ const { resourceName, actionName } = ctx.action;
14
+ if (resourceName === options.resourceName && actionName === options.actionName) {
15
+ ctx.permission = {
16
+ skip: true,
17
+ };
18
+ }
19
+ yield next();
20
+ });
21
+ };
22
+ };
23
+ //# sourceMappingURL=skip-middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skip-middleware.js","sourceRoot":"","sources":["../src/skip-middleware.ts"],"names":[],"mappings":";;;;;;;;;AAAA,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,OAAuB,EAAE,EAAE;IAC9C,OAAO,SAAe,iBAAiB,CAAC,GAAG,EAAE,IAAI;;YAC/C,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;YAChD,IAAI,YAAY,KAAK,OAAO,CAAC,YAAY,IAAI,UAAU,KAAK,OAAO,CAAC,UAAU,EAAE;gBAC9E,GAAG,CAAC,UAAU,GAAG;oBACf,IAAI,EAAE,IAAI;iBACX,CAAC;aACH;YACD,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;KAAA,CAAC;AACJ,CAAC,CAAC","sourcesContent":["export const skip = (options: ACLSkipOptions) => {\n return async function ACLSkipMiddleware(ctx, next) {\n const { resourceName, actionName } = ctx.action;\n if (resourceName === options.resourceName && actionName === options.actionName) {\n ctx.permission = {\n skip: true,\n };\n }\n await next();\n };\n};\n\ninterface ACLSkipOptions {\n resourceName: string;\n actionName: string;\n}\n"]}
@@ -0,0 +1,16 @@
1
+ export interface AvailableActionOptions {
2
+ /**
3
+ * @deprecated
4
+ */
5
+ type: 'new-data' | 'old-data';
6
+ displayName?: string;
7
+ aliases?: string[] | string;
8
+ resource?: string;
9
+ onNewRecord?: boolean;
10
+ allowConfigureFields?: boolean;
11
+ }
12
+ export declare class AclAvailableAction {
13
+ name: string;
14
+ options: AvailableActionOptions;
15
+ constructor(name: string, options: AvailableActionOptions);
16
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AclAvailableAction = void 0;
4
+ class AclAvailableAction {
5
+ constructor(name, options) {
6
+ this.name = name;
7
+ this.options = options;
8
+ }
9
+ }
10
+ exports.AclAvailableAction = AclAvailableAction;
11
+ //# sourceMappingURL=acl-available-action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acl-available-action.js","sourceRoot":"","sources":["../src/acl-available-action.ts"],"names":[],"mappings":";;;AAcA,MAAa,kBAAkB;IAC7B,YAAmB,IAAY,EAAS,OAA+B;QAApD,SAAI,GAAJ,IAAI,CAAQ;QAAS,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;CAC5E;AAFD,gDAEC","sourcesContent":["export interface AvailableActionOptions {\n /**\n * @deprecated\n */\n type: 'new-data' | 'old-data';\n displayName?: string;\n aliases?: string[] | string;\n resource?: string;\n // 对新数据进行操作\n onNewRecord?: boolean;\n // 允许配置字段\n allowConfigureFields?: boolean;\n}\n\nexport class AclAvailableAction {\n constructor(public name: string, public options: AvailableActionOptions) {}\n}\n"]}
@@ -0,0 +1,29 @@
1
+ import { ACL } from './acl';
2
+ declare type StrategyValue = false | '*' | string | string[];
3
+ export interface AvailableStrategyOptions {
4
+ displayName?: string;
5
+ actions?: false | string | string[];
6
+ allowConfigure?: boolean;
7
+ resource?: '*';
8
+ }
9
+ export declare function strategyValueMatched(strategy: StrategyValue, value: string): boolean;
10
+ export declare const predicate: {
11
+ own: {
12
+ filter: {
13
+ createdById: string;
14
+ };
15
+ };
16
+ all: {};
17
+ };
18
+ export declare class ACLAvailableStrategy {
19
+ acl: ACL;
20
+ options: AvailableStrategyOptions;
21
+ actionsAsObject: {
22
+ [key: string]: string;
23
+ };
24
+ allowConfigure: boolean;
25
+ constructor(acl: ACL, options: AvailableStrategyOptions);
26
+ matchAction(actionName: string): any;
27
+ allow(resourceName: string, actionName: string): any;
28
+ }
29
+ export {};
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ACLAvailableStrategy = exports.predicate = exports.strategyValueMatched = void 0;
7
+ const lodash_1 = __importDefault(require("lodash"));
8
+ function strategyValueMatched(strategy, value) {
9
+ if (strategy === '*') {
10
+ return true;
11
+ }
12
+ if (lodash_1.default.isString(strategy) && strategy === value) {
13
+ return true;
14
+ }
15
+ if (lodash_1.default.isArray(strategy) && strategy.includes(value)) {
16
+ return true;
17
+ }
18
+ return false;
19
+ }
20
+ exports.strategyValueMatched = strategyValueMatched;
21
+ exports.predicate = {
22
+ own: {
23
+ filter: {
24
+ createdById: '{{ ctx.state.currentUser.id }}',
25
+ },
26
+ },
27
+ all: {},
28
+ };
29
+ class ACLAvailableStrategy {
30
+ constructor(acl, options) {
31
+ this.acl = acl;
32
+ this.options = options;
33
+ this.allowConfigure = options.allowConfigure;
34
+ let actions = this.options.actions;
35
+ if (lodash_1.default.isString(actions) && actions != '*') {
36
+ actions = [actions];
37
+ }
38
+ if (lodash_1.default.isArray(actions)) {
39
+ this.actionsAsObject = actions.reduce((carry, action) => {
40
+ const [actionName, predicate] = action.split(':');
41
+ carry[actionName] = predicate;
42
+ return carry;
43
+ }, {});
44
+ }
45
+ }
46
+ matchAction(actionName) {
47
+ var _a;
48
+ if (this.options.actions == '*') {
49
+ return true;
50
+ }
51
+ if ((_a = this.actionsAsObject) === null || _a === void 0 ? void 0 : _a.hasOwnProperty(actionName)) {
52
+ const predicateName = this.actionsAsObject[actionName];
53
+ if (predicateName) {
54
+ return exports.predicate[predicateName];
55
+ }
56
+ return true;
57
+ }
58
+ return false;
59
+ }
60
+ allow(resourceName, actionName) {
61
+ if (this.acl.isConfigResource(resourceName) && this.allowConfigure) {
62
+ return true;
63
+ }
64
+ return this.matchAction(this.acl.resolveActionAlias(actionName));
65
+ }
66
+ }
67
+ exports.ACLAvailableStrategy = ACLAvailableStrategy;
68
+ //# sourceMappingURL=acl-available-strategy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acl-available-strategy.js","sourceRoot":"","sources":["../src/acl-available-strategy.ts"],"names":[],"mappings":";;;;;;AAAA,oDAA4B;AAW5B,SAAgB,oBAAoB,CAAC,QAAuB,EAAE,KAAa;IACzE,IAAI,QAAQ,KAAK,GAAG,EAAE;QACpB,OAAO,IAAI,CAAC;KACb;IAED,IAAI,gBAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,KAAK,EAAE;QACnD,OAAO,IAAI,CAAC;KACb;IAED,IAAI,gBAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE;QACxD,OAAO,IAAI,CAAC;KACb;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAdD,oDAcC;AAEY,QAAA,SAAS,GAAG;IACvB,GAAG,EAAE;QACH,MAAM,EAAE;YACN,WAAW,EAAE,gCAAgC;SAC9C;KACF;IACD,GAAG,EAAE,EAAE;CACR,CAAC;AAEF,MAAa,oBAAoB;IAO/B,YAAY,GAAQ,EAAE,OAAiC;QACrD,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;QAE7C,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QACnC,IAAI,gBAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,IAAI,GAAG,EAAE;YAC9C,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC;SACrB;QAED,IAAI,gBAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE;YAC3B,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;gBACtD,MAAM,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAClD,KAAK,CAAC,UAAU,CAAC,GAAG,SAAS,CAAC;gBAC9B,OAAO,KAAK,CAAC;YACf,CAAC,EAAE,EAAE,CAAC,CAAC;SACR;IACH,CAAC;IAED,WAAW,CAAC,UAAkB;;QAC5B,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,GAAG,EAAE;YAC/B,OAAO,IAAI,CAAC;SACb;QAED,IAAI,MAAA,IAAI,CAAC,eAAe,0CAAE,cAAc,CAAC,UAAU,CAAC,EAAE;YACpD,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACvD,IAAI,aAAa,EAAE;gBACjB,OAAO,iBAAS,CAAC,aAAa,CAAC,CAAC;aACjC;YAED,OAAO,IAAI,CAAC;SACb;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,YAAoB,EAAE,UAAkB;QAC5C,IAAI,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,YAAY,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE;YAClE,OAAO,IAAI,CAAC;SACb;QAED,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,UAAU,CAAC,CAAC,CAAC;IACnE,CAAC;CACF;AAlDD,oDAkDC","sourcesContent":["import lodash from 'lodash';\nimport { ACL } from './acl';\ntype StrategyValue = false | '*' | string | string[];\n\nexport interface AvailableStrategyOptions {\n displayName?: string;\n actions?: false | string | string[];\n allowConfigure?: boolean;\n resource?: '*';\n}\n\nexport function strategyValueMatched(strategy: StrategyValue, value: string) {\n if (strategy === '*') {\n return true;\n }\n\n if (lodash.isString(strategy) && strategy === value) {\n return true;\n }\n\n if (lodash.isArray(strategy) && strategy.includes(value)) {\n return true;\n }\n\n return false;\n}\n\nexport const predicate = {\n own: {\n filter: {\n createdById: '{{ ctx.state.currentUser.id }}',\n },\n },\n all: {},\n};\n\nexport class ACLAvailableStrategy {\n acl: ACL;\n options: AvailableStrategyOptions;\n actionsAsObject: { [key: string]: string };\n\n allowConfigure: boolean;\n\n constructor(acl: ACL, options: AvailableStrategyOptions) {\n this.acl = acl;\n this.options = options;\n this.allowConfigure = options.allowConfigure;\n\n let actions = this.options.actions;\n if (lodash.isString(actions) && actions != '*') {\n actions = [actions];\n }\n\n if (lodash.isArray(actions)) {\n this.actionsAsObject = actions.reduce((carry, action) => {\n const [actionName, predicate] = action.split(':');\n carry[actionName] = predicate;\n return carry;\n }, {});\n }\n }\n\n matchAction(actionName: string) {\n if (this.options.actions == '*') {\n return true;\n }\n\n if (this.actionsAsObject?.hasOwnProperty(actionName)) {\n const predicateName = this.actionsAsObject[actionName];\n if (predicateName) {\n return predicate[predicateName];\n }\n\n return true;\n }\n\n return false;\n }\n\n allow(resourceName: string, actionName: string) {\n if (this.acl.isConfigResource(resourceName) && this.allowConfigure) {\n return true;\n }\n\n return this.matchAction(this.acl.resolveActionAlias(actionName));\n }\n}\n"]}
@@ -0,0 +1,25 @@
1
+ import { ACLRole, RoleActionParams } from './acl-role';
2
+ import { ACL } from './acl';
3
+ export declare type ResourceActions = {
4
+ [key: string]: RoleActionParams;
5
+ };
6
+ interface AclResourceOptions {
7
+ name: string;
8
+ role: ACLRole;
9
+ actions?: ResourceActions;
10
+ }
11
+ export declare class ACLResource {
12
+ actions: Map<string, RoleActionParams>;
13
+ acl: ACL;
14
+ role: ACLRole;
15
+ name: string;
16
+ constructor(options: AclResourceOptions);
17
+ getActions(): {};
18
+ getAction(name: string): RoleActionParams;
19
+ setAction(name: string, params: RoleActionParams): void;
20
+ setActions(actions: {
21
+ [key: string]: RoleActionParams;
22
+ }): void;
23
+ removeAction(name: string): void;
24
+ }
25
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ACLResource = void 0;
4
+ class ACLResource {
5
+ constructor(options) {
6
+ this.actions = new Map();
7
+ this.acl = options.role.acl;
8
+ this.role = options.role;
9
+ this.name = options.name;
10
+ const actionsOption = options.actions || {};
11
+ for (const actionName of Object.keys(actionsOption)) {
12
+ this.actions.set(actionName, actionsOption[actionName]);
13
+ }
14
+ }
15
+ getActions() {
16
+ return Array.from(this.actions.keys()).reduce((carry, key) => {
17
+ carry[key] = this.actions.get(key);
18
+ return carry;
19
+ }, {});
20
+ }
21
+ getAction(name) {
22
+ return this.actions.get(name) || this.actions.get(this.acl.resolveActionAlias(name));
23
+ }
24
+ setAction(name, params) {
25
+ const context = {
26
+ role: this.role,
27
+ acl: this.role.acl,
28
+ params: params || {},
29
+ path: `${this.name}:${name}`,
30
+ resourceName: this.name,
31
+ actionName: name,
32
+ };
33
+ this.acl.emit('beforeGrantAction', context);
34
+ this.actions.set(name, context.params);
35
+ }
36
+ setActions(actions) {
37
+ for (const actionName of Object.keys(actions)) {
38
+ this.setAction(actionName, actions[actionName]);
39
+ }
40
+ }
41
+ removeAction(name) {
42
+ this.actions.delete(name);
43
+ }
44
+ }
45
+ exports.ACLResource = ACLResource;
46
+ //# sourceMappingURL=acl-resource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acl-resource.js","sourceRoot":"","sources":["../src/acl-resource.ts"],"names":[],"mappings":";;;AAWA,MAAa,WAAW;IAMtB,YAAY,OAA2B;QALvC,YAAO,GAAG,IAAI,GAAG,EAA4B,CAAC;QAM5C,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC;QAE5B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QAEzB,MAAM,aAAa,GAAoB,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;QAC7D,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE;YACnD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC;SACzD;IACH,CAAC;IAED,UAAU;QACR,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;YAC3D,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACnC,OAAO,KAAK,CAAC;QACf,CAAC,EAAE,EAAE,CAAC,CAAC;IACT,CAAC;IAED,SAAS,CAAC,IAAY;QACpB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,SAAS,CAAC,IAAY,EAAE,MAAwB;QAC9C,MAAM,OAAO,GAAoB;YAC/B,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG;YAClB,MAAM,EAAE,MAAM,IAAI,EAAE;YACpB,IAAI,EAAE,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,EAAE;YAC5B,YAAY,EAAE,IAAI,CAAC,IAAI;YACvB,UAAU,EAAE,IAAI;SACjB,CAAC;QAEF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAE5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,UAAU,CAAC,OAA4C;QACrD,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAC7C,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;SACjD;IACH,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;CACF;AArDD,kCAqDC","sourcesContent":["import { ACLRole, RoleActionParams } from './acl-role';\nimport { ACL, ListenerContext } from './acl';\n\nexport type ResourceActions = { [key: string]: RoleActionParams };\n\ninterface AclResourceOptions {\n name: string;\n role: ACLRole;\n actions?: ResourceActions;\n}\n\nexport class ACLResource {\n actions = new Map<string, RoleActionParams>();\n acl: ACL;\n role: ACLRole;\n name: string;\n\n constructor(options: AclResourceOptions) {\n this.acl = options.role.acl;\n\n this.role = options.role;\n this.name = options.name;\n\n const actionsOption: ResourceActions = options.actions || {};\n for (const actionName of Object.keys(actionsOption)) {\n this.actions.set(actionName, actionsOption[actionName]);\n }\n }\n\n getActions() {\n return Array.from(this.actions.keys()).reduce((carry, key) => {\n carry[key] = this.actions.get(key);\n return carry;\n }, {});\n }\n\n getAction(name: string) {\n return this.actions.get(name) || this.actions.get(this.acl.resolveActionAlias(name));\n }\n\n setAction(name: string, params: RoleActionParams) {\n const context: ListenerContext = {\n role: this.role,\n acl: this.role.acl,\n params: params || {},\n path: `${this.name}:${name}`,\n resourceName: this.name,\n actionName: name,\n };\n\n this.acl.emit('beforeGrantAction', context);\n\n this.actions.set(name, context.params);\n }\n\n setActions(actions: { [key: string]: RoleActionParams }) {\n for (const actionName of Object.keys(actions)) {\n this.setAction(actionName, actions[actionName]);\n }\n }\n\n removeAction(name: string) {\n this.actions.delete(name);\n }\n}\n"]}