@via-profit/ability 2.0.0-rc.3 → 2.0.0-rc.5

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.
@@ -22,6 +22,18 @@ class AbilityCode {
22
22
  isNotEqual(compareWith) {
23
23
  return !this.isEqual(compareWith);
24
24
  }
25
+ static fromLiteral(literal) {
26
+ let Code = null;
27
+ Object.keys(this).forEach(member => {
28
+ if (member === literal) {
29
+ Code = this[member];
30
+ }
31
+ });
32
+ if (!Code) {
33
+ throw new Error(`Mismatch error. The literal ${literal} can not be find as a member of this class`);
34
+ }
35
+ return new this(Code.code);
36
+ }
25
37
  }
26
38
  exports.AbilityCode = AbilityCode;
27
39
  exports["default"] = AbilityCode;
@@ -43,8 +55,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
43
55
  exports.AbilityCompare = void 0;
44
56
  const AbilityCode_1 = __importDefault(__webpack_require__(/*! ./AbilityCode */ "./src/AbilityCode.ts"));
45
57
  class AbilityCompare extends AbilityCode_1.default {
46
- static OR = new AbilityCompare(0);
47
- static AND = new AbilityCompare(1);
58
+ static and = new AbilityCompare(0);
59
+ static or = new AbilityCompare(1);
48
60
  }
49
61
  exports.AbilityCompare = AbilityCompare;
50
62
  exports["default"] = AbilityCompare;
@@ -126,146 +138,14 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
126
138
  exports.AbilityMatch = void 0;
127
139
  const AbilityCode_1 = __importDefault(__webpack_require__(/*! ./AbilityCode */ "./src/AbilityCode.ts"));
128
140
  class AbilityMatch extends AbilityCode_1.default {
129
- static PENDING = new AbilityMatch(2);
130
- static MATCH = new AbilityMatch(1);
131
- static MISMATCH = new AbilityMatch(0);
141
+ static pending = new AbilityMatch('pending');
142
+ static match = new AbilityMatch('match');
143
+ static mismatch = new AbilityMatch('mismatch');
132
144
  }
133
145
  exports.AbilityMatch = AbilityMatch;
134
146
  exports["default"] = AbilityMatch;
135
147
 
136
148
 
137
- /***/ }),
138
-
139
- /***/ "./src/AbilityParser.ts":
140
- /*!******************************!*\
141
- !*** ./src/AbilityParser.ts ***!
142
- \******************************/
143
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
144
-
145
-
146
- var __importDefault = (this && this.__importDefault) || function (mod) {
147
- return (mod && mod.__esModule) ? mod : { "default": mod };
148
- };
149
- Object.defineProperty(exports, "__esModule", ({ value: true }));
150
- exports.AbilityParser = void 0;
151
- const AbilityError_1 = __webpack_require__(/*! ./AbilityError */ "./src/AbilityError.ts");
152
- const AbilityCondition_1 = __importDefault(__webpack_require__(/*! ./AbilityCondition */ "./src/AbilityCondition.ts"));
153
- class AbilityParser {
154
- /**
155
- * Validates the configuration object based on the provided field validation configurations.
156
- * @param config - The configuration object to validate.
157
- * @param fields - An array of field validation configurations.
158
- * @throws {AbilityParserError} If a required field is missing or if a field has an incorrect type.
159
- */
160
- static validateConfig(config, fields) {
161
- fields.forEach(([field, type, isRequired]) => {
162
- const value = config[field];
163
- if (isRequired) {
164
- if (typeof value === 'undefined') {
165
- throw new AbilityError_1.AbilityParserError(`Missing required field [${field}]`);
166
- }
167
- }
168
- switch (type) {
169
- case 'array':
170
- if (typeof value !== 'object' || !Array.isArray(value)) {
171
- throw new AbilityError_1.AbilityParserError(`Field [${field}] must be an type of [${type}], bit got [${typeof value}]`);
172
- }
173
- break;
174
- default:
175
- if (typeof value !== type && typeof value !== 'undefined') {
176
- throw new AbilityError_1.AbilityParserError(`Field [${field}] must be a type of [${type}], bit got [${typeof value}]`);
177
- }
178
- break;
179
- }
180
- });
181
- }
182
- /**
183
- * Prepares and validates the configuration object or JSON string.
184
- * @param configOrJson - The configuration object or JSON string to validate.
185
- * @param fields - An array of field validation configurations.
186
- * @returns The validated configuration object.
187
- */
188
- static prepareAndValidateConfig(configOrJson, fields) {
189
- const config = typeof configOrJson === 'string'
190
- ? (JSON.parse(configOrJson))
191
- : configOrJson;
192
- AbilityParser.validateConfig(config, fields);
193
- return config;
194
- }
195
- /*
196
- *
197
- * readonly ['order.update']: {
198
- * readonly user: {
199
- * readonly roles: readonly string[];
200
- * readonly department: string;
201
- * };
202
- * readonly order: {
203
- * readonly estimatedArrivalAt: number;
204
- * readonly status: string;
205
- * }
206
- * }
207
- *
208
- * */
209
- /**
210
- * Sets a value in a nested object structure based on a dot/bracket notation path.
211
- * @param object - The target object to modify.
212
- * @param path - The path to the property in dot/bracket notation.
213
- * @param value - The value to set at the specified path.
214
- */
215
- static setValueDotValue(object, path, value) {
216
- const way = path.replace(/\[/g, '.').replace(/\]/g, '').split('.');
217
- const last = way.pop();
218
- if (!last) {
219
- throw new AbilityError_1.AbilityParserError(`Invalid path provided on a [${path}]`);
220
- }
221
- way.reduce((o, k, i, kk) => {
222
- if (!o[k]) {
223
- o[k] = isFinite(Number(kk[i + 1])) ? [] : {};
224
- }
225
- return o[k];
226
- }, object)[last] = value;
227
- }
228
- /**
229
- * Generates TypeScript type definitions based on the provided policies.
230
- * @param policies - An array of AbilityPolicy instances.
231
- * @param outPath - The output path for the generated type definitions.
232
- * @returns A record containing the generated type definitions.
233
- */
234
- static generateTypeDefs(policies, outPath) {
235
- const record = {};
236
- policies.forEach(policy => {
237
- policy.ruleSet.forEach(ruleSet => {
238
- ruleSet.rules.forEach(rule => {
239
- const [leftFieldPath, condition, rightFiledPath] = rule.matches;
240
- let value = 'any';
241
- switch (true) {
242
- case condition.isEqual(AbilityCondition_1.default.NOT_EQUAL):
243
- case condition.isEqual(AbilityCondition_1.default.EQUAL):
244
- value = typeof rightFiledPath;
245
- break;
246
- case condition.isEqual(AbilityCondition_1.default.IN):
247
- case condition.isEqual(AbilityCondition_1.default.NOT_IN):
248
- value = `${typeof rightFiledPath}[]`;
249
- break;
250
- case condition.isEqual(AbilityCondition_1.default.MORE_OR_EQUAL):
251
- case condition.isEqual(AbilityCondition_1.default.MORE_THAN):
252
- case condition.isEqual(AbilityCondition_1.default.LESS_OR_EQUAL):
253
- case condition.isEqual(AbilityCondition_1.default.LESS_THAN):
254
- value = 'number';
255
- break;
256
- }
257
- AbilityParser.setValueDotValue(record, leftFieldPath, value);
258
- });
259
- });
260
- });
261
- console.log(JSON.stringify(record));
262
- return record;
263
- }
264
- }
265
- exports.AbilityParser = AbilityParser;
266
- exports["default"] = AbilityParser;
267
-
268
-
269
149
  /***/ }),
270
150
 
271
151
  /***/ "./src/AbilityPolicy.ts":
@@ -280,14 +160,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
280
160
  };
281
161
  Object.defineProperty(exports, "__esModule", ({ value: true }));
282
162
  exports.AbilityPolicy = void 0;
283
- const AbilityRule_1 = __importDefault(__webpack_require__(/*! ./AbilityRule */ "./src/AbilityRule.ts"));
284
163
  const AbilityRuleSet_1 = __importDefault(__webpack_require__(/*! ./AbilityRuleSet */ "./src/AbilityRuleSet.ts"));
285
164
  const AbilityMatch_1 = __importDefault(__webpack_require__(/*! ./AbilityMatch */ "./src/AbilityMatch.ts"));
286
165
  const AbilityCompare_1 = __importDefault(__webpack_require__(/*! ./AbilityCompare */ "./src/AbilityCompare.ts"));
287
166
  const AbilityPolicyEffect_1 = __importDefault(__webpack_require__(/*! ./AbilityPolicyEffect */ "./src/AbilityPolicyEffect.ts"));
288
- const AbilityParser_1 = __importDefault(__webpack_require__(/*! ./AbilityParser */ "./src/AbilityParser.ts"));
289
167
  class AbilityPolicy {
290
- matchState = AbilityMatch_1.default.PENDING;
168
+ matchState = AbilityMatch_1.default.pending;
291
169
  /**
292
170
  * List of rules
293
171
  */
@@ -302,7 +180,7 @@ class AbilityPolicy {
302
180
  * rules will be returns «permit» status and for the «or» - if\
303
181
  * one of the rules returns as «permit»
304
182
  */
305
- compareMethod = AbilityCompare_1.default.AND;
183
+ compareMethod = AbilityCompare_1.default.and;
306
184
  /**
307
185
  * Policy name
308
186
  */
@@ -317,8 +195,8 @@ class AbilityPolicy {
317
195
  action;
318
196
  constructor(params) {
319
197
  const { name, id, action, effect } = params;
320
- this.name = name || Symbol('name');
321
- this.id = id || Symbol('id');
198
+ this.name = name;
199
+ this.id = id;
322
200
  this.action = action;
323
201
  this.effect = effect;
324
202
  }
@@ -330,40 +208,26 @@ class AbilityPolicy {
330
208
  this.ruleSet.push(ruleSet);
331
209
  return this;
332
210
  }
333
- /**
334
- * Add rule to the policy
335
- * @param rule - The rule to add
336
- */
337
- addRule(rule) {
338
- this.addRuleSet(new AbilityRuleSet_1.default({
339
- name: rule.name,
340
- }).addRule(rule, AbilityCompare_1.default.AND));
341
- return this;
342
- }
343
211
  /**
344
212
  * Check if the policy is matched
345
- * @param resource - The resource to check
213
+ * @param resources - The resource to check
346
214
  */
347
- check(resource) {
348
- this.matchState = AbilityMatch_1.default.MISMATCH;
349
- /**
350
- * If policy contain a rules
351
- */
352
- if (this.ruleSet.length) {
353
- const ruleCheckStates = [];
354
- this.ruleSet.forEach(rule => {
355
- const ruleCheckState = rule.check(resource);
356
- ruleCheckStates.push(ruleCheckState);
357
- });
358
- if (AbilityCompare_1.default.AND.isEqual(this.compareMethod)) {
359
- if (ruleCheckStates.every(ruleState => AbilityMatch_1.default.MATCH.isEqual(ruleState))) {
360
- this.matchState = AbilityMatch_1.default.MATCH;
361
- }
215
+ check(resources) {
216
+ this.matchState = AbilityMatch_1.default.mismatch;
217
+ if (!this.ruleSet.length) {
218
+ return this.matchState;
219
+ }
220
+ const rulesetCheckStates = this.ruleSet.reduce((collect, ruleSet) => {
221
+ return collect.concat(ruleSet.check(resources));
222
+ }, []);
223
+ if (AbilityCompare_1.default.and.isEqual(this.compareMethod)) {
224
+ if (rulesetCheckStates.every(ruleState => AbilityMatch_1.default.match.isEqual(ruleState))) {
225
+ this.matchState = AbilityMatch_1.default.match;
362
226
  }
363
- if (AbilityCompare_1.default.OR.isEqual(this.compareMethod)) {
364
- if (ruleCheckStates.some(ruleState => AbilityMatch_1.default.MATCH.isEqual(ruleState))) {
365
- this.matchState = AbilityMatch_1.default.MATCH;
366
- }
227
+ }
228
+ if (AbilityCompare_1.default.or.isEqual(this.compareMethod)) {
229
+ if (rulesetCheckStates.some(ruleState => AbilityMatch_1.default.match.isEqual(ruleState))) {
230
+ this.matchState = AbilityMatch_1.default.match;
367
231
  }
368
232
  }
369
233
  return this.matchState;
@@ -371,15 +235,7 @@ class AbilityPolicy {
371
235
  /**
372
236
  * Parse the config JSON format to Policy class instance
373
237
  */
374
- static parse(configOrJson) {
375
- const config = AbilityParser_1.default.prepareAndValidateConfig(configOrJson, [
376
- ['id', 'string', false],
377
- ['name', 'string', true],
378
- ['action', 'string', true],
379
- ['effect', 'number', true],
380
- ['compareMethod', 'number', true],
381
- ['ruleSet', 'array', true],
382
- ]);
238
+ static parse(config) {
383
239
  const { id, name, ruleSet, compareMethod, action, effect } = config;
384
240
  // Create the empty policy
385
241
  const policy = new AbilityPolicy({
@@ -388,16 +244,9 @@ class AbilityPolicy {
388
244
  action,
389
245
  effect: new AbilityPolicyEffect_1.default(effect),
390
246
  });
391
- policy.compareMethod = new AbilityCompare_1.default(compareMethod);
392
- ruleSet.forEach(ruleOrRuleSet => {
393
- // is ruleset
394
- if ('rules' in ruleOrRuleSet) {
395
- policy.addRuleSet(AbilityRuleSet_1.default.parse(ruleOrRuleSet));
396
- }
397
- // is simple rule
398
- if (!('rules' in ruleOrRuleSet)) {
399
- policy.addRule(AbilityRule_1.default.parse(ruleOrRuleSet));
400
- }
247
+ policy.compareMethod = AbilityCompare_1.default.fromLiteral(compareMethod);
248
+ ruleSet.forEach(ruleSetConfig => {
249
+ policy.addRuleSet(AbilityRuleSet_1.default.parse(ruleSetConfig));
401
250
  });
402
251
  return policy;
403
252
  }
@@ -405,7 +254,7 @@ class AbilityPolicy {
405
254
  return {
406
255
  id: this.id.toString(),
407
256
  name: this.name.toString(),
408
- compareMethod: this.compareMethod.code,
257
+ compareMethod: this.compareMethod.code.toString(),
409
258
  ruleSet: this.ruleSet.map(rule => rule.export()),
410
259
  action: this.action,
411
260
  effect: this.effect.code,
@@ -432,8 +281,8 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
432
281
  exports.AbilityPolicyEffect = void 0;
433
282
  const AbilityCode_1 = __importDefault(__webpack_require__(/*! ./AbilityCode */ "./src/AbilityCode.ts"));
434
283
  class AbilityPolicyEffect extends AbilityCode_1.default {
435
- static DENY = new AbilityPolicyEffect(0);
436
- static PERMIT = new AbilityPolicyEffect(1);
284
+ static deny = new AbilityPolicyEffect('deny');
285
+ static permit = new AbilityPolicyEffect('permit');
437
286
  }
438
287
  exports.AbilityPolicyEffect = AbilityPolicyEffect;
439
288
  exports["default"] = AbilityPolicyEffect;
@@ -458,8 +307,10 @@ const AbilityMatch_1 = __importDefault(__webpack_require__(/*! ./AbilityMatch */
458
307
  const AbilityError_1 = __webpack_require__(/*! ./AbilityError */ "./src/AbilityError.ts");
459
308
  class AbilityResolver {
460
309
  policies;
461
- constructor(policies) {
462
- this.policies = policies;
310
+ constructor(policyOrListOfPolicies) {
311
+ this.policies = Array.isArray(policyOrListOfPolicies)
312
+ ? policyOrListOfPolicies
313
+ : [policyOrListOfPolicies];
463
314
  }
464
315
  /**
465
316
  * Resolve policy for the resource and action
@@ -479,7 +330,7 @@ class AbilityResolver {
479
330
  const resolver = this.resolve(action, resource);
480
331
  if (resolver) {
481
332
  if (resolver.isDeny()) {
482
- throw new AbilityError_1.PermissionError(['Permission denied', resolver.getPolicy()?.name?.toString()].join('. '));
333
+ throw new AbilityError_1.PermissionError(resolver.getPolicy()?.name?.toString() || 'Unknown permission error');
483
334
  }
484
335
  }
485
336
  }
@@ -490,7 +341,7 @@ class AbilityResolver {
490
341
  */
491
342
  getEffect() {
492
343
  const effects = this.policies.reduce((collect, policy, _index) => {
493
- if (policy.matchState.isEqual(AbilityMatch_1.default.MATCH)) {
344
+ if (policy.matchState.isEqual(AbilityMatch_1.default.match)) {
494
345
  return collect.concat(policy.effect);
495
346
  }
496
347
  return collect;
@@ -502,15 +353,15 @@ class AbilityResolver {
502
353
  }
503
354
  isPermit() {
504
355
  const effect = this.getEffect();
505
- return effect !== null && effect.isEqual(AbilityPolicyEffect_1.default.PERMIT);
356
+ return effect !== null && effect.isEqual(AbilityPolicyEffect_1.default.permit);
506
357
  }
507
358
  isDeny() {
508
359
  const effect = this.getEffect();
509
- return effect !== null && effect.isEqual(AbilityPolicyEffect_1.default.DENY);
360
+ return effect !== null && effect.isEqual(AbilityPolicyEffect_1.default.deny);
510
361
  }
511
362
  getPolicy() {
512
363
  const lastPolicy = this.policies.length ? this.policies[this.policies.length - 1] : null;
513
- return lastPolicy && lastPolicy.matchState.isEqual(AbilityMatch_1.default.MATCH) ? lastPolicy : null;
364
+ return lastPolicy && lastPolicy.matchState.isEqual(AbilityMatch_1.default.match) ? lastPolicy : null;
514
365
  }
515
366
  /**
516
367
  * Check if the action is contained in another action
@@ -550,43 +401,53 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
550
401
  exports.AbilityRule = void 0;
551
402
  const AbilityMatch_1 = __importDefault(__webpack_require__(/*! ./AbilityMatch */ "./src/AbilityMatch.ts"));
552
403
  const AbilityCondition_1 = __importDefault(__webpack_require__(/*! ./AbilityCondition */ "./src/AbilityCondition.ts"));
553
- const AbilityParser_1 = __importDefault(__webpack_require__(/*! ./AbilityParser */ "./src/AbilityParser.ts"));
554
404
  class AbilityRule {
555
- matches;
405
+ /**
406
+ * Subject key path like a 'user.name'
407
+ */
408
+ subject;
409
+ /**
410
+ * Resource key path like a 'user.name' or value
411
+ */
412
+ resource;
413
+ condition;
556
414
  name;
557
- state = AbilityMatch_1.default.PENDING;
415
+ id;
416
+ state = AbilityMatch_1.default.pending;
558
417
  constructor(params) {
559
- const { name, matches } = params;
560
- this.name = name || Symbol('name');
561
- this.matches = matches;
418
+ const { id, name, subject, resource, condition } = params;
419
+ this.id = id;
420
+ this.name = name;
421
+ this.subject = subject;
422
+ this.resource = resource;
423
+ this.condition = new AbilityCondition_1.default(condition);
562
424
  }
563
425
  /**
564
426
  * Check if the rule is matched
565
427
  * @param resource - The resource to check
566
428
  */
567
429
  check(resource) {
568
- const [_subjectPathName, condition, _staticValueOrPathName] = this.matches;
569
430
  let is = false;
570
431
  const [valueS, valueO] = this.extractValues(resource);
571
- if (AbilityCondition_1.default.LESS_THAN.isEqual(condition)) {
432
+ if (AbilityCondition_1.default.LESS_THAN.isEqual(this.condition)) {
572
433
  is = Number(valueS) < Number(valueO);
573
434
  }
574
- if (AbilityCondition_1.default.LESS_OR_EQUAL.isEqual(condition)) {
435
+ if (AbilityCondition_1.default.LESS_OR_EQUAL.isEqual(this.condition)) {
575
436
  is = Number(valueS) <= Number(valueO);
576
437
  }
577
- if (AbilityCondition_1.default.MORE_THAN.isEqual(condition)) {
438
+ if (AbilityCondition_1.default.MORE_THAN.isEqual(this.condition)) {
578
439
  is = Number(valueS) > Number(valueO);
579
440
  }
580
- if (AbilityCondition_1.default.MORE_OR_EQUAL.isEqual(condition)) {
441
+ if (AbilityCondition_1.default.MORE_OR_EQUAL.isEqual(this.condition)) {
581
442
  is = Number(valueS) >= Number(valueO);
582
443
  }
583
- if (AbilityCondition_1.default.EQUAL.isEqual(condition)) {
444
+ if (AbilityCondition_1.default.EQUAL.isEqual(this.condition)) {
584
445
  is = valueS === valueO;
585
446
  }
586
- if (AbilityCondition_1.default.NOT_EQUAL.isEqual(condition)) {
447
+ if (AbilityCondition_1.default.NOT_EQUAL.isEqual(this.condition)) {
587
448
  is = valueS !== valueO;
588
449
  }
589
- if (AbilityCondition_1.default.IN.isEqual(condition)) {
450
+ if (AbilityCondition_1.default.IN.isEqual(this.condition)) {
590
451
  // [<some>] and [<some>]
591
452
  if (Array.isArray(valueS) && Array.isArray(valueO)) {
592
453
  is = valueS.some(v => valueO.find(v1 => v1 === v));
@@ -600,7 +461,7 @@ class AbilityRule {
600
461
  is = valueS.includes(valueO);
601
462
  }
602
463
  }
603
- if (AbilityCondition_1.default.NOT_IN.isEqual(condition)) {
464
+ if (AbilityCondition_1.default.NOT_IN.isEqual(this.condition)) {
604
465
  // [<some>] and [<some>]
605
466
  if (Array.isArray(valueS) && Array.isArray(valueO)) {
606
467
  is = !valueS.some(v => valueO.find(v1 => v1 === v));
@@ -614,31 +475,33 @@ class AbilityRule {
614
475
  is = !valueS.includes(valueO);
615
476
  }
616
477
  }
617
- this.state = is ? AbilityMatch_1.default.MATCH : AbilityMatch_1.default.MISMATCH;
478
+ this.state = is ? AbilityMatch_1.default.match : AbilityMatch_1.default.mismatch;
618
479
  return this.state;
619
480
  }
620
481
  /**
621
- * Extract values from the resource
622
- * @param resource - The resource to extract values from
482
+ * Extract values from the resourceData
483
+ * @param resourceData - The resourceData to extract values from
623
484
  */
624
- extractValues(resource) {
625
- const [subjectPathName, _condition, staticValueOrPathName] = this.matches;
485
+ extractValues(resourceData) {
626
486
  let leftSideValue;
627
487
  let rightSideValue;
628
- if (resource === null || typeof resource === 'undefined') {
488
+ if (resourceData === null || typeof resourceData === 'undefined') {
629
489
  return [NaN, NaN];
630
490
  }
631
491
  const isPath = (str) => {
632
492
  return typeof str === 'string' && str.match(/\./g) !== null;
633
493
  };
634
- if (isPath(subjectPathName)) {
635
- leftSideValue = this.getDotNotationValue(resource, subjectPathName);
494
+ if (isPath(this.subject)) {
495
+ leftSideValue = this.getDotNotationValue(resourceData, this.subject);
636
496
  }
637
- if (isPath(staticValueOrPathName)) {
638
- rightSideValue = this.getDotNotationValue(resource, staticValueOrPathName);
497
+ else {
498
+ leftSideValue = this.subject;
499
+ }
500
+ if (isPath(this.resource)) {
501
+ rightSideValue = this.getDotNotationValue(resourceData, this.resource);
639
502
  }
640
503
  else {
641
- rightSideValue = staticValueOrPathName;
504
+ rightSideValue = this.resource;
642
505
  }
643
506
  return [leftSideValue, rightSideValue];
644
507
  }
@@ -670,27 +533,26 @@ class AbilityRule {
670
533
  }
671
534
  return resource;
672
535
  }
673
- static parse(configOrJson) {
674
- const config = AbilityParser_1.default.prepareAndValidateConfig(configOrJson, [
675
- ['id', 'string', false],
676
- ['name', 'string', true],
677
- ['matches', 'array', true],
678
- ]);
679
- const { name, matches } = config;
680
- const [leftField, condition, rightField] = matches;
536
+ static parse(config) {
537
+ const { id, name, subject, resource, condition } = config;
681
538
  return new AbilityRule({
539
+ id,
682
540
  name,
683
- matches: [leftField, new AbilityCondition_1.default(condition), rightField],
541
+ subject,
542
+ resource,
543
+ condition,
684
544
  });
685
545
  }
686
546
  /**
687
547
  * Export the rule to config object
688
548
  */
689
549
  export() {
690
- const [leftField, condition, rightField] = this.matches;
691
550
  return {
551
+ id: this.id,
692
552
  name: this.name,
693
- matches: [leftField, condition.code, rightField],
553
+ subject: this.subject,
554
+ resource: this.resource,
555
+ condition: this.condition.code,
694
556
  };
695
557
  }
696
558
  }
@@ -715,9 +577,8 @@ exports.AbilityRuleSet = void 0;
715
577
  const AbilityRule_1 = __importDefault(__webpack_require__(/*! ./AbilityRule */ "./src/AbilityRule.ts"));
716
578
  const AbilityCompare_1 = __importDefault(__webpack_require__(/*! ./AbilityCompare */ "./src/AbilityCompare.ts"));
717
579
  const AbilityMatch_1 = __importDefault(__webpack_require__(/*! ./AbilityMatch */ "./src/AbilityMatch.ts"));
718
- const AbilityParser_1 = __importDefault(__webpack_require__(/*! ./AbilityParser */ "./src/AbilityParser.ts"));
719
580
  class AbilityRuleSet {
720
- state = AbilityMatch_1.default.PENDING;
581
+ state = AbilityMatch_1.default.pending;
721
582
  /**
722
583
  * List of rules
723
584
  */
@@ -728,7 +589,7 @@ class AbilityRuleSet {
728
589
  * rules will be returns «permit» status and for the «or» - if\
729
590
  * one of the rules returns as «permit»
730
591
  */
731
- compareMethod = AbilityCompare_1.default.AND;
592
+ compareMethod = AbilityCompare_1.default.and;
732
593
  /**
733
594
  * Group name
734
595
  */
@@ -738,9 +599,11 @@ class AbilityRuleSet {
738
599
  */
739
600
  id;
740
601
  constructor(params) {
741
- const { name, id } = params || {};
742
- this.name = name || Symbol('name');
743
- this.id = id || Symbol('id');
602
+ const { name, id, compareMethod } = params;
603
+ this.name = name;
604
+ this.id = id;
605
+ this.compareMethod = AbilityCompare_1.default.fromLiteral(compareMethod);
606
+ // this.compareMethod = new AbilityCompare(compareMethod);
744
607
  }
745
608
  addRule(rule, compareMethod) {
746
609
  this.rules.push(rule);
@@ -752,21 +615,21 @@ class AbilityRuleSet {
752
615
  return this;
753
616
  }
754
617
  check(resources) {
755
- this.state = AbilityMatch_1.default.MISMATCH;
618
+ this.state = AbilityMatch_1.default.mismatch;
756
619
  if (!this.rules.length) {
757
620
  return this.state;
758
621
  }
759
622
  const ruleCheckStates = this.rules.reduce((collect, rule) => {
760
623
  return collect.concat(rule.check(resources));
761
624
  }, []);
762
- if (AbilityCompare_1.default.AND.isEqual(this.compareMethod)) {
763
- if (ruleCheckStates.every(ruleState => AbilityMatch_1.default.MATCH.isEqual(ruleState))) {
764
- this.state = AbilityMatch_1.default.MATCH;
625
+ if (AbilityCompare_1.default.and.isEqual(this.compareMethod)) {
626
+ if (ruleCheckStates.every(ruleState => AbilityMatch_1.default.match.isEqual(ruleState))) {
627
+ this.state = AbilityMatch_1.default.match;
765
628
  }
766
629
  }
767
- if (AbilityCompare_1.default.OR.isEqual(this.compareMethod)) {
768
- if (ruleCheckStates.some(ruleState => AbilityMatch_1.default.MATCH.isEqual(ruleState))) {
769
- this.state = AbilityMatch_1.default.MATCH;
630
+ if (AbilityCompare_1.default.or.isEqual(this.compareMethod)) {
631
+ if (ruleCheckStates.some(ruleState => AbilityMatch_1.default.match.isEqual(ruleState))) {
632
+ this.state = AbilityMatch_1.default.match;
770
633
  }
771
634
  }
772
635
  return this.state;
@@ -774,19 +637,13 @@ class AbilityRuleSet {
774
637
  /**
775
638
  * Parse the config JSON format to Group class instance
776
639
  */
777
- static parse(configOrJson) {
778
- const config = AbilityParser_1.default.prepareAndValidateConfig(configOrJson, [
779
- ['id', 'string', false],
780
- ['name', 'string', true],
781
- ['compareMethod', 'number', true],
782
- ['rules', 'array', true],
783
- ]);
640
+ static parse(config) {
784
641
  const { id, name, rules, compareMethod } = config;
785
642
  const ruleSet = new AbilityRuleSet({
643
+ compareMethod,
786
644
  name,
787
645
  id,
788
646
  });
789
- ruleSet.compareMethod = new AbilityCompare_1.default(compareMethod);
790
647
  // Adding rules if exists
791
648
  if (rules && rules.length > 0) {
792
649
  const abilityRules = rules.map(ruleConfig => AbilityRule_1.default.parse(ruleConfig));
@@ -798,7 +655,7 @@ class AbilityRuleSet {
798
655
  return {
799
656
  id: this.id.toString(),
800
657
  name: this.name.toString(),
801
- compareMethod: this.compareMethod.code,
658
+ compareMethod: this.compareMethod.code.toString(),
802
659
  rules: this.rules.map(rule => rule.export()),
803
660
  };
804
661
  }
@@ -823,24 +680,80 @@ Object.defineProperty(exports, "__esModule", ({ value: true }));
823
680
  const node_http_1 = __importDefault(__webpack_require__(/*! node:http */ "node:http"));
824
681
  const AbilityPolicy_1 = __importDefault(__webpack_require__(/*! ./AbilityPolicy */ "./src/AbilityPolicy.ts"));
825
682
  const AbilityResolver_1 = __importDefault(__webpack_require__(/*! ~/AbilityResolver */ "./src/AbilityResolver.ts"));
826
- const policies_json_1 = __importDefault(__webpack_require__(/*! ./policies.json */ "./src/policies.json"));
827
683
  const server = node_http_1.default.createServer();
828
684
  server.on('request', (_req, res) => {
829
- new AbilityResolver_1.default(policies_json_1.default.map(p => {
830
- return AbilityPolicy_1.default.parse(p);
831
- })).enforce('account.read', {
685
+ const config = [
686
+ {
687
+ id: 'bb758c1b-1015-4894-ba25-d23156e063cf',
688
+ name: 'Status hui',
689
+ action: 'order.status',
690
+ effect: 'deny',
691
+ compareMethod: 'and',
692
+ ruleSet: [
693
+ {
694
+ id: '9cc009e5-0aa9-453a-a668-cb3f418ced92',
695
+ name: 'Не администратор',
696
+ compareMethod: 'and',
697
+ rules: [
698
+ {
699
+ id: '4093cd50-e54f-4062-8053-2d3b5966fad3',
700
+ name: 'Нет роли администраторв',
701
+ subject: 'account.roles',
702
+ resource: 'administrator',
703
+ condition: '<>',
704
+ },
705
+ ],
706
+ },
707
+ ]
708
+ },
709
+ {
710
+ id: 'bb758c1b-1015-4894-ba25-d23156e063cf',
711
+ name: 'Status hui 1',
712
+ action: 'order.status',
713
+ effect: 'permit',
714
+ compareMethod: 'and',
715
+ ruleSet: [
716
+ {
717
+ id: '2f8f9d71-860b-4fa6-b395-9331f1f0848e',
718
+ name: 'Rule set',
719
+ compareMethod: 'and',
720
+ rules: [
721
+ {
722
+ id: 'a3c7d66f-5c2d-4a24-83bc-03b0a2d9c32b',
723
+ name: 'Rule 1',
724
+ subject: 'order.status',
725
+ resource: 'не обработан',
726
+ condition: '=',
727
+ },
728
+ {
729
+ id: 'a3c7d66f-5c2d-4a24-83bc-03b0a2d9c32b',
730
+ name: 'Rule 2',
731
+ subject: 'feature.status',
732
+ resource: 'отменен',
733
+ condition: '=',
734
+ },
735
+ ],
736
+ },
737
+ ],
738
+ },
739
+ ];
740
+ const policies = config.map(cfg => AbilityPolicy_1.default.parse(cfg));
741
+ const result = new AbilityResolver_1.default(policies).resolve('order.status', {
832
742
  account: {
833
- id: '1',
834
- roles: ['managers'],
743
+ roles: ['user', 'couch'],
835
744
  },
836
- resource: {
837
- id: '1',
745
+ order: {
746
+ status: 'не обработан',
747
+ },
748
+ feature: {
749
+ status: 'отменен',
750
+ // status: 'завершен'
838
751
  },
839
752
  });
840
753
  res.statusCode = 200;
841
754
  res.setHeader('content-type', 'application/json');
842
755
  res.write(JSON.stringify({
843
- status: 'Done',
756
+ status: result.isDeny() ? 'deny' : 'permit',
844
757
  }));
845
758
  res.end();
846
759
  });
@@ -859,16 +772,6 @@ server.listen(8080, 'localhost', () => {
859
772
 
860
773
  module.exports = require("node:http");
861
774
 
862
- /***/ }),
863
-
864
- /***/ "./src/policies.json":
865
- /*!***************************!*\
866
- !*** ./src/policies.json ***!
867
- \***************************/
868
- /***/ ((module) => {
869
-
870
- module.exports = /*#__PURE__*/JSON.parse('[{"name":"Просмотр чужого логина разрешен только владельцу аккаунта и администраторам","action":"account.read","effect":0,"compareMethod":0,"ruleSet":[{"name":"Не владелец аккаунта и не администратор","compareMethod":1,"rules":[{"name":"Владелец аккаунта","matches":["account.id","<>","resource.id"]},{"name":"Администратор","matches":["account.roles","not in","administrator"]}]}]},{"name":"Unauthorized. Access token expected","action":"access.auth","effect":0,"compareMethod":1,"ruleSet":[{"name":"Токен - AccessToken","matches":["token.type","=","access"]},{"name":"Токен не пустой","matches":["token.id","<>","ACCESS_TOKEN_EMPTY_ID"]}]},{"id":"1121","name":"Менеджеры не могут редактировать заказы, созданные более 3-х дней назад, а так же завершенные и отменённые заявки","action":"order.update","effect":0,"compareMethod":1,"ruleSet":[{"name":"Менеджеры","compareMethod":0,"rules":[{"name":"Отдел - Менеджеры","matches":["user.department","=","managers"]},{"name":"Роль - Менеджер","matches":["user.roles","in","manager"]}]},{"name":"Заказы более 3-х дней, а так же завершённые и отменённые заказы","compareMethod":0,"rules":[{"name":"заказ создан более 3-х дней назад","matches":["order.createdDaysAgo",">",3]},{"name":"заказ завершён","matches":["order.status","=","COMPLETED"]},{"name":"заказ отменён","matches":["order.status","=","CANCELLED"]}]}]},{"name":"Менеджеры не могут создавать заказы","action":"order.create","effect":0,"compareMethod":0,"ruleSet":[{"name":"Менеджеры","compareMethod":0,"rules":[{"name":"Отдел - Менеджеры","matches":["user.department","=","managers"]},{"name":"Роль - Менеджер","matches":["user.roles","in","manager"]}]}]},{"name":"Уборщицы не могут видеть стоимость заказа","action":"order.read","effect":0,"compareMethod":0,"ruleSet":[{"name":"все из отдела уборщиц","matches":["user.department","in","cleaner"]}]}]');
871
-
872
775
  /***/ })
873
776
 
874
777
  /******/ });