@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.
- package/build/playground.js +183 -280
- package/build/playground.js.map +1 -1
- package/dist/AbilityCode.d.ts +1 -0
- package/dist/AbilityCompare.d.ts +3 -2
- package/dist/AbilityCondition.d.ts +2 -1
- package/dist/AbilityMatch.d.ts +5 -4
- package/dist/AbilityParser.d.ts +0 -15
- package/dist/AbilityPolicy.d.ts +7 -13
- package/dist/AbilityPolicyEffect.d.ts +4 -3
- package/dist/AbilityResolver.d.ts +1 -1
- package/dist/AbilityRule.d.ts +24 -13
- package/dist/AbilityRuleSet.d.ts +4 -7
- package/dist/index.js +130 -180
- package/package.json +1 -1
package/build/playground.js
CHANGED
|
@@ -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
|
|
47
|
-
static
|
|
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
|
|
130
|
-
static
|
|
131
|
-
static
|
|
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.
|
|
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.
|
|
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
|
|
321
|
-
this.id = 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
|
|
213
|
+
* @param resources - The resource to check
|
|
346
214
|
*/
|
|
347
|
-
check(
|
|
348
|
-
this.matchState = AbilityMatch_1.default.
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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(
|
|
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 =
|
|
392
|
-
ruleSet.forEach(
|
|
393
|
-
|
|
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
|
|
436
|
-
static
|
|
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(
|
|
462
|
-
this.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(
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
415
|
+
id;
|
|
416
|
+
state = AbilityMatch_1.default.pending;
|
|
558
417
|
constructor(params) {
|
|
559
|
-
const { name,
|
|
560
|
-
this.
|
|
561
|
-
this.
|
|
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.
|
|
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
|
|
622
|
-
* @param
|
|
482
|
+
* Extract values from the resourceData
|
|
483
|
+
* @param resourceData - The resourceData to extract values from
|
|
623
484
|
*/
|
|
624
|
-
extractValues(
|
|
625
|
-
const [subjectPathName, _condition, staticValueOrPathName] = this.matches;
|
|
485
|
+
extractValues(resourceData) {
|
|
626
486
|
let leftSideValue;
|
|
627
487
|
let rightSideValue;
|
|
628
|
-
if (
|
|
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(
|
|
635
|
-
leftSideValue = this.getDotNotationValue(
|
|
494
|
+
if (isPath(this.subject)) {
|
|
495
|
+
leftSideValue = this.getDotNotationValue(resourceData, this.subject);
|
|
636
496
|
}
|
|
637
|
-
|
|
638
|
-
|
|
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 =
|
|
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(
|
|
674
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
743
|
-
this.id = 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.
|
|
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.
|
|
763
|
-
if (ruleCheckStates.every(ruleState => AbilityMatch_1.default.
|
|
764
|
-
this.state = AbilityMatch_1.default.
|
|
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.
|
|
768
|
-
if (ruleCheckStates.some(ruleState => AbilityMatch_1.default.
|
|
769
|
-
this.state = AbilityMatch_1.default.
|
|
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(
|
|
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
|
-
|
|
830
|
-
|
|
831
|
-
|
|
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
|
-
|
|
834
|
-
roles: ['managers'],
|
|
743
|
+
roles: ['user', 'couch'],
|
|
835
744
|
},
|
|
836
|
-
|
|
837
|
-
|
|
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: '
|
|
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
|
/******/ });
|