@nocobase/acl 0.8.1-alpha.3 → 0.9.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.
@@ -78,7 +78,7 @@ class ACLAvailableStrategy {
78
78
  const predicateName = this.actionsAsObject[actionName];
79
79
 
80
80
  if (predicateName) {
81
- return predicate[predicateName];
81
+ return _lodash().default.cloneDeep(predicate[predicateName]);
82
82
  }
83
83
 
84
84
  return true;
@@ -88,10 +88,6 @@ class ACLAvailableStrategy {
88
88
  }
89
89
 
90
90
  allow(resourceName, actionName) {
91
- if (this.acl.isConfigResource(resourceName) && this.allowConfigure) {
92
- return true;
93
- }
94
-
95
91
  return this.matchAction(this.acl.resolveActionAlias(actionName));
96
92
  }
97
93
 
package/lib/acl-role.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ACL, DefineOptions } from './acl';
2
- import { AvailableStrategyOptions } from './acl-available-strategy';
2
+ import { ACLAvailableStrategy, AvailableStrategyOptions } from './acl-available-strategy';
3
3
  import { ACLResource } from './acl-resource';
4
4
  export interface RoleActionParams {
5
5
  fields?: string[];
@@ -17,14 +17,21 @@ export declare class ACLRole {
17
17
  name: string;
18
18
  strategy: string | AvailableStrategyOptions;
19
19
  resources: Map<string, ACLResource>;
20
+ snippets: Set<string>;
20
21
  constructor(acl: ACL, name: string);
21
22
  getResource(name: string): ACLResource | undefined;
22
23
  setStrategy(value: string | AvailableStrategyOptions): void;
24
+ getStrategy(): ACLAvailableStrategy;
23
25
  getResourceActionsParams(resourceName: string): {};
24
26
  revokeResource(resourceName: string): void;
25
27
  grantAction(path: string, options?: RoleActionParams): void;
26
28
  getActionParams(path: string): RoleActionParams;
27
29
  revokeAction(path: string): void;
30
+ effectiveSnippets(): {
31
+ allowed: Array<string>;
32
+ rejected: Array<string>;
33
+ };
34
+ snippetAllowed(actionPath: string): boolean;
28
35
  toJSON(): DefineOptions;
29
36
  protected getResourceActionFromPath(path: string): {
30
37
  resourceName: string;
package/lib/acl-role.js CHANGED
@@ -5,8 +5,32 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.ACLRole = void 0;
7
7
 
8
+ var _aclAvailableStrategy = require("./acl-available-strategy");
9
+
8
10
  var _aclResource = require("./acl-resource");
9
11
 
12
+ function _lodash() {
13
+ const data = _interopRequireDefault(require("lodash"));
14
+
15
+ _lodash = function _lodash() {
16
+ return data;
17
+ };
18
+
19
+ return data;
20
+ }
21
+
22
+ function _minimatch() {
23
+ const data = _interopRequireDefault(require("minimatch"));
24
+
25
+ _minimatch = function _minimatch() {
26
+ return data;
27
+ };
28
+
29
+ return data;
30
+ }
31
+
32
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
+
10
34
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
11
35
 
12
36
  function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
@@ -27,6 +51,7 @@ class ACLRole {
27
51
  this.name = void 0;
28
52
  this.strategy = void 0;
29
53
  this.resources = new Map();
54
+ this.snippets = new Set();
30
55
  this.acl = acl;
31
56
  this.name = name;
32
57
  }
@@ -39,6 +64,14 @@ class ACLRole {
39
64
  this.strategy = value;
40
65
  }
41
66
 
67
+ getStrategy() {
68
+ if (!this.strategy) {
69
+ return null;
70
+ }
71
+
72
+ return _lodash().default.isString(this.strategy) ? this.acl.availableStrategy.get(this.strategy) : new _aclAvailableStrategy.ACLAvailableStrategy(this.acl, this.strategy);
73
+ }
74
+
42
75
  getResourceActionsParams(resourceName) {
43
76
  const resource = this.getResource(resourceName);
44
77
  return resource.getActions();
@@ -86,15 +119,117 @@ class ACLRole {
86
119
  resource.removeAction(actionName);
87
120
  }
88
121
 
89
- toJSON() {
90
- const actions = {};
122
+ effectiveSnippets() {
123
+ const allowedSnippets = new Set();
124
+ const rejectedSnippets = new Set();
125
+ const availableSnippets = this.acl.snippetManager.snippets;
91
126
 
92
- var _iterator = _createForOfIteratorHelper(this.resources.keys()),
127
+ var _iterator = _createForOfIteratorHelper(this.snippets),
93
128
  _step;
94
129
 
95
130
  try {
96
131
  for (_iterator.s(); !(_step = _iterator.n()).done;) {
97
- const resourceName = _step.value;
132
+ let snippetRule = _step.value;
133
+ const negated = snippetRule.startsWith('!');
134
+ snippetRule = negated ? snippetRule.slice(1) : snippetRule;
135
+
136
+ var _iterator2 = _createForOfIteratorHelper(availableSnippets),
137
+ _step2;
138
+
139
+ try {
140
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
141
+ const _step2$value = _slicedToArray(_step2.value, 2),
142
+ _ = _step2$value[0],
143
+ availableSnippet = _step2$value[1];
144
+
145
+ if ((0, _minimatch().default)(availableSnippet.name, snippetRule)) {
146
+ if (negated) {
147
+ rejectedSnippets.add(availableSnippet.name);
148
+ } else {
149
+ allowedSnippets.add(availableSnippet.name);
150
+ }
151
+ }
152
+ }
153
+ } catch (err) {
154
+ _iterator2.e(err);
155
+ } finally {
156
+ _iterator2.f();
157
+ }
158
+ } // get difference of allowed and rejected snippets
159
+
160
+ } catch (err) {
161
+ _iterator.e(err);
162
+ } finally {
163
+ _iterator.f();
164
+ }
165
+
166
+ const effectiveSnippets = new Set([...allowedSnippets].filter(x => !rejectedSnippets.has(x)));
167
+ return {
168
+ allowed: [...effectiveSnippets],
169
+ rejected: [...rejectedSnippets]
170
+ };
171
+ }
172
+
173
+ snippetAllowed(actionPath) {
174
+ const effectiveSnippets = this.effectiveSnippets();
175
+
176
+ const getActions = snippets => {
177
+ return snippets.map(snippetName => this.acl.snippetManager.snippets.get(snippetName).actions).flat();
178
+ };
179
+
180
+ const allowedActions = getActions(effectiveSnippets.allowed);
181
+ const rejectedActions = getActions(effectiveSnippets.rejected);
182
+
183
+ const actionMatched = (actionPath, actionRule) => {
184
+ return (0, _minimatch().default)(actionPath, actionRule);
185
+ };
186
+
187
+ var _iterator3 = _createForOfIteratorHelper(allowedActions),
188
+ _step3;
189
+
190
+ try {
191
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
192
+ const action = _step3.value;
193
+
194
+ if (actionMatched(actionPath, action)) {
195
+ return true;
196
+ }
197
+ }
198
+ } catch (err) {
199
+ _iterator3.e(err);
200
+ } finally {
201
+ _iterator3.f();
202
+ }
203
+
204
+ var _iterator4 = _createForOfIteratorHelper(rejectedActions),
205
+ _step4;
206
+
207
+ try {
208
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
209
+ const action = _step4.value;
210
+
211
+ if (actionMatched(actionPath, action)) {
212
+ return false;
213
+ }
214
+ }
215
+ } catch (err) {
216
+ _iterator4.e(err);
217
+ } finally {
218
+ _iterator4.f();
219
+ }
220
+
221
+ return null;
222
+ }
223
+
224
+ toJSON() {
225
+ const actions = {};
226
+
227
+ var _iterator5 = _createForOfIteratorHelper(this.resources.keys()),
228
+ _step5;
229
+
230
+ try {
231
+ for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) {
232
+ const resourceName = _step5.value;
98
233
  const resourceActions = this.getResourceActionsParams(resourceName);
99
234
 
100
235
  for (var _i2 = 0, _Object$keys = Object.keys(resourceActions); _i2 < _Object$keys.length; _i2++) {
@@ -103,15 +238,16 @@ class ACLRole {
103
238
  }
104
239
  }
105
240
  } catch (err) {
106
- _iterator.e(err);
241
+ _iterator5.e(err);
107
242
  } finally {
108
- _iterator.f();
243
+ _iterator5.f();
109
244
  }
110
245
 
111
246
  return {
112
247
  role: this.name,
113
248
  strategy: this.strategy,
114
- actions
249
+ actions,
250
+ snippets: Array.from(this.snippets)
115
251
  };
116
252
  }
117
253
 
package/lib/acl.d.ts CHANGED
@@ -5,6 +5,8 @@ import { ACLAvailableAction, AvailableActionOptions } from './acl-available-acti
5
5
  import { ACLAvailableStrategy, AvailableStrategyOptions } from './acl-available-strategy';
6
6
  import { ACLRole, ResourceActionsOptions, RoleActionParams } from './acl-role';
7
7
  import { AllowManager, ConditionFunc } from './allow-manager';
8
+ import FixedParamsManager, { Merger } from './fixed-params-manager';
9
+ import SnippetManager, { SnippetOptions } from './snippet-manager';
8
10
  interface CanResult {
9
11
  role: string;
10
12
  resource: string;
@@ -17,6 +19,7 @@ export interface DefineOptions {
17
19
  strategy?: string | AvailableStrategyOptions;
18
20
  actions?: ResourceActionsOptions;
19
21
  routes?: any;
22
+ snippets?: string[];
20
23
  }
21
24
  export interface ListenerContext {
22
25
  acl: ACL;
@@ -31,16 +34,20 @@ interface CanArgs {
31
34
  role: string;
32
35
  resource: string;
33
36
  action: string;
37
+ ctx?: any;
34
38
  }
35
39
  export declare class ACL extends EventEmitter {
36
40
  protected availableActions: Map<string, ACLAvailableAction>;
37
- protected availableStrategy: Map<string, ACLAvailableStrategy>;
41
+ availableStrategy: Map<string, ACLAvailableStrategy>;
42
+ protected fixedParamsManager: FixedParamsManager;
38
43
  protected middlewares: Toposort<any>;
39
44
  allowManager: AllowManager;
45
+ snippetManager: SnippetManager;
40
46
  roles: Map<string, ACLRole>;
41
47
  actionAlias: Map<string, string>;
42
48
  configResources: string[];
43
49
  constructor();
50
+ protected addCoreMiddleware(): void;
44
51
  define(options: DefineOptions): ACLRole;
45
52
  getRole(name: string): ACLRole;
46
53
  removeRole(name: string): boolean;
@@ -57,7 +64,11 @@ export declare class ACL extends EventEmitter {
57
64
  resolveActionAlias(action: string): string;
58
65
  use(fn: any, options?: ToposortOptions): void;
59
66
  allow(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc): void;
67
+ skip(resourceName: string, actionNames: string[] | string, condition?: string | ConditionFunc): void;
60
68
  parseJsonTemplate(json: any, ctx: any): any;
61
69
  middleware(): (ctx: any, next: any) => Promise<void>;
70
+ getActionParams(ctx: any): Promise<void>;
71
+ addFixedParams(resource: string, action: string, merger: Merger): void;
72
+ registerSnippet(snippet: SnippetOptions): void;
62
73
  }
63
74
  export {};
package/lib/acl.js CHANGED
@@ -63,11 +63,11 @@ var _aclRole = require("./acl-role");
63
63
 
64
64
  var _allowManager = require("./allow-manager");
65
65
 
66
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
66
+ var _fixedParamsManager = _interopRequireDefault(require("./fixed-params-manager"));
67
67
 
68
- function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
68
+ var _snippetManager = _interopRequireDefault(require("./snippet-manager"));
69
69
 
70
- function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
70
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
71
71
 
72
72
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
73
73
 
@@ -83,6 +83,10 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
83
83
 
84
84
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
85
85
 
86
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
87
+
88
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
89
+
86
90
  function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
87
91
 
88
92
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
@@ -94,8 +98,10 @@ class ACL extends _events().default {
94
98
  super();
95
99
  this.availableActions = new Map();
96
100
  this.availableStrategy = new Map();
101
+ this.fixedParamsManager = new _fixedParamsManager.default();
97
102
  this.middlewares = void 0;
98
103
  this.allowManager = new _allowManager.AllowManager(this);
104
+ this.snippetManager = new _snippetManager.default();
99
105
  this.roles = new Map();
100
106
  this.actionAlias = new Map();
101
107
  this.configResources = [];
@@ -123,7 +129,73 @@ class ACL extends _events().default {
123
129
  }
124
130
  }
125
131
  });
126
- this.middlewares.add(this.allowManager.aclMiddleware());
132
+ this.use(this.allowManager.aclMiddleware(), {
133
+ tag: 'allow-manager',
134
+ before: 'core'
135
+ });
136
+ this.addCoreMiddleware();
137
+ }
138
+
139
+ addCoreMiddleware() {
140
+ const acl = this;
141
+
142
+ const filterParams = (ctx, resourceName, params) => {
143
+ var _params$filter;
144
+
145
+ if (params === null || params === void 0 ? void 0 : (_params$filter = params.filter) === null || _params$filter === void 0 ? void 0 : _params$filter.createdById) {
146
+ const collection = ctx.db.getCollection(resourceName);
147
+
148
+ if (!collection || !collection.getField('createdById')) {
149
+ return _lodash().default.omit(params, 'filter.createdById');
150
+ }
151
+ }
152
+
153
+ return params;
154
+ };
155
+
156
+ this.middlewares.add( /*#__PURE__*/function () {
157
+ var _ref = _asyncToGenerator(function* (ctx, next) {
158
+ var _ctx$log, _permission$can, _ctx$log2;
159
+
160
+ const resourcerAction = ctx.action;
161
+ const _ctx$action = ctx.action,
162
+ resourceName = _ctx$action.resourceName,
163
+ actionName = _ctx$action.actionName;
164
+ const permission = ctx.permission;
165
+ ((_ctx$log = ctx.log) === null || _ctx$log === void 0 ? void 0 : _ctx$log.info) && ctx.log.info('ctx permission', permission);
166
+
167
+ if ((!permission.can || typeof permission.can !== 'object') && !permission.skip) {
168
+ ctx.throw(403, 'No permissions');
169
+ return;
170
+ }
171
+
172
+ const params = ((_permission$can = permission.can) === null || _permission$can === void 0 ? void 0 : _permission$can.params) || acl.fixedParamsManager.getParams(resourceName, actionName);
173
+ ((_ctx$log2 = ctx.log) === null || _ctx$log2 === void 0 ? void 0 : _ctx$log2.info) && ctx.log.info('acl params', params);
174
+
175
+ if (params && resourcerAction.mergeParams) {
176
+ var _ctx$log3;
177
+
178
+ const filteredParams = filterParams(ctx, resourceName, params);
179
+ const parsedParams = acl.parseJsonTemplate(filteredParams, ctx);
180
+ ctx.permission.parsedParams = parsedParams;
181
+ ((_ctx$log3 = ctx.log) === null || _ctx$log3 === void 0 ? void 0 : _ctx$log3.info) && ctx.log.info('acl parsedParams', parsedParams);
182
+ ctx.permission.rawParams = _lodash().default.cloneDeep(resourcerAction.params);
183
+ resourcerAction.mergeParams(parsedParams, {
184
+ appends: (x, y) => _lodash().default.intersection(x, y)
185
+ });
186
+ ctx.permission.mergedParams = _lodash().default.cloneDeep(resourcerAction.params);
187
+ }
188
+
189
+ yield next();
190
+ });
191
+
192
+ return function (_x, _x2) {
193
+ return _ref.apply(this, arguments);
194
+ };
195
+ }(), {
196
+ tag: 'core',
197
+ group: 'core'
198
+ });
127
199
  }
128
200
 
129
201
  define(options) {
@@ -217,6 +289,27 @@ class ACL extends _events().default {
217
289
  return null;
218
290
  }
219
291
 
292
+ const snippetAllowed = aclRole.snippetAllowed(`${resource}:${action}`);
293
+
294
+ if (snippetAllowed === false) {
295
+ return null;
296
+ }
297
+
298
+ const fixedParams = this.fixedParamsManager.getParams(resource, action);
299
+
300
+ const mergeParams = result => {
301
+ const params = result['params'] || {};
302
+ const mergedParams = (0, _utils().assign)(params, fixedParams);
303
+
304
+ if (Object.keys(mergedParams).length) {
305
+ result['params'] = mergedParams;
306
+ } else {
307
+ delete result['params'];
308
+ }
309
+
310
+ return result;
311
+ };
312
+
220
313
  const aclResource = aclRole.getResource(resource);
221
314
 
222
315
  if (aclResource) {
@@ -224,41 +317,42 @@ class ACL extends _events().default {
224
317
 
225
318
  if (actionParams) {
226
319
  // handle single action config
227
- return {
320
+ return mergeParams({
228
321
  role,
229
322
  resource,
230
323
  action,
231
324
  params: actionParams
232
- };
325
+ });
233
326
  } else {
234
327
  return null;
235
328
  }
236
329
  }
237
330
 
238
- if (!aclRole.strategy) {
331
+ const roleStrategy = aclRole.getStrategy();
332
+
333
+ if (!roleStrategy && !snippetAllowed) {
239
334
  return null;
240
335
  }
241
336
 
242
- const roleStrategy = _lodash().default.isString(aclRole.strategy) ? this.availableStrategy.get(aclRole.strategy) : new _aclAvailableStrategy.ACLAvailableStrategy(this, aclRole.strategy);
337
+ let roleStrategyParams = roleStrategy === null || roleStrategy === void 0 ? void 0 : roleStrategy.allow(resource, this.resolveActionAlias(action));
243
338
 
244
- if (!roleStrategy) {
245
- return null;
339
+ if (!roleStrategyParams && snippetAllowed) {
340
+ roleStrategyParams = {};
246
341
  }
247
342
 
248
- const roleStrategyParams = roleStrategy.allow(resource, this.resolveActionAlias(action));
249
-
250
343
  if (roleStrategyParams) {
251
344
  const result = {
252
345
  role,
253
346
  resource,
254
- action
347
+ action,
348
+ params: {}
255
349
  };
256
350
 
257
351
  if (_lodash().default.isPlainObject(roleStrategyParams)) {
258
352
  result['params'] = roleStrategyParams;
259
353
  }
260
354
 
261
- return result;
355
+ return mergeParams(result);
262
356
  }
263
357
 
264
358
  return null;
@@ -273,10 +367,16 @@ class ACL extends _events().default {
273
367
  }
274
368
 
275
369
  use(fn, options) {
276
- this.middlewares.add(fn, options);
370
+ this.middlewares.add(fn, _objectSpread({
371
+ group: 'prep'
372
+ }, options));
277
373
  }
278
374
 
279
375
  allow(resourceName, actionNames, condition) {
376
+ return this.skip(resourceName, actionNames, condition);
377
+ }
378
+
379
+ skip(resourceName, actionNames, condition) {
280
380
  if (!Array.isArray(actionNames)) {
281
381
  actionNames = [actionNames];
282
382
  }
@@ -306,28 +406,12 @@ class ACL extends _events().default {
306
406
 
307
407
  middleware() {
308
408
  const acl = this;
309
-
310
- const filterParams = (ctx, resourceName, params) => {
311
- var _params$filter;
312
-
313
- if (params === null || params === void 0 ? void 0 : (_params$filter = params.filter) === null || _params$filter === void 0 ? void 0 : _params$filter.createdById) {
314
- const collection = ctx.db.getCollection(resourceName);
315
-
316
- if (collection && !collection.getField('createdById')) {
317
- return _lodash().default.omit(params, 'filter.createdById');
318
- }
319
- }
320
-
321
- return params;
322
- };
323
-
324
409
  return /*#__PURE__*/function () {
325
410
  var _ACLMiddleware = _asyncToGenerator(function* (ctx, next) {
326
411
  const roleName = ctx.state.currentRole || 'anonymous';
327
- const _ctx$action = ctx.action,
328
- resourceName = _ctx$action.resourceName,
329
- actionName = _ctx$action.actionName;
330
- const resourcerAction = ctx.action;
412
+ const _ctx$action2 = ctx.action,
413
+ resourceName = _ctx$action2.resourceName,
414
+ actionName = _ctx$action2.actionName;
331
415
 
332
416
  ctx.can = options => {
333
417
  return acl.can(_objectSpread({
@@ -341,31 +425,10 @@ class ACL extends _events().default {
341
425
  action: actionName
342
426
  })
343
427
  };
344
- return (0, _koaCompose().default)(acl.middlewares.nodes)(ctx, /*#__PURE__*/_asyncToGenerator(function* () {
345
- const permission = ctx.permission;
346
-
347
- if (permission.skip) {
348
- return next();
349
- }
350
-
351
- if (!permission.can || typeof permission.can !== 'object') {
352
- ctx.throw(403, 'No permissions');
353
- return;
354
- }
355
-
356
- const params = permission.can.params;
357
-
358
- if (params) {
359
- const filteredParams = filterParams(ctx, resourceName, params);
360
- const parsedParams = acl.parseJsonTemplate(filteredParams, ctx);
361
- resourcerAction.mergeParams(parsedParams);
362
- }
363
-
364
- yield next();
365
- }));
428
+ return (0, _koaCompose().default)(acl.middlewares.nodes)(ctx, next);
366
429
  });
367
430
 
368
- function ACLMiddleware(_x, _x2) {
431
+ function ACLMiddleware(_x3, _x4) {
369
432
  return _ACLMiddleware.apply(this, arguments);
370
433
  }
371
434
 
@@ -373,6 +436,39 @@ class ACL extends _events().default {
373
436
  }();
374
437
  }
375
438
 
439
+ getActionParams(ctx) {
440
+ var _this = this;
441
+
442
+ return _asyncToGenerator(function* () {
443
+ const roleName = ctx.state.currentRole || 'anonymous';
444
+ const _ctx$action3 = ctx.action,
445
+ resourceName = _ctx$action3.resourceName,
446
+ actionName = _ctx$action3.actionName;
447
+
448
+ ctx.can = options => {
449
+ return _this.can(_objectSpread({
450
+ role: roleName
451
+ }, options));
452
+ };
453
+
454
+ ctx.permission = {
455
+ can: ctx.can({
456
+ resource: resourceName,
457
+ action: actionName
458
+ })
459
+ };
460
+ yield (0, _koaCompose().default)(_this.middlewares.nodes)(ctx, /*#__PURE__*/_asyncToGenerator(function* () {}));
461
+ })();
462
+ }
463
+
464
+ addFixedParams(resource, action, merger) {
465
+ this.fixedParamsManager.addParams(resource, action, merger);
466
+ }
467
+
468
+ registerSnippet(snippet) {
469
+ this.snippetManager.register(snippet);
470
+ }
471
+
376
472
  }
377
473
 
378
474
  exports.ACL = ACL;
@@ -8,5 +8,6 @@ export declare class AllowManager {
8
8
  allow(resourceName: string, actionName: string, condition?: string | ConditionFunc): void;
9
9
  getAllowedConditions(resourceName: string, actionName: string): Array<ConditionFunc | true>;
10
10
  registerAllowCondition(name: string, condition: ConditionFunc): void;
11
+ isAllowed(resourceName: string, actionName: string, ctx: any): Promise<boolean>;
11
12
  aclMiddleware(): (ctx: any, next: any) => Promise<void>;
12
13
  }
@@ -5,6 +5,12 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.AllowManager = void 0;
7
7
 
8
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
9
+
10
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
11
+
12
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
13
+
8
14
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
9
15
 
10
16
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
@@ -24,20 +30,26 @@ class AllowManager {
24
30
  this.registerAllowCondition('loggedIn', ctx => {
25
31
  return ctx.state.currentUser;
26
32
  });
33
+ this.registerAllowCondition('public', ctx => {
34
+ return true;
35
+ });
27
36
  this.registerAllowCondition('allowConfigure', /*#__PURE__*/function () {
28
37
  var _ref = _asyncToGenerator(function* (ctx) {
38
+ var _role$getStrategy;
39
+
29
40
  const roleName = ctx.state.currentRole;
30
41
 
31
42
  if (!roleName) {
32
43
  return false;
33
44
  }
34
45
 
35
- const roleInstance = yield ctx.db.getRepository('roles').findOne({
36
- filter: {
37
- name: roleName
38
- }
39
- });
40
- return roleInstance === null || roleInstance === void 0 ? void 0 : roleInstance.get('allowConfigure');
46
+ const role = acl.getRole(roleName);
47
+
48
+ if (!role) {
49
+ return false;
50
+ }
51
+
52
+ return (_role$getStrategy = role.getStrategy()) === null || _role$getStrategy === void 0 ? void 0 : _role$getStrategy.allowConfigure;
41
53
  });
42
54
 
43
55
  return function (_x) {
@@ -61,10 +73,13 @@ class AllowManager {
61
73
  const resource = this.skipActions.get(fetchActionStep);
62
74
 
63
75
  if (resource) {
64
- const condition = resource.get('*') || resource.get(actionName);
76
+ for (var _i2 = 0, _arr = ['*', actionName]; _i2 < _arr.length; _i2++) {
77
+ const fetchActionStep = _arr[_i2];
78
+ const condition = resource.get(fetchActionStep);
65
79
 
66
- if (condition) {
67
- results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);
80
+ if (condition) {
81
+ results.push(typeof condition === 'string' ? this.registeredCondition.get(condition) : condition);
82
+ }
68
83
  }
69
84
  }
70
85
  }
@@ -76,47 +91,57 @@ class AllowManager {
76
91
  this.registeredCondition.set(name, condition);
77
92
  }
78
93
 
79
- aclMiddleware() {
80
- return /*#__PURE__*/function () {
81
- var _ref2 = _asyncToGenerator(function* (ctx, next) {
82
- const _ctx$action = ctx.action,
83
- resourceName = _ctx$action.resourceName,
84
- actionName = _ctx$action.actionName;
85
- const skippedConditions = ctx.app.acl.allowManager.getAllowedConditions(resourceName, actionName);
86
- let skip = false;
94
+ isAllowed(resourceName, actionName, ctx) {
95
+ var _this = this;
87
96
 
88
- var _iterator = _createForOfIteratorHelper(skippedConditions),
89
- _step;
97
+ return _asyncToGenerator(function* () {
98
+ const skippedConditions = _this.getAllowedConditions(resourceName, actionName);
90
99
 
91
- try {
92
- for (_iterator.s(); !(_step = _iterator.n()).done;) {
93
- const skippedCondition = _step.value;
100
+ var _iterator = _createForOfIteratorHelper(skippedConditions),
101
+ _step;
94
102
 
95
- if (skippedCondition) {
96
- let skipResult = false;
103
+ try {
104
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
105
+ const skippedCondition = _step.value;
97
106
 
98
- if (typeof skippedCondition === 'function') {
99
- skipResult = yield skippedCondition(ctx);
100
- } else if (skippedCondition) {
101
- skipResult = true;
102
- }
107
+ if (skippedCondition) {
108
+ let skipResult = false;
109
+
110
+ if (typeof skippedCondition === 'function') {
111
+ skipResult = yield skippedCondition(ctx);
112
+ } else if (skippedCondition) {
113
+ skipResult = true;
114
+ }
103
115
 
104
- if (skipResult) {
105
- skip = true;
106
- break;
107
- }
116
+ if (skipResult) {
117
+ return true;
108
118
  }
109
119
  }
110
- } catch (err) {
111
- _iterator.e(err);
112
- } finally {
113
- _iterator.f();
114
120
  }
121
+ } catch (err) {
122
+ _iterator.e(err);
123
+ } finally {
124
+ _iterator.f();
125
+ }
126
+
127
+ return false;
128
+ })();
129
+ }
130
+
131
+ aclMiddleware() {
132
+ var _this2 = this;
133
+
134
+ return /*#__PURE__*/function () {
135
+ var _ref2 = _asyncToGenerator(function* (ctx, next) {
136
+ const _ctx$action = ctx.action,
137
+ resourceName = _ctx$action.resourceName,
138
+ actionName = _ctx$action.actionName;
139
+ let skip = yield _this2.acl.allowManager.isAllowed(resourceName, actionName, ctx);
115
140
 
116
141
  if (skip) {
117
- ctx.permission = {
142
+ ctx.permission = _objectSpread(_objectSpread({}, ctx.permission || {}), {}, {
118
143
  skip: true
119
- };
144
+ });
120
145
  }
121
146
 
122
147
  yield next();
@@ -0,0 +1,10 @@
1
+ export declare type Merger = () => object;
2
+ export declare type ActionPath = string;
3
+ export default class FixedParamsManager {
4
+ merger: Map<string, Merger[]>;
5
+ addParams(resource: string, action: string, merger: Merger): void;
6
+ getParamsMerger(resource: string, action: string): Merger[];
7
+ protected getActionPath(resource: string, action: string): string;
8
+ getParams(resource: string, action: string, extraParams?: any): {};
9
+ static mergeParams(a: any, b: any): void;
10
+ }
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ function _utils() {
9
+ const data = require("@nocobase/utils");
10
+
11
+ _utils = function _utils() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
19
+
20
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
21
+
22
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
23
+
24
+ const SPLIT = ':';
25
+
26
+ class FixedParamsManager {
27
+ constructor() {
28
+ this.merger = new Map();
29
+ }
30
+
31
+ addParams(resource, action, merger) {
32
+ const path = this.getActionPath(resource, action);
33
+ this.merger.set(path, [...this.getParamsMerger(resource, action), merger]);
34
+ }
35
+
36
+ getParamsMerger(resource, action) {
37
+ const path = this.getActionPath(resource, action);
38
+ return this.merger.get(path) || [];
39
+ }
40
+
41
+ getActionPath(resource, action) {
42
+ return `${resource}${SPLIT}${action}`;
43
+ }
44
+
45
+ getParams(resource, action, extraParams = {}) {
46
+ const results = {};
47
+
48
+ var _iterator = _createForOfIteratorHelper(this.getParamsMerger(resource, action)),
49
+ _step;
50
+
51
+ try {
52
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
53
+ const merger = _step.value;
54
+ FixedParamsManager.mergeParams(results, merger());
55
+ }
56
+ } catch (err) {
57
+ _iterator.e(err);
58
+ } finally {
59
+ _iterator.f();
60
+ }
61
+
62
+ if (extraParams) {
63
+ FixedParamsManager.mergeParams(results, extraParams);
64
+ }
65
+
66
+ return results;
67
+ }
68
+
69
+ static mergeParams(a, b) {
70
+ (0, _utils().assign)(a, b, {
71
+ filter: 'andMerge',
72
+ fields: 'intersect',
73
+ appends: 'union',
74
+ except: 'union',
75
+ whitelist: 'intersect',
76
+ blacklist: 'intersect',
77
+ sort: 'overwrite'
78
+ });
79
+ }
80
+
81
+ }
82
+
83
+ exports.default = FixedParamsManager;
package/lib/index.d.ts CHANGED
@@ -3,4 +3,5 @@ export * from './acl-available-action';
3
3
  export * from './acl-available-strategy';
4
4
  export * from './acl-resource';
5
5
  export * from './acl-role';
6
+ export * from './no-permission-error';
6
7
  export * from './skip-middleware';
package/lib/index.js CHANGED
@@ -69,6 +69,19 @@ Object.keys(_aclRole).forEach(function (key) {
69
69
  });
70
70
  });
71
71
 
72
+ var _noPermissionError = require("./no-permission-error");
73
+
74
+ Object.keys(_noPermissionError).forEach(function (key) {
75
+ if (key === "default" || key === "__esModule") return;
76
+ if (key in exports && exports[key] === _noPermissionError[key]) return;
77
+ Object.defineProperty(exports, key, {
78
+ enumerable: true,
79
+ get: function get() {
80
+ return _noPermissionError[key];
81
+ }
82
+ });
83
+ });
84
+
72
85
  var _skipMiddleware = require("./skip-middleware");
73
86
 
74
87
  Object.keys(_skipMiddleware).forEach(function (key) {
@@ -0,0 +1,4 @@
1
+ declare class NoPermissionError extends Error {
2
+ constructor(...args: any[]);
3
+ }
4
+ export { NoPermissionError };
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.NoPermissionError = void 0;
7
+
8
+ class NoPermissionError extends Error {
9
+ constructor(...args) {
10
+ super(...args);
11
+ }
12
+
13
+ }
14
+
15
+ exports.NoPermissionError = NoPermissionError;
@@ -0,0 +1,19 @@
1
+ export declare type SnippetOptions = {
2
+ name: string;
3
+ actions: Array<string>;
4
+ };
5
+ declare class Snippet {
6
+ name: string;
7
+ actions: Array<string>;
8
+ constructor(name: string, actions: Array<string>);
9
+ }
10
+ export declare type SnippetGroup = {
11
+ name: string;
12
+ snippets: SnippetOptions[];
13
+ };
14
+ declare class SnippetManager {
15
+ snippets: Map<string, Snippet>;
16
+ register(snippet: SnippetOptions): void;
17
+ allow(actionPath: string, snippetName: string): boolean;
18
+ }
19
+ export default SnippetManager;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ function _minimatch() {
9
+ const data = _interopRequireDefault(require("minimatch"));
10
+
11
+ _minimatch = function _minimatch() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
+
20
+ class Snippet {
21
+ constructor(name, actions) {
22
+ this.name = void 0;
23
+ this.actions = void 0;
24
+ this.name = name;
25
+ this.actions = actions;
26
+ }
27
+
28
+ }
29
+
30
+ class SnippetManager {
31
+ constructor() {
32
+ this.snippets = new Map();
33
+ }
34
+
35
+ register(snippet) {
36
+ this.snippets.set(snippet.name, snippet);
37
+ }
38
+
39
+ allow(actionPath, snippetName) {
40
+ const negated = snippetName.startsWith('!');
41
+ snippetName = negated ? snippetName.slice(1) : snippetName;
42
+ const snippet = this.snippets.get(snippetName);
43
+
44
+ if (!snippet) {
45
+ throw new Error(`Snippet ${snippetName} not found`);
46
+ }
47
+
48
+ const matched = snippet.actions.some(action => (0, _minimatch().default)(actionPath, action));
49
+
50
+ if (matched) {
51
+ return negated ? false : true;
52
+ }
53
+
54
+ return null;
55
+ }
56
+
57
+ }
58
+
59
+ var _default = SnippetManager;
60
+ exports.default = _default;
package/package.json CHANGED
@@ -1,24 +1,20 @@
1
1
  {
2
2
  "name": "@nocobase/acl",
3
- "version": "0.8.1-alpha.3",
3
+ "version": "0.9.0-alpha.1",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
- "licenses": [
7
- {
8
- "type": "Apache-2.0",
9
- "url": "http://www.apache.org/licenses/LICENSE-2.0"
10
- }
11
- ],
12
6
  "main": "./lib/index.js",
13
7
  "types": "./lib/index.d.ts",
14
8
  "dependencies": {
15
- "@nocobase/resourcer": "0.8.1-alpha.3",
16
- "json-templates": "^4.2.0"
9
+ "@nocobase/resourcer": "0.9.0-alpha.1",
10
+ "@nocobase/utils": "0.9.0-alpha.1",
11
+ "json-templates": "^4.2.0",
12
+ "minimatch": "^5.1.1"
17
13
  },
18
14
  "repository": {
19
15
  "type": "git",
20
16
  "url": "git+https://github.com/nocobase/nocobase.git",
21
17
  "directory": "packages/acl"
22
18
  },
23
- "gitHead": "e03df3df5962b99d9fbf5b6e33fbe2b23f14f3d3"
19
+ "gitHead": "d84c2a47c42c2ffec4fbf317d53268952fbd58d7"
24
20
  }