@via-profit/ability 3.1.1 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +129 -129
- package/CONTRIBUTING.md +14 -14
- package/LICENSE +21 -21
- package/README.md +1325 -1147
- package/SECURITY.md +33 -33
- package/dist/core/AbilityPolicy.d.ts +8 -13
- package/dist/core/AbilityRule.d.ts +7 -1
- package/dist/core/AbilityRuleSet.d.ts +6 -4
- package/dist/index.js +647 -1002
- package/package.json +73 -76
package/dist/index.js
CHANGED
|
@@ -1,105 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
/******/ "use strict";
|
|
3
|
-
/******/ var __webpack_modules__ = ({
|
|
1
|
+
'use strict';
|
|
4
2
|
|
|
5
|
-
/***/ 6:
|
|
6
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/***/ }),
|
|
13
|
-
|
|
14
|
-
/***/ 829:
|
|
15
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
19
|
-
exports.AbilityInMemoryCache = void 0;
|
|
20
|
-
class AbilityInMemoryCache {
|
|
21
|
-
store = new Map();
|
|
22
|
-
async get(key) {
|
|
23
|
-
const entry = this.store.get(key);
|
|
24
|
-
if (!entry)
|
|
25
|
-
return undefined;
|
|
26
|
-
if (Date.now() > entry.expires) {
|
|
27
|
-
this.store.delete(key);
|
|
28
|
-
return undefined;
|
|
29
|
-
}
|
|
30
|
-
return entry.value;
|
|
31
|
-
}
|
|
32
|
-
async set(key, value, ttlSeconds = 60) {
|
|
33
|
-
this.store.set(key, {
|
|
34
|
-
value,
|
|
35
|
-
expires: Date.now() + ttlSeconds * 1000,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
serialize(input) {
|
|
39
|
-
return this.fastHash(this.stableStringify(input));
|
|
40
|
-
}
|
|
41
|
-
async delete(key) {
|
|
42
|
-
this.store.delete(key);
|
|
43
|
-
}
|
|
44
|
-
async clear() {
|
|
45
|
-
this.store.clear();
|
|
46
|
-
}
|
|
47
|
-
async deleteByPrefix(prefix) {
|
|
48
|
-
for (const key of this.store.keys()) {
|
|
49
|
-
if (key.startsWith(prefix)) {
|
|
50
|
-
this.store.delete(key);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
fastHash(str) {
|
|
55
|
-
let hash = 5381;
|
|
56
|
-
for (let i = 0; i < str.length; i++) {
|
|
57
|
-
hash = (hash * 33) ^ str.charCodeAt(i);
|
|
58
|
-
}
|
|
59
|
-
return (hash >>> 0).toString(36);
|
|
60
|
-
}
|
|
61
|
-
stableStringify(obj) {
|
|
62
|
-
if (obj === null)
|
|
63
|
-
return 'null';
|
|
64
|
-
const type = typeof obj;
|
|
65
|
-
if (type === 'string')
|
|
66
|
-
return JSON.stringify(obj);
|
|
67
|
-
if (type === 'number' || type === 'boolean')
|
|
68
|
-
return String(obj);
|
|
69
|
-
if (type === 'undefined')
|
|
70
|
-
return 'undefined';
|
|
71
|
-
if (Array.isArray(obj)) {
|
|
72
|
-
let out = '[';
|
|
73
|
-
for (let i = 0; i < obj.length; i++) {
|
|
74
|
-
if (i > 0)
|
|
75
|
-
out += ',';
|
|
76
|
-
out += this.stableStringify(obj[i]);
|
|
77
|
-
}
|
|
78
|
-
return out + ']';
|
|
79
|
-
}
|
|
80
|
-
const keys = Object.keys(obj);
|
|
81
|
-
keys.sort();
|
|
82
|
-
let out = '{';
|
|
83
|
-
for (let i = 0; i < keys.length; i++) {
|
|
84
|
-
const k = keys[i];
|
|
85
|
-
if (i > 0)
|
|
86
|
-
out += ',';
|
|
87
|
-
out += k + ':' + this.stableStringify(obj[k]);
|
|
88
|
-
}
|
|
89
|
-
return out + '}';
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
exports.AbilityInMemoryCache = AbilityInMemoryCache;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/***/ }),
|
|
96
|
-
|
|
97
|
-
/***/ 301:
|
|
98
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
102
|
-
exports.AbilityCode = void 0;
|
|
103
3
|
class AbilityCode {
|
|
104
4
|
_code;
|
|
105
5
|
constructor(code) {
|
|
@@ -115,44 +15,24 @@ class AbilityCode {
|
|
|
115
15
|
return !this.isEqual(compareWith);
|
|
116
16
|
}
|
|
117
17
|
}
|
|
118
|
-
exports.AbilityCode = AbilityCode;
|
|
119
|
-
exports["default"] = AbilityCode;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
/***/ }),
|
|
123
18
|
|
|
124
|
-
|
|
125
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
129
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
130
|
-
};
|
|
131
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
132
|
-
exports.AbilityCompare = void 0;
|
|
133
|
-
const AbilityCode_1 = __importDefault(__webpack_require__(301));
|
|
134
|
-
class AbilityCompare extends AbilityCode_1.default {
|
|
19
|
+
class AbilityCompare extends AbilityCode {
|
|
135
20
|
static and = new AbilityCompare('and');
|
|
136
21
|
static or = new AbilityCompare('or');
|
|
137
22
|
}
|
|
138
|
-
exports.AbilityCompare = AbilityCompare;
|
|
139
|
-
exports["default"] = AbilityCompare;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
/***/ }),
|
|
143
|
-
|
|
144
|
-
/***/ 167:
|
|
145
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
146
23
|
|
|
24
|
+
class AbilityError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
class AbilityParserError extends Error {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
147
34
|
|
|
148
|
-
|
|
149
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
150
|
-
};
|
|
151
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
152
|
-
exports.AbilityCondition = void 0;
|
|
153
|
-
const AbilityCode_1 = __importDefault(__webpack_require__(301));
|
|
154
|
-
const AbilityError_1 = __webpack_require__(216);
|
|
155
|
-
class AbilityCondition extends AbilityCode_1.default {
|
|
35
|
+
class AbilityCondition extends AbilityCode {
|
|
156
36
|
static equals = new AbilityCondition('=');
|
|
157
37
|
static not_equals = new AbilityCondition('<>');
|
|
158
38
|
static greater_than = new AbilityCondition('>');
|
|
@@ -193,7 +73,7 @@ class AbilityCondition extends AbilityCode_1.default {
|
|
|
193
73
|
case 'length_equals':
|
|
194
74
|
return this.length_equals;
|
|
195
75
|
default:
|
|
196
|
-
throw new
|
|
76
|
+
throw new AbilityParserError(`Literal ${literal} does not found in AbilityCondition class`);
|
|
197
77
|
}
|
|
198
78
|
}
|
|
199
79
|
get literal() {
|
|
@@ -207,134 +87,13 @@ class AbilityCondition extends AbilityCode_1.default {
|
|
|
207
87
|
return literal;
|
|
208
88
|
}
|
|
209
89
|
}
|
|
210
|
-
exports.AbilityCondition = AbilityCondition;
|
|
211
|
-
exports["default"] = AbilityCondition;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
/***/ }),
|
|
215
|
-
|
|
216
|
-
/***/ 216:
|
|
217
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
221
|
-
exports.AbilityParserError = exports.AbilityError = void 0;
|
|
222
|
-
class AbilityError extends Error {
|
|
223
|
-
constructor(message) {
|
|
224
|
-
super(message);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
exports.AbilityError = AbilityError;
|
|
228
|
-
class AbilityParserError extends Error {
|
|
229
|
-
constructor(message) {
|
|
230
|
-
super(message);
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
exports.AbilityParserError = AbilityParserError;
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
/***/ }),
|
|
237
|
-
|
|
238
|
-
/***/ 221:
|
|
239
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
243
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
244
|
-
};
|
|
245
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
246
|
-
exports.AbilityExplainPolicy = exports.AbilityExplainRuleSet = exports.AbilityExplainRule = exports.AbilityExplain = void 0;
|
|
247
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
248
|
-
class AbilityExplain {
|
|
249
|
-
type;
|
|
250
|
-
children;
|
|
251
|
-
name;
|
|
252
|
-
match;
|
|
253
|
-
constructor(config, children = []) {
|
|
254
|
-
this.type = config.type;
|
|
255
|
-
this.children = children;
|
|
256
|
-
this.name = config.name;
|
|
257
|
-
this.match = config.match;
|
|
258
|
-
}
|
|
259
|
-
toString(indent = 0) {
|
|
260
|
-
const pad = ' '.repeat(indent);
|
|
261
|
-
const mark = this.match.code === AbilityMatch_1.default.match.code ? '✓' : '✗';
|
|
262
|
-
let out = `${pad}${mark} ${this.type} «${this.name}» is ${this.match.code}`;
|
|
263
|
-
this.children.forEach(child => {
|
|
264
|
-
out += '\n' + child.toString(indent + 1);
|
|
265
|
-
});
|
|
266
|
-
return out;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
exports.AbilityExplain = AbilityExplain;
|
|
270
|
-
class AbilityExplainRule extends AbilityExplain {
|
|
271
|
-
constructor(rule) {
|
|
272
|
-
super({
|
|
273
|
-
type: 'rule',
|
|
274
|
-
match: rule.state,
|
|
275
|
-
name: rule.name,
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
exports.AbilityExplainRule = AbilityExplainRule;
|
|
280
|
-
class AbilityExplainRuleSet extends AbilityExplain {
|
|
281
|
-
constructor(ruleSet) {
|
|
282
|
-
const children = ruleSet.rules.map(rule => new AbilityExplainRule(rule));
|
|
283
|
-
super({
|
|
284
|
-
type: 'ruleSet',
|
|
285
|
-
match: ruleSet.state,
|
|
286
|
-
name: ruleSet.name,
|
|
287
|
-
}, children);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
exports.AbilityExplainRuleSet = AbilityExplainRuleSet;
|
|
291
|
-
class AbilityExplainPolicy extends AbilityExplain {
|
|
292
|
-
constructor(policy) {
|
|
293
|
-
const children = policy.ruleSet.map(ruleSet => new AbilityExplainRuleSet(ruleSet));
|
|
294
|
-
super({
|
|
295
|
-
type: 'policy',
|
|
296
|
-
name: policy.name,
|
|
297
|
-
match: policy.matchState,
|
|
298
|
-
}, children);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
exports.AbilityExplainPolicy = AbilityExplainPolicy;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
/***/ }),
|
|
305
|
-
|
|
306
|
-
/***/ 247:
|
|
307
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
308
|
-
|
|
309
90
|
|
|
310
|
-
|
|
311
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
312
|
-
};
|
|
313
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
314
|
-
exports.AbilityMatch = void 0;
|
|
315
|
-
const AbilityCode_1 = __importDefault(__webpack_require__(301));
|
|
316
|
-
class AbilityMatch extends AbilityCode_1.default {
|
|
91
|
+
class AbilityMatch extends AbilityCode {
|
|
317
92
|
static pending = new AbilityMatch('pending');
|
|
318
93
|
static match = new AbilityMatch('match');
|
|
319
94
|
static mismatch = new AbilityMatch('mismatch');
|
|
320
95
|
}
|
|
321
|
-
exports.AbilityMatch = AbilityMatch;
|
|
322
|
-
exports["default"] = AbilityMatch;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
/***/ }),
|
|
326
|
-
|
|
327
|
-
/***/ 147:
|
|
328
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
329
96
|
|
|
330
|
-
|
|
331
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
332
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
333
|
-
};
|
|
334
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
335
|
-
exports.AbilityParser = void 0;
|
|
336
|
-
const AbilityError_1 = __webpack_require__(216);
|
|
337
|
-
const AbilityCondition_1 = __importDefault(__webpack_require__(167));
|
|
338
97
|
class AbilityParser {
|
|
339
98
|
/**
|
|
340
99
|
* Sets a value in a nested object structure based on a dot/bracket notation path.
|
|
@@ -344,12 +103,12 @@ class AbilityParser {
|
|
|
344
103
|
*/
|
|
345
104
|
static setValueDotValue(object, path, value) {
|
|
346
105
|
if (!path || path.trim().length === 0) {
|
|
347
|
-
throw new
|
|
106
|
+
throw new AbilityParserError(`Invalid path provided on a [${path}]`);
|
|
348
107
|
}
|
|
349
108
|
const way = path.replace(/\[/g, '.').replace(/]/g, '').split('.');
|
|
350
109
|
const last = way.pop();
|
|
351
110
|
if (!last) {
|
|
352
|
-
throw new
|
|
111
|
+
throw new AbilityParserError(`Invalid path provided on a [${path}]`);
|
|
353
112
|
}
|
|
354
113
|
const lastObj = way.reduce((acc, key, index, array) => {
|
|
355
114
|
const currentValue = acc[key];
|
|
@@ -362,7 +121,7 @@ class AbilityParser {
|
|
|
362
121
|
return newValue;
|
|
363
122
|
}
|
|
364
123
|
if (typeof currentValue !== 'object') {
|
|
365
|
-
throw new
|
|
124
|
+
throw new AbilityParserError(`Cannot set property '${key}' on non-object value at path: ${path}`);
|
|
366
125
|
}
|
|
367
126
|
return currentValue;
|
|
368
127
|
}, object);
|
|
@@ -371,7 +130,7 @@ class AbilityParser {
|
|
|
371
130
|
typeof existingValue === 'object' &&
|
|
372
131
|
existingValue !== null &&
|
|
373
132
|
!Array.isArray(existingValue)) {
|
|
374
|
-
throw new
|
|
133
|
+
throw new AbilityParserError(`Cannot set primitive value on existing object at path: ${path}`);
|
|
375
134
|
}
|
|
376
135
|
lastObj[last] = value;
|
|
377
136
|
}
|
|
@@ -418,20 +177,20 @@ class AbilityParser {
|
|
|
418
177
|
*/
|
|
419
178
|
static determineTypeFromRule(rule) {
|
|
420
179
|
// Numeric comparisons - always number
|
|
421
|
-
if (rule.condition.isEqual(
|
|
422
|
-
rule.condition.isEqual(
|
|
423
|
-
rule.condition.isEqual(
|
|
424
|
-
rule.condition.isEqual(
|
|
180
|
+
if (rule.condition.isEqual(AbilityCondition.greater_than) ||
|
|
181
|
+
rule.condition.isEqual(AbilityCondition.less_than) ||
|
|
182
|
+
rule.condition.isEqual(AbilityCondition.greater_or_equal) ||
|
|
183
|
+
rule.condition.isEqual(AbilityCondition.less_or_equal)) {
|
|
425
184
|
return 'number';
|
|
426
185
|
}
|
|
427
186
|
// Array operations
|
|
428
|
-
if (rule.condition.isEqual(
|
|
429
|
-
rule.condition.isEqual(
|
|
187
|
+
if (rule.condition.isEqual(AbilityCondition.in) ||
|
|
188
|
+
rule.condition.isEqual(AbilityCondition.not_in)) {
|
|
430
189
|
return this.getArrayType(rule.resource);
|
|
431
190
|
}
|
|
432
191
|
// Equality/Inequality operations
|
|
433
|
-
if (rule.condition.isEqual(
|
|
434
|
-
rule.condition.isEqual(
|
|
192
|
+
if (rule.condition.isEqual(AbilityCondition.equals) ||
|
|
193
|
+
rule.condition.isEqual(AbilityCondition.not_equals)) {
|
|
435
194
|
return this.getPrimitiveType(rule.resource);
|
|
436
195
|
}
|
|
437
196
|
return 'any';
|
|
@@ -562,29 +321,60 @@ class AbilityParser {
|
|
|
562
321
|
return output;
|
|
563
322
|
}
|
|
564
323
|
}
|
|
565
|
-
exports.AbilityParser = AbilityParser;
|
|
566
|
-
exports["default"] = AbilityParser;
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
/***/ }),
|
|
570
|
-
|
|
571
|
-
/***/ 278:
|
|
572
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
573
324
|
|
|
325
|
+
class AbilityExplain {
|
|
326
|
+
type;
|
|
327
|
+
children;
|
|
328
|
+
name;
|
|
329
|
+
match;
|
|
330
|
+
constructor(config, children = []) {
|
|
331
|
+
this.type = config.type;
|
|
332
|
+
this.children = children;
|
|
333
|
+
this.name = config.name;
|
|
334
|
+
this.match = config.match;
|
|
335
|
+
}
|
|
336
|
+
toString(indent = 0) {
|
|
337
|
+
const pad = ' '.repeat(indent);
|
|
338
|
+
const mark = this.match.code === AbilityMatch.match.code ? '✓' : '✗';
|
|
339
|
+
let out = `${pad}${mark} ${this.type} «${this.name}» is ${this.match.code}`;
|
|
340
|
+
this.children.forEach(child => {
|
|
341
|
+
out += '\n' + child.toString(indent + 1);
|
|
342
|
+
});
|
|
343
|
+
return out;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
class AbilityExplainRule extends AbilityExplain {
|
|
347
|
+
constructor(rule) {
|
|
348
|
+
super({
|
|
349
|
+
type: 'rule',
|
|
350
|
+
match: rule.state,
|
|
351
|
+
name: rule.name,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
class AbilityExplainRuleSet extends AbilityExplain {
|
|
356
|
+
constructor(ruleSet) {
|
|
357
|
+
const children = ruleSet.rules.map(rule => new AbilityExplainRule(rule));
|
|
358
|
+
super({
|
|
359
|
+
type: 'ruleSet',
|
|
360
|
+
match: ruleSet.state,
|
|
361
|
+
name: ruleSet.name,
|
|
362
|
+
}, children);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
class AbilityExplainPolicy extends AbilityExplain {
|
|
366
|
+
constructor(policy) {
|
|
367
|
+
const children = policy.ruleSet.map(ruleSet => new AbilityExplainRuleSet(ruleSet));
|
|
368
|
+
super({
|
|
369
|
+
type: 'policy',
|
|
370
|
+
name: policy.name,
|
|
371
|
+
match: policy.matchState,
|
|
372
|
+
}, children);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
574
375
|
|
|
575
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
576
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
577
|
-
};
|
|
578
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
579
|
-
exports.AbilityPolicy = void 0;
|
|
580
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
581
|
-
const AbilityCompare_1 = __importDefault(__webpack_require__(413));
|
|
582
|
-
const AbilityExplain_1 = __webpack_require__(221);
|
|
583
|
-
const AbilityError_1 = __webpack_require__(216);
|
|
584
|
-
const AbilityJSONParser_1 = __webpack_require__(909);
|
|
585
|
-
const AbilityDSLParser_1 = __webpack_require__(577);
|
|
586
376
|
class AbilityPolicy {
|
|
587
|
-
matchState =
|
|
377
|
+
matchState = AbilityMatch.pending;
|
|
588
378
|
/**
|
|
589
379
|
* List of rules
|
|
590
380
|
*/
|
|
@@ -599,7 +389,7 @@ class AbilityPolicy {
|
|
|
599
389
|
* rules will be returns «permit» status and for the «or» - if\
|
|
600
390
|
* one of the rules returns as «permit»
|
|
601
391
|
*/
|
|
602
|
-
compareMethod =
|
|
392
|
+
compareMethod = AbilityCompare.and;
|
|
603
393
|
/**
|
|
604
394
|
* Policy name
|
|
605
395
|
*/
|
|
@@ -614,7 +404,7 @@ class AbilityPolicy {
|
|
|
614
404
|
*/
|
|
615
405
|
permission;
|
|
616
406
|
constructor(params) {
|
|
617
|
-
const { name, id, permission, effect, compareMethod =
|
|
407
|
+
const { name, id, permission, effect, compareMethod = AbilityCompare.and } = params;
|
|
618
408
|
this.name = name;
|
|
619
409
|
this.id = id;
|
|
620
410
|
this.permission = permission;
|
|
@@ -645,7 +435,7 @@ class AbilityPolicy {
|
|
|
645
435
|
* @param environment - The user environment object
|
|
646
436
|
*/
|
|
647
437
|
async check(resource, environment) {
|
|
648
|
-
this.matchState =
|
|
438
|
+
this.matchState = AbilityMatch.mismatch;
|
|
649
439
|
if (!this.ruleSet.length) {
|
|
650
440
|
return this.matchState;
|
|
651
441
|
}
|
|
@@ -653,94 +443,104 @@ class AbilityPolicy {
|
|
|
653
443
|
for (const ruleSet of this.ruleSet) {
|
|
654
444
|
const state = await ruleSet.check(resource, environment);
|
|
655
445
|
rulesetCheckStates.push(state);
|
|
656
|
-
if (
|
|
446
|
+
if (AbilityCompare.and.isEqual(this.compareMethod) && AbilityMatch.mismatch.isEqual(state)) {
|
|
657
447
|
return this.matchState; // mismatch
|
|
658
448
|
}
|
|
659
|
-
if (
|
|
660
|
-
this.matchState =
|
|
449
|
+
if (AbilityCompare.or.isEqual(this.compareMethod) && AbilityMatch.match.isEqual(state)) {
|
|
450
|
+
this.matchState = AbilityMatch.match;
|
|
661
451
|
return this.matchState;
|
|
662
452
|
}
|
|
663
453
|
}
|
|
664
|
-
if (
|
|
665
|
-
if (rulesetCheckStates.every(s =>
|
|
666
|
-
this.matchState =
|
|
454
|
+
if (AbilityCompare.and.isEqual(this.compareMethod)) {
|
|
455
|
+
if (rulesetCheckStates.every(s => AbilityMatch.match.isEqual(s))) {
|
|
456
|
+
this.matchState = AbilityMatch.match;
|
|
667
457
|
}
|
|
668
458
|
}
|
|
669
|
-
if (
|
|
670
|
-
if (rulesetCheckStates.some(s =>
|
|
671
|
-
this.matchState =
|
|
459
|
+
if (AbilityCompare.or.isEqual(this.compareMethod)) {
|
|
460
|
+
if (rulesetCheckStates.some(s => AbilityMatch.match.isEqual(s))) {
|
|
461
|
+
this.matchState = AbilityMatch.match;
|
|
672
462
|
}
|
|
673
463
|
}
|
|
674
464
|
return this.matchState;
|
|
675
465
|
}
|
|
676
466
|
explain() {
|
|
677
|
-
if (this.matchState ===
|
|
678
|
-
throw new
|
|
467
|
+
if (this.matchState === AbilityMatch.pending) {
|
|
468
|
+
throw new AbilityError('First, run the check method, then explain');
|
|
469
|
+
}
|
|
470
|
+
return new AbilityExplainPolicy(this);
|
|
471
|
+
}
|
|
472
|
+
copyWith(props) {
|
|
473
|
+
const policy = new AbilityPolicy({
|
|
474
|
+
id: props.id ?? this.id,
|
|
475
|
+
name: props.name ?? this.name,
|
|
476
|
+
permission: props.permission ?? this.permission,
|
|
477
|
+
effect: props.effect ?? this.effect,
|
|
478
|
+
compareMethod: props.compareMethod ?? this.compareMethod,
|
|
479
|
+
});
|
|
480
|
+
const nextRuleSet = props.ruleSet ?? this.ruleSet;
|
|
481
|
+
for (const rule of nextRuleSet) {
|
|
482
|
+
policy.addRuleSet(rule);
|
|
679
483
|
}
|
|
680
|
-
return
|
|
484
|
+
return policy;
|
|
681
485
|
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
class AbilityPolicyEffect extends AbilityCode {
|
|
489
|
+
static deny = new AbilityPolicyEffect('deny');
|
|
490
|
+
static permit = new AbilityPolicyEffect('permit');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
class AbilityResult {
|
|
682
494
|
/**
|
|
683
|
-
*
|
|
684
|
-
* @param configs - Array of policy configurations
|
|
685
|
-
* @returns Array of AbilityPolicy instances
|
|
495
|
+
* Already checked policies (after call the policy.check())
|
|
686
496
|
*/
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
497
|
+
policies;
|
|
498
|
+
constructor(policies) {
|
|
499
|
+
this.policies = policies;
|
|
500
|
+
}
|
|
690
501
|
/**
|
|
691
|
-
*
|
|
502
|
+
* Returns a list of explanations for each policy involved in the ability evaluation.
|
|
503
|
+
* Each item describes how a specific policy contributed to the final permission result.
|
|
504
|
+
*
|
|
505
|
+
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
692
506
|
*/
|
|
693
|
-
|
|
694
|
-
return
|
|
507
|
+
explain() {
|
|
508
|
+
return this.policies.map(policy => {
|
|
509
|
+
return new AbilityExplainPolicy(policy);
|
|
510
|
+
});
|
|
695
511
|
}
|
|
696
|
-
|
|
697
|
-
|
|
512
|
+
getLastMatchedPolicy() {
|
|
513
|
+
for (let i = this.policies.length - 1; i >= 0; i--) {
|
|
514
|
+
if (this.policies[i].matchState.isEqual(AbilityMatch.match)) {
|
|
515
|
+
return this.policies[i];
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return null;
|
|
698
519
|
}
|
|
699
|
-
|
|
700
|
-
|
|
520
|
+
isAllowed() {
|
|
521
|
+
const effect = this.getLastEffectOfMatchedPolicy();
|
|
522
|
+
return effect?.isEqual(AbilityPolicyEffect.permit) ?? false;
|
|
701
523
|
}
|
|
702
|
-
|
|
703
|
-
|
|
524
|
+
isDenied() {
|
|
525
|
+
const effect = this.getLastEffectOfMatchedPolicy();
|
|
526
|
+
return effect?.isEqual(AbilityPolicyEffect.deny) ?? true;
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Get the last effect of the policy
|
|
530
|
+
*
|
|
531
|
+
* @returns {AbilityPolicyEffect | null}
|
|
532
|
+
*/
|
|
533
|
+
getLastEffectOfMatchedPolicy() {
|
|
534
|
+
for (let i = this.policies.length - 1; i >= 0; i--) {
|
|
535
|
+
const p = this.policies[i];
|
|
536
|
+
if (p.matchState.isEqual(AbilityMatch.match)) {
|
|
537
|
+
return p.effect;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
return null;
|
|
704
541
|
}
|
|
705
542
|
}
|
|
706
|
-
exports.AbilityPolicy = AbilityPolicy;
|
|
707
|
-
exports["default"] = AbilityPolicy;
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
/***/ }),
|
|
711
|
-
|
|
712
|
-
/***/ 179:
|
|
713
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
717
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
718
|
-
};
|
|
719
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
720
|
-
exports.AbilityPolicyEffect = void 0;
|
|
721
|
-
const AbilityCode_1 = __importDefault(__webpack_require__(301));
|
|
722
|
-
class AbilityPolicyEffect extends AbilityCode_1.default {
|
|
723
|
-
static deny = new AbilityPolicyEffect('deny');
|
|
724
|
-
static permit = new AbilityPolicyEffect('permit');
|
|
725
|
-
}
|
|
726
|
-
exports.AbilityPolicyEffect = AbilityPolicyEffect;
|
|
727
|
-
exports["default"] = AbilityPolicyEffect;
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
/***/ }),
|
|
731
|
-
|
|
732
|
-
/***/ 634:
|
|
733
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
734
543
|
|
|
735
|
-
|
|
736
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
737
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
738
|
-
};
|
|
739
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
740
|
-
exports.AbilityResolver = void 0;
|
|
741
|
-
const AbilityError_1 = __webpack_require__(216);
|
|
742
|
-
const AbilityResult_1 = __webpack_require__(941);
|
|
743
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
744
544
|
class AbilityResolver {
|
|
745
545
|
policies;
|
|
746
546
|
cache;
|
|
@@ -775,20 +575,20 @@ class AbilityResolver {
|
|
|
775
575
|
}
|
|
776
576
|
}
|
|
777
577
|
const policyMatchState = await policy.check(resource, environment);
|
|
778
|
-
if (policyMatchState ===
|
|
779
|
-
throw new
|
|
578
|
+
if (policyMatchState === AbilityMatch.pending) {
|
|
579
|
+
throw new AbilityError(`The policy "${policy.name}" is still in a pending state. Make sure to call "check" to evaluate the policy before resolving permissions.`);
|
|
780
580
|
}
|
|
781
581
|
if (this.cache) {
|
|
782
582
|
await this.cache.set(cacheKey, policyMatchState);
|
|
783
583
|
}
|
|
784
584
|
}
|
|
785
|
-
return new
|
|
585
|
+
return new AbilityResult(filteredPolicies);
|
|
786
586
|
}
|
|
787
587
|
async enforce(permission, resource, environment) {
|
|
788
588
|
const result = await this.resolve(permission, resource, environment);
|
|
789
589
|
if (result.isDenied()) {
|
|
790
590
|
const policyName = result.getLastMatchedPolicy()?.name?.toString() || 'unknown';
|
|
791
|
-
throw new
|
|
591
|
+
throw new AbilityError(`Permission denied by policy "${policyName}"`);
|
|
792
592
|
}
|
|
793
593
|
}
|
|
794
594
|
/**
|
|
@@ -817,91 +617,7 @@ class AbilityResolver {
|
|
|
817
617
|
await this.cache?.clear();
|
|
818
618
|
}
|
|
819
619
|
}
|
|
820
|
-
exports.AbilityResolver = AbilityResolver;
|
|
821
|
-
exports["default"] = AbilityResolver;
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
/***/ }),
|
|
825
|
-
|
|
826
|
-
/***/ 941:
|
|
827
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
828
|
-
|
|
829
620
|
|
|
830
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
831
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
832
|
-
};
|
|
833
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
834
|
-
exports.AbilityResult = void 0;
|
|
835
|
-
const AbilityExplain_1 = __webpack_require__(221);
|
|
836
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
837
|
-
const AbilityPolicyEffect_1 = __importDefault(__webpack_require__(179));
|
|
838
|
-
class AbilityResult {
|
|
839
|
-
/**
|
|
840
|
-
* Already checked policies (after call the policy.check())
|
|
841
|
-
*/
|
|
842
|
-
policies;
|
|
843
|
-
constructor(policies) {
|
|
844
|
-
this.policies = policies;
|
|
845
|
-
}
|
|
846
|
-
/**
|
|
847
|
-
* Returns a list of explanations for each policy involved in the ability evaluation.
|
|
848
|
-
* Each item describes how a specific policy contributed to the final permission result.
|
|
849
|
-
*
|
|
850
|
-
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
851
|
-
*/
|
|
852
|
-
explain() {
|
|
853
|
-
return this.policies.map(policy => {
|
|
854
|
-
return new AbilityExplain_1.AbilityExplainPolicy(policy);
|
|
855
|
-
});
|
|
856
|
-
}
|
|
857
|
-
getLastMatchedPolicy() {
|
|
858
|
-
for (let i = this.policies.length - 1; i >= 0; i--) {
|
|
859
|
-
if (this.policies[i].matchState.isEqual(AbilityMatch_1.default.match)) {
|
|
860
|
-
return this.policies[i];
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
return null;
|
|
864
|
-
}
|
|
865
|
-
isAllowed() {
|
|
866
|
-
const effect = this.getLastEffectOfMatchedPolicy();
|
|
867
|
-
return effect?.isEqual(AbilityPolicyEffect_1.default.permit) ?? false;
|
|
868
|
-
}
|
|
869
|
-
isDenied() {
|
|
870
|
-
const effect = this.getLastEffectOfMatchedPolicy();
|
|
871
|
-
return effect?.isEqual(AbilityPolicyEffect_1.default.deny) ?? true;
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Get the last effect of the policy
|
|
875
|
-
*
|
|
876
|
-
* @returns {AbilityPolicyEffect | null}
|
|
877
|
-
*/
|
|
878
|
-
getLastEffectOfMatchedPolicy() {
|
|
879
|
-
for (let i = this.policies.length - 1; i >= 0; i--) {
|
|
880
|
-
const p = this.policies[i];
|
|
881
|
-
if (p.matchState.isEqual(AbilityMatch_1.default.match)) {
|
|
882
|
-
return p.effect;
|
|
883
|
-
}
|
|
884
|
-
}
|
|
885
|
-
return null;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
exports.AbilityResult = AbilityResult;
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
/***/ }),
|
|
892
|
-
|
|
893
|
-
/***/ 306:
|
|
894
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
898
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
899
|
-
};
|
|
900
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
901
|
-
exports.AbilityRule = void 0;
|
|
902
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
903
|
-
const AbilityCondition_1 = __importDefault(__webpack_require__(167));
|
|
904
|
-
const AbilityJSONParser_1 = __webpack_require__(909);
|
|
905
621
|
/**
|
|
906
622
|
* Represents a rule that defines a condition to be checked against a subject and resource.
|
|
907
623
|
*/
|
|
@@ -917,7 +633,7 @@ class AbilityRule {
|
|
|
917
633
|
condition;
|
|
918
634
|
name;
|
|
919
635
|
id;
|
|
920
|
-
state =
|
|
636
|
+
state = AbilityMatch.pending;
|
|
921
637
|
/**
|
|
922
638
|
* Creates an instance of AbilityRule.
|
|
923
639
|
* @param {string} params.id - The unique identifier of the rule.
|
|
@@ -945,39 +661,39 @@ class AbilityRule {
|
|
|
945
661
|
const [subjectValue, resourceValue] = this.extractValues(resource, environment);
|
|
946
662
|
const isValue = (v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean' || v === null;
|
|
947
663
|
// equals
|
|
948
|
-
if (
|
|
664
|
+
if (AbilityCondition.equals.isEqual(this.condition)) {
|
|
949
665
|
is = subjectValue === resourceValue;
|
|
950
666
|
}
|
|
951
667
|
// not equals
|
|
952
|
-
if (
|
|
668
|
+
if (AbilityCondition.not_equals.isEqual(this.condition)) {
|
|
953
669
|
is = subjectValue !== resourceValue;
|
|
954
670
|
}
|
|
955
671
|
// less than
|
|
956
|
-
if (
|
|
672
|
+
if (AbilityCondition.less_than.isEqual(this.condition)) {
|
|
957
673
|
if (typeof subjectValue === 'number' && typeof resourceValue === 'number') {
|
|
958
674
|
is = subjectValue < resourceValue;
|
|
959
675
|
}
|
|
960
676
|
}
|
|
961
677
|
// less or equal
|
|
962
|
-
if (
|
|
678
|
+
if (AbilityCondition.less_or_equal.isEqual(this.condition)) {
|
|
963
679
|
if (typeof subjectValue === 'number' && typeof resourceValue === 'number') {
|
|
964
680
|
is = subjectValue <= resourceValue;
|
|
965
681
|
}
|
|
966
682
|
}
|
|
967
683
|
// more than
|
|
968
|
-
if (
|
|
684
|
+
if (AbilityCondition.greater_than.isEqual(this.condition)) {
|
|
969
685
|
if (typeof subjectValue === 'number' && typeof resourceValue === 'number') {
|
|
970
686
|
is = subjectValue > resourceValue;
|
|
971
687
|
}
|
|
972
688
|
}
|
|
973
689
|
// more or equal
|
|
974
|
-
if (
|
|
690
|
+
if (AbilityCondition.greater_or_equal.isEqual(this.condition)) {
|
|
975
691
|
if (typeof subjectValue === 'number' && typeof resourceValue === 'number') {
|
|
976
692
|
is = subjectValue >= resourceValue;
|
|
977
693
|
}
|
|
978
694
|
}
|
|
979
695
|
// in
|
|
980
|
-
if (
|
|
696
|
+
if (AbilityCondition.in.isEqual(this.condition)) {
|
|
981
697
|
// value in array
|
|
982
698
|
if (isValue(subjectValue) && Array.isArray(resourceValue)) {
|
|
983
699
|
is = resourceValue.includes(subjectValue);
|
|
@@ -988,7 +704,7 @@ class AbilityRule {
|
|
|
988
704
|
}
|
|
989
705
|
}
|
|
990
706
|
// not in
|
|
991
|
-
if (
|
|
707
|
+
if (AbilityCondition.not_in.isEqual(this.condition)) {
|
|
992
708
|
if (isValue(subjectValue) && Array.isArray(resourceValue)) {
|
|
993
709
|
is = !resourceValue.includes(subjectValue);
|
|
994
710
|
}
|
|
@@ -997,7 +713,7 @@ class AbilityRule {
|
|
|
997
713
|
}
|
|
998
714
|
}
|
|
999
715
|
// contains
|
|
1000
|
-
if (
|
|
716
|
+
if (AbilityCondition.contains.isEqual(this.condition)) {
|
|
1001
717
|
// array contains value
|
|
1002
718
|
if (Array.isArray(subjectValue) && isValue(resourceValue)) {
|
|
1003
719
|
is = subjectValue.includes(resourceValue);
|
|
@@ -1008,7 +724,7 @@ class AbilityRule {
|
|
|
1008
724
|
}
|
|
1009
725
|
}
|
|
1010
726
|
// not contains
|
|
1011
|
-
if (
|
|
727
|
+
if (AbilityCondition.not_contains.isEqual(this.condition)) {
|
|
1012
728
|
if (Array.isArray(subjectValue) && isValue(resourceValue)) {
|
|
1013
729
|
is = !subjectValue.includes(resourceValue);
|
|
1014
730
|
}
|
|
@@ -1017,7 +733,7 @@ class AbilityRule {
|
|
|
1017
733
|
}
|
|
1018
734
|
}
|
|
1019
735
|
// length equals
|
|
1020
|
-
if (
|
|
736
|
+
if (AbilityCondition.length_equals.isEqual(this.condition)) {
|
|
1021
737
|
// foo.bar == n
|
|
1022
738
|
if (isValue(subjectValue) && typeof resourceValue === 'number') {
|
|
1023
739
|
is = String(subjectValue).length === resourceValue;
|
|
@@ -1036,7 +752,7 @@ class AbilityRule {
|
|
|
1036
752
|
}
|
|
1037
753
|
}
|
|
1038
754
|
// length greater than
|
|
1039
|
-
if (
|
|
755
|
+
if (AbilityCondition.length_greater_than.isEqual(this.condition)) {
|
|
1040
756
|
// foo.bar > n
|
|
1041
757
|
if (isValue(subjectValue) && typeof resourceValue === 'number') {
|
|
1042
758
|
is = String(subjectValue).length > resourceValue;
|
|
@@ -1055,7 +771,7 @@ class AbilityRule {
|
|
|
1055
771
|
}
|
|
1056
772
|
}
|
|
1057
773
|
// length greater than
|
|
1058
|
-
if (
|
|
774
|
+
if (AbilityCondition.length_less_than.isEqual(this.condition)) {
|
|
1059
775
|
// foo.bar < n
|
|
1060
776
|
if (isValue(subjectValue) && typeof resourceValue === 'number') {
|
|
1061
777
|
is = String(subjectValue).length < resourceValue;
|
|
@@ -1073,7 +789,7 @@ class AbilityRule {
|
|
|
1073
789
|
is = subjectValue.length < resourceValue.length;
|
|
1074
790
|
}
|
|
1075
791
|
}
|
|
1076
|
-
this.state = is ?
|
|
792
|
+
this.state = is ? AbilityMatch.match : AbilityMatch.mismatch;
|
|
1077
793
|
return this.state;
|
|
1078
794
|
}
|
|
1079
795
|
/**
|
|
@@ -1149,107 +865,96 @@ class AbilityRule {
|
|
|
1149
865
|
toString() {
|
|
1150
866
|
return `AbilityRule: ${this.name} condition: ${this.condition.code} subject: "${this.subject?.toString()}" resource: "${this.resource?.toString()}"`;
|
|
1151
867
|
}
|
|
1152
|
-
|
|
1153
|
-
return
|
|
868
|
+
copyWith(props) {
|
|
869
|
+
return new AbilityRule({
|
|
870
|
+
id: props.id ?? this.id,
|
|
871
|
+
name: props.name ?? this.name,
|
|
872
|
+
subject: props.subject ?? this.subject,
|
|
873
|
+
resource: props.resource ?? this.resource,
|
|
874
|
+
condition: props.condition ?? this.condition,
|
|
875
|
+
});
|
|
1154
876
|
}
|
|
1155
877
|
static equals(subject, resource) {
|
|
1156
878
|
return new AbilityRule({
|
|
1157
|
-
condition:
|
|
879
|
+
condition: AbilityCondition.equals,
|
|
1158
880
|
subject,
|
|
1159
881
|
resource,
|
|
1160
882
|
});
|
|
1161
883
|
}
|
|
1162
884
|
static notEquals(subject, resource) {
|
|
1163
885
|
return new AbilityRule({
|
|
1164
|
-
condition:
|
|
886
|
+
condition: AbilityCondition.not_equals,
|
|
1165
887
|
subject,
|
|
1166
888
|
resource,
|
|
1167
889
|
});
|
|
1168
890
|
}
|
|
1169
891
|
static contains(subject, resource) {
|
|
1170
892
|
return new AbilityRule({
|
|
1171
|
-
condition:
|
|
893
|
+
condition: AbilityCondition.contains,
|
|
1172
894
|
subject,
|
|
1173
895
|
resource,
|
|
1174
896
|
});
|
|
1175
897
|
}
|
|
1176
898
|
static notContains(subject, resource) {
|
|
1177
899
|
return new AbilityRule({
|
|
1178
|
-
condition:
|
|
900
|
+
condition: AbilityCondition.not_contains,
|
|
1179
901
|
subject,
|
|
1180
902
|
resource,
|
|
1181
903
|
});
|
|
1182
904
|
}
|
|
1183
905
|
static notIn(subject, resource) {
|
|
1184
906
|
return new AbilityRule({
|
|
1185
|
-
condition:
|
|
907
|
+
condition: AbilityCondition.not_in,
|
|
1186
908
|
subject,
|
|
1187
909
|
resource,
|
|
1188
910
|
});
|
|
1189
911
|
}
|
|
1190
912
|
static in(subject, resource) {
|
|
1191
913
|
return new AbilityRule({
|
|
1192
|
-
condition:
|
|
914
|
+
condition: AbilityCondition.in,
|
|
1193
915
|
subject,
|
|
1194
916
|
resource,
|
|
1195
917
|
});
|
|
1196
918
|
}
|
|
1197
919
|
static notEqual(subject, resource) {
|
|
1198
920
|
return new AbilityRule({
|
|
1199
|
-
condition:
|
|
921
|
+
condition: AbilityCondition.not_equals,
|
|
1200
922
|
subject,
|
|
1201
923
|
resource,
|
|
1202
924
|
});
|
|
1203
925
|
}
|
|
1204
926
|
static lessThan(subject, resource) {
|
|
1205
927
|
return new AbilityRule({
|
|
1206
|
-
condition:
|
|
928
|
+
condition: AbilityCondition.less_than,
|
|
1207
929
|
subject,
|
|
1208
930
|
resource,
|
|
1209
931
|
});
|
|
1210
932
|
}
|
|
1211
933
|
static lessOrEqual(subject, resource) {
|
|
1212
934
|
return new AbilityRule({
|
|
1213
|
-
condition:
|
|
935
|
+
condition: AbilityCondition.less_or_equal,
|
|
1214
936
|
subject,
|
|
1215
937
|
resource,
|
|
1216
938
|
});
|
|
1217
939
|
}
|
|
1218
940
|
static moreThan(subject, resource) {
|
|
1219
941
|
return new AbilityRule({
|
|
1220
|
-
condition:
|
|
942
|
+
condition: AbilityCondition.greater_than,
|
|
1221
943
|
subject,
|
|
1222
944
|
resource,
|
|
1223
945
|
});
|
|
1224
946
|
}
|
|
1225
947
|
static moreOrEqual(subject, resource) {
|
|
1226
948
|
return new AbilityRule({
|
|
1227
|
-
condition:
|
|
949
|
+
condition: AbilityCondition.greater_or_equal,
|
|
1228
950
|
subject,
|
|
1229
951
|
resource,
|
|
1230
952
|
});
|
|
1231
953
|
}
|
|
1232
954
|
}
|
|
1233
|
-
exports.AbilityRule = AbilityRule;
|
|
1234
|
-
exports["default"] = AbilityRule;
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
/***/ }),
|
|
1238
955
|
|
|
1239
|
-
/***/ 56:
|
|
1240
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
1244
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
1245
|
-
};
|
|
1246
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1247
|
-
exports.AbilityRuleSet = void 0;
|
|
1248
|
-
const AbilityCompare_1 = __importDefault(__webpack_require__(413));
|
|
1249
|
-
const AbilityMatch_1 = __importDefault(__webpack_require__(247));
|
|
1250
|
-
const AbilityJSONParser_1 = __webpack_require__(909);
|
|
1251
956
|
class AbilityRuleSet {
|
|
1252
|
-
state =
|
|
957
|
+
state = AbilityMatch.pending;
|
|
1253
958
|
/**
|
|
1254
959
|
* List of rules
|
|
1255
960
|
*/
|
|
@@ -1260,7 +965,7 @@ class AbilityRuleSet {
|
|
|
1260
965
|
* rules will be returns «permit» status and for the «or» - if\
|
|
1261
966
|
* one of the rules returns as «permit»
|
|
1262
967
|
*/
|
|
1263
|
-
compareMethod =
|
|
968
|
+
compareMethod = AbilityCompare.and;
|
|
1264
969
|
/**
|
|
1265
970
|
* Group name
|
|
1266
971
|
*/
|
|
@@ -1284,7 +989,7 @@ class AbilityRuleSet {
|
|
|
1284
989
|
return this;
|
|
1285
990
|
}
|
|
1286
991
|
async check(resources, environment) {
|
|
1287
|
-
this.state =
|
|
992
|
+
this.state = AbilityMatch.mismatch;
|
|
1288
993
|
if (!this.rules.length) {
|
|
1289
994
|
return this.state;
|
|
1290
995
|
}
|
|
@@ -1292,22 +997,22 @@ class AbilityRuleSet {
|
|
|
1292
997
|
for (const rule of this.rules) {
|
|
1293
998
|
const state = await rule.check(resources, environment);
|
|
1294
999
|
ruleCheckStates.push(state);
|
|
1295
|
-
if (
|
|
1000
|
+
if (AbilityCompare.and.isEqual(this.compareMethod) && AbilityMatch.mismatch.isEqual(state)) {
|
|
1296
1001
|
return this.state; // mismatch
|
|
1297
1002
|
}
|
|
1298
|
-
if (
|
|
1299
|
-
this.state =
|
|
1003
|
+
if (AbilityCompare.or.isEqual(this.compareMethod) && AbilityMatch.match.isEqual(state)) {
|
|
1004
|
+
this.state = AbilityMatch.match;
|
|
1300
1005
|
return this.state;
|
|
1301
1006
|
}
|
|
1302
1007
|
}
|
|
1303
|
-
if (
|
|
1304
|
-
if (ruleCheckStates.every(s =>
|
|
1305
|
-
this.state =
|
|
1008
|
+
if (AbilityCompare.and.isEqual(this.compareMethod)) {
|
|
1009
|
+
if (ruleCheckStates.every(s => AbilityMatch.match.isEqual(s))) {
|
|
1010
|
+
this.state = AbilityMatch.match;
|
|
1306
1011
|
}
|
|
1307
1012
|
}
|
|
1308
|
-
if (
|
|
1309
|
-
if (ruleCheckStates.some(s =>
|
|
1310
|
-
this.state =
|
|
1013
|
+
if (AbilityCompare.or.isEqual(this.compareMethod)) {
|
|
1014
|
+
if (ruleCheckStates.some(s => AbilityMatch.match.isEqual(s))) {
|
|
1015
|
+
this.state = AbilityMatch.match;
|
|
1311
1016
|
}
|
|
1312
1017
|
}
|
|
1313
1018
|
return this.state;
|
|
@@ -1315,78 +1020,247 @@ class AbilityRuleSet {
|
|
|
1315
1020
|
toString() {
|
|
1316
1021
|
return `AbilityRuleSet: ${this.name} compareMethod: ${this.compareMethod.code}, rules: ${this.rules.map(rule => rule.toString()).join('\n')}`;
|
|
1317
1022
|
}
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1023
|
+
copyWith(props) {
|
|
1024
|
+
const next = new AbilityRuleSet({
|
|
1025
|
+
id: props.id ?? this.id,
|
|
1026
|
+
name: props.name ?? this.name,
|
|
1027
|
+
compareMethod: props.compareMethod ?? this.compareMethod,
|
|
1028
|
+
});
|
|
1029
|
+
const nextRules = props.rules ?? this.rules;
|
|
1030
|
+
for (const rule of nextRules) {
|
|
1031
|
+
next.addRule(rule);
|
|
1032
|
+
}
|
|
1033
|
+
return next;
|
|
1323
1034
|
}
|
|
1324
1035
|
static and(rules) {
|
|
1325
1036
|
return new AbilityRuleSet({
|
|
1326
|
-
compareMethod:
|
|
1037
|
+
compareMethod: AbilityCompare.and,
|
|
1327
1038
|
}).addRules(rules);
|
|
1328
1039
|
}
|
|
1329
1040
|
static or(rules) {
|
|
1330
1041
|
return new AbilityRuleSet({
|
|
1331
|
-
compareMethod:
|
|
1042
|
+
compareMethod: AbilityCompare.or,
|
|
1332
1043
|
}).addRules(rules);
|
|
1333
1044
|
}
|
|
1334
1045
|
}
|
|
1335
|
-
exports.AbilityRuleSet = AbilityRuleSet;
|
|
1336
|
-
exports["default"] = AbilityRuleSet;
|
|
1337
|
-
|
|
1338
1046
|
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
}
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
})
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1047
|
+
class AbilityInMemoryCache {
|
|
1048
|
+
store = new Map();
|
|
1049
|
+
async get(key) {
|
|
1050
|
+
const entry = this.store.get(key);
|
|
1051
|
+
if (!entry)
|
|
1052
|
+
return undefined;
|
|
1053
|
+
if (Date.now() > entry.expires) {
|
|
1054
|
+
this.store.delete(key);
|
|
1055
|
+
return undefined;
|
|
1056
|
+
}
|
|
1057
|
+
return entry.value;
|
|
1058
|
+
}
|
|
1059
|
+
async set(key, value, ttlSeconds = 60) {
|
|
1060
|
+
this.store.set(key, {
|
|
1061
|
+
value,
|
|
1062
|
+
expires: Date.now() + ttlSeconds * 1000,
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
serialize(input) {
|
|
1066
|
+
return this.fastHash(this.stableStringify(input));
|
|
1067
|
+
}
|
|
1068
|
+
async delete(key) {
|
|
1069
|
+
this.store.delete(key);
|
|
1070
|
+
}
|
|
1071
|
+
async clear() {
|
|
1072
|
+
this.store.clear();
|
|
1073
|
+
}
|
|
1074
|
+
async deleteByPrefix(prefix) {
|
|
1075
|
+
for (const key of this.store.keys()) {
|
|
1076
|
+
if (key.startsWith(prefix)) {
|
|
1077
|
+
this.store.delete(key);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
fastHash(str) {
|
|
1082
|
+
let hash = 5381;
|
|
1083
|
+
for (let i = 0; i < str.length; i++) {
|
|
1084
|
+
hash = (hash * 33) ^ str.charCodeAt(i);
|
|
1085
|
+
}
|
|
1086
|
+
return (hash >>> 0).toString(36);
|
|
1087
|
+
}
|
|
1088
|
+
stableStringify(obj) {
|
|
1089
|
+
if (obj === null)
|
|
1090
|
+
return 'null';
|
|
1091
|
+
const type = typeof obj;
|
|
1092
|
+
if (type === 'string')
|
|
1093
|
+
return JSON.stringify(obj);
|
|
1094
|
+
if (type === 'number' || type === 'boolean')
|
|
1095
|
+
return String(obj);
|
|
1096
|
+
if (type === 'undefined')
|
|
1097
|
+
return 'undefined';
|
|
1098
|
+
if (Array.isArray(obj)) {
|
|
1099
|
+
let out = '[';
|
|
1100
|
+
for (let i = 0; i < obj.length; i++) {
|
|
1101
|
+
if (i > 0)
|
|
1102
|
+
out += ',';
|
|
1103
|
+
out += this.stableStringify(obj[i]);
|
|
1104
|
+
}
|
|
1105
|
+
return out + ']';
|
|
1106
|
+
}
|
|
1107
|
+
const keys = Object.keys(obj);
|
|
1108
|
+
keys.sort();
|
|
1109
|
+
let out = '{';
|
|
1110
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1111
|
+
const k = keys[i];
|
|
1112
|
+
if (i > 0)
|
|
1113
|
+
out += ',';
|
|
1114
|
+
out += k + ':' + this.stableStringify(obj[k]);
|
|
1115
|
+
}
|
|
1116
|
+
return out + '}';
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1382
1119
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1120
|
+
class AbilityJSONParser {
|
|
1121
|
+
/**
|
|
1122
|
+
* Parses an array of policy configurations into an array of AbilityPolicy instances.
|
|
1123
|
+
* @param configs - Array of policy configurations
|
|
1124
|
+
* @returns Array of AbilityPolicy instances
|
|
1125
|
+
*/
|
|
1126
|
+
static parse(configs) {
|
|
1127
|
+
return configs.map(config => AbilityJSONParser.parsePolicy(config));
|
|
1128
|
+
}
|
|
1129
|
+
static parsePolicy(config) {
|
|
1130
|
+
const { id, name, ruleSet, compareMethod, permission, effect } = config;
|
|
1131
|
+
// Create the empty policy
|
|
1132
|
+
const policy = new AbilityPolicy({
|
|
1133
|
+
name,
|
|
1134
|
+
id,
|
|
1135
|
+
permission: permission,
|
|
1136
|
+
effect: new AbilityPolicyEffect(effect),
|
|
1137
|
+
});
|
|
1138
|
+
policy.compareMethod = new AbilityCompare(compareMethod);
|
|
1139
|
+
ruleSet.forEach(ruleSetConfig => {
|
|
1140
|
+
policy.addRuleSet(AbilityJSONParser.parseRuleSet(ruleSetConfig));
|
|
1141
|
+
});
|
|
1142
|
+
return policy;
|
|
1143
|
+
}
|
|
1144
|
+
static parseRule(config) {
|
|
1145
|
+
const { id, name, subject, resource, condition } = config;
|
|
1146
|
+
return new AbilityRule({
|
|
1147
|
+
id,
|
|
1148
|
+
name,
|
|
1149
|
+
subject,
|
|
1150
|
+
resource,
|
|
1151
|
+
condition: new AbilityCondition(condition),
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Parse the config JSON format to Group class instance
|
|
1156
|
+
*/
|
|
1157
|
+
static parseRuleSet(config) {
|
|
1158
|
+
const { id, name, rules, compareMethod } = config;
|
|
1159
|
+
const ruleSet = new AbilityRuleSet({
|
|
1160
|
+
compareMethod: new AbilityCompare(compareMethod),
|
|
1161
|
+
name,
|
|
1162
|
+
id,
|
|
1163
|
+
});
|
|
1164
|
+
// Adding rules if exists
|
|
1165
|
+
if (rules && rules.length > 0) {
|
|
1166
|
+
const abilityRules = rules.map(ruleConfig => AbilityJSONParser.parseRule(ruleConfig));
|
|
1167
|
+
ruleSet.addRules(abilityRules);
|
|
1168
|
+
}
|
|
1169
|
+
return ruleSet;
|
|
1170
|
+
}
|
|
1171
|
+
static ruleToJSON(rule) {
|
|
1172
|
+
return {
|
|
1173
|
+
id: rule.id,
|
|
1174
|
+
name: rule.name,
|
|
1175
|
+
subject: rule.subject,
|
|
1176
|
+
resource: rule.resource,
|
|
1177
|
+
condition: rule.condition.code,
|
|
1178
|
+
};
|
|
1179
|
+
}
|
|
1180
|
+
static ruleSetToJSON(ruleSet) {
|
|
1181
|
+
return {
|
|
1182
|
+
id: ruleSet.id.toString(),
|
|
1183
|
+
name: ruleSet.name.toString(),
|
|
1184
|
+
compareMethod: ruleSet.compareMethod.code.toString(),
|
|
1185
|
+
rules: ruleSet.rules.map(rule => AbilityJSONParser.ruleToJSON(rule)),
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
static policyToJSON(policy) {
|
|
1189
|
+
return {
|
|
1190
|
+
id: policy.id.toString(),
|
|
1191
|
+
name: policy.name.toString(),
|
|
1192
|
+
compareMethod: policy.compareMethod.code.toString(),
|
|
1193
|
+
ruleSet: policy.ruleSet.map(ruleSet => AbilityJSONParser.ruleSetToJSON(ruleSet)),
|
|
1194
|
+
permission: policy.permission,
|
|
1195
|
+
effect: policy.effect.code,
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
static toJSON(policies) {
|
|
1199
|
+
return policies.map(policy => AbilityJSONParser.policyToJSON(policy));
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1385
1202
|
|
|
1203
|
+
/**
|
|
1204
|
+
* Represents a single token produced by the Ability DSL lexer.
|
|
1205
|
+
* Each token carries a type (e.g., EFFECT, IDENTIFIER, STRING) and its raw string value.
|
|
1206
|
+
*/
|
|
1207
|
+
class AbilityDSLToken extends AbilityCode {
|
|
1208
|
+
/** The literal text of the token as it appeared in the input (e.g., "permit", "user.roles", "admin"). */
|
|
1209
|
+
value = '';
|
|
1210
|
+
/** The line number in DSL */
|
|
1211
|
+
line = 1;
|
|
1212
|
+
/** The column in dsl */
|
|
1213
|
+
column = 1;
|
|
1214
|
+
constructor(type, value, line, column) {
|
|
1215
|
+
super(type);
|
|
1216
|
+
this.value = value;
|
|
1217
|
+
this.line = line;
|
|
1218
|
+
this.column = column;
|
|
1219
|
+
}
|
|
1220
|
+
/**
|
|
1221
|
+
* Returns a human-readable representation of the token, useful for debugging.
|
|
1222
|
+
* Example output: "AbilityDSLToken([EFFECT] permit"
|
|
1223
|
+
*/
|
|
1224
|
+
toString() {
|
|
1225
|
+
return `AbilityDSLToken([${this.code}] "${this.value}" at ${this.line}:${this.column})`;
|
|
1226
|
+
}
|
|
1227
|
+
static EFFECT = 'EFFECT';
|
|
1228
|
+
static IF = 'IF';
|
|
1229
|
+
static PERMISSION = 'PERMISSION';
|
|
1230
|
+
static IDENTIFIER = 'IDENTIFIER';
|
|
1231
|
+
static COLON = 'COLON';
|
|
1232
|
+
static COMMA = 'COMMA';
|
|
1233
|
+
static DOT = 'DOT';
|
|
1234
|
+
static LBRACKET = 'LBRACKET';
|
|
1235
|
+
static RBRACKET = 'RBRACKET';
|
|
1236
|
+
static ALL = 'ALL';
|
|
1237
|
+
static ANY = 'ANY';
|
|
1238
|
+
static OF = 'OF';
|
|
1239
|
+
static EOF = 'EOF';
|
|
1240
|
+
static COMMENT = 'COMMENT';
|
|
1241
|
+
static EQ = 'EQ';
|
|
1242
|
+
static CONTAINS = 'CONTAINS';
|
|
1243
|
+
static IN = 'IN';
|
|
1244
|
+
static NOT_IN = 'NOT_IN';
|
|
1245
|
+
static NOT_CONTAINS = 'NOT_CONTAINS';
|
|
1246
|
+
static GT = 'GT';
|
|
1247
|
+
static GTE = 'GTE';
|
|
1248
|
+
static LT = 'LT';
|
|
1249
|
+
static LTE = 'LTE';
|
|
1250
|
+
static NULL = 'NULL';
|
|
1251
|
+
static EQ_NULL = 'EQ_NULL';
|
|
1252
|
+
static NOT_EQ_NULL = 'NOT_EQ_NULL';
|
|
1253
|
+
static LEN_GT = 'LEN_GT';
|
|
1254
|
+
static LEN_LT = 'LEN_LT';
|
|
1255
|
+
static LEN_EQ = 'LEN_EQ';
|
|
1256
|
+
static NOT_EQ = 'NOT_EQ';
|
|
1257
|
+
static STRING = 'STRING';
|
|
1258
|
+
static NUMBER = 'NUMBER';
|
|
1259
|
+
static BOOLEAN = 'BOOLEAN';
|
|
1260
|
+
static SYMBOL = 'SYMBOL';
|
|
1261
|
+
static KEYWORD = 'KEYWORD';
|
|
1262
|
+
}
|
|
1386
1263
|
|
|
1387
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1388
|
-
exports.AbilityDSLLexer = void 0;
|
|
1389
|
-
const AbilityDSLToken_1 = __webpack_require__(325);
|
|
1390
1264
|
class AbilityDSLLexer {
|
|
1391
1265
|
input;
|
|
1392
1266
|
pos = 0;
|
|
@@ -1454,7 +1328,7 @@ class AbilityDSLLexer {
|
|
|
1454
1328
|
}
|
|
1455
1329
|
throw new Error(`Unexpected character '${char}' at ${this.line}:${this.column}`);
|
|
1456
1330
|
}
|
|
1457
|
-
this.tokens.push(new
|
|
1331
|
+
this.tokens.push(new AbilityDSLToken(AbilityDSLToken.EOF, '', this.line, this.column));
|
|
1458
1332
|
return this.tokens;
|
|
1459
1333
|
}
|
|
1460
1334
|
readComment() {
|
|
@@ -1465,7 +1339,7 @@ class AbilityDSLLexer {
|
|
|
1465
1339
|
while (!this.isAtEnd() && !this.isNewline()) {
|
|
1466
1340
|
value += this.advance();
|
|
1467
1341
|
}
|
|
1468
|
-
return new
|
|
1342
|
+
return new AbilityDSLToken(AbilityDSLToken.COMMENT, value.trim(), startLine, startColumn);
|
|
1469
1343
|
}
|
|
1470
1344
|
readString() {
|
|
1471
1345
|
const startLine = this.line;
|
|
@@ -1485,7 +1359,7 @@ class AbilityDSLLexer {
|
|
|
1485
1359
|
continue;
|
|
1486
1360
|
}
|
|
1487
1361
|
if (char === quote) {
|
|
1488
|
-
return new
|
|
1362
|
+
return new AbilityDSLToken(AbilityDSLToken.STRING, value, startLine, startColumn);
|
|
1489
1363
|
}
|
|
1490
1364
|
value += char;
|
|
1491
1365
|
}
|
|
@@ -1499,7 +1373,7 @@ class AbilityDSLLexer {
|
|
|
1499
1373
|
this.advance();
|
|
1500
1374
|
}
|
|
1501
1375
|
const value = this.input.slice(start, this.pos);
|
|
1502
|
-
return new
|
|
1376
|
+
return new AbilityDSLToken(AbilityDSLToken.NUMBER, value, startLine, startColumn);
|
|
1503
1377
|
}
|
|
1504
1378
|
readSymbol() {
|
|
1505
1379
|
const startLine = this.line;
|
|
@@ -1507,41 +1381,41 @@ class AbilityDSLLexer {
|
|
|
1507
1381
|
const char = this.advance();
|
|
1508
1382
|
switch (char) {
|
|
1509
1383
|
case '.':
|
|
1510
|
-
return new
|
|
1384
|
+
return new AbilityDSLToken(AbilityDSLToken.DOT, char, startLine, startColumn);
|
|
1511
1385
|
case ':':
|
|
1512
|
-
return new
|
|
1386
|
+
return new AbilityDSLToken(AbilityDSLToken.COLON, char, startLine, startColumn);
|
|
1513
1387
|
case ',':
|
|
1514
|
-
return new
|
|
1388
|
+
return new AbilityDSLToken(AbilityDSLToken.COMMA, char, startLine, startColumn);
|
|
1515
1389
|
case '[':
|
|
1516
|
-
return new
|
|
1390
|
+
return new AbilityDSLToken(AbilityDSLToken.LBRACKET, char, startLine, startColumn);
|
|
1517
1391
|
case ']':
|
|
1518
|
-
return new
|
|
1392
|
+
return new AbilityDSLToken(AbilityDSLToken.RBRACKET, char, startLine, startColumn);
|
|
1519
1393
|
case '>':
|
|
1520
1394
|
if (this.peek() === '=') {
|
|
1521
1395
|
this.advance();
|
|
1522
|
-
return new
|
|
1396
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '>=', startLine, startColumn);
|
|
1523
1397
|
}
|
|
1524
|
-
return new
|
|
1398
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '>', startLine, startColumn);
|
|
1525
1399
|
case '<':
|
|
1526
1400
|
if (this.peek() === '=') {
|
|
1527
1401
|
this.advance();
|
|
1528
|
-
return new
|
|
1402
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '<=', startLine, startColumn);
|
|
1529
1403
|
}
|
|
1530
1404
|
if (this.peek() === '>') {
|
|
1531
1405
|
this.advance();
|
|
1532
|
-
return new
|
|
1406
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '<>', startLine, startColumn);
|
|
1533
1407
|
}
|
|
1534
|
-
return new
|
|
1408
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '<', startLine, startColumn);
|
|
1535
1409
|
case '=':
|
|
1536
1410
|
if (this.peek() === '=') {
|
|
1537
1411
|
this.advance();
|
|
1538
|
-
return new
|
|
1412
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '==', startLine, startColumn);
|
|
1539
1413
|
}
|
|
1540
|
-
return new
|
|
1414
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '=', startLine, startColumn);
|
|
1541
1415
|
case '!':
|
|
1542
1416
|
if (this.peek() === '=') {
|
|
1543
1417
|
this.advance();
|
|
1544
|
-
return new
|
|
1418
|
+
return new AbilityDSLToken(AbilityDSLToken.SYMBOL, '!=', startLine, startColumn);
|
|
1545
1419
|
}
|
|
1546
1420
|
throw new Error(`Unexpected symbol '!' at ${this.line}:${this.column}`);
|
|
1547
1421
|
default:
|
|
@@ -1570,52 +1444,52 @@ class AbilityDSLLexer {
|
|
|
1570
1444
|
// Если есть точка — это путь (identifier или permission)
|
|
1571
1445
|
if (word.includes('.')) {
|
|
1572
1446
|
const last = this.tokens[this.tokens.length - 1];
|
|
1573
|
-
if (last?.code ===
|
|
1447
|
+
if (last?.code === AbilityDSLToken.EFFECT) {
|
|
1574
1448
|
if (word.startsWith('permission.')) {
|
|
1575
|
-
return new
|
|
1449
|
+
return new AbilityDSLToken(AbilityDSLToken.PERMISSION, word, startLine, startColumn);
|
|
1576
1450
|
}
|
|
1577
1451
|
}
|
|
1578
|
-
return new
|
|
1452
|
+
return new AbilityDSLToken(AbilityDSLToken.IDENTIFIER, word, startLine, startColumn);
|
|
1579
1453
|
}
|
|
1580
1454
|
// Ключевые слова
|
|
1581
1455
|
if (this.keywords.has(word)) {
|
|
1582
1456
|
// Эффекты
|
|
1583
1457
|
if (word === 'permit' || word === 'allow') {
|
|
1584
|
-
return new
|
|
1458
|
+
return new AbilityDSLToken(AbilityDSLToken.EFFECT, 'permit', startLine, startColumn);
|
|
1585
1459
|
}
|
|
1586
1460
|
if (word === 'deny' || word === 'forbidden') {
|
|
1587
|
-
return new
|
|
1461
|
+
return new AbilityDSLToken(AbilityDSLToken.EFFECT, 'deny', startLine, startColumn);
|
|
1588
1462
|
}
|
|
1589
1463
|
// Групповые ключевые слова
|
|
1590
1464
|
if (word === 'all') {
|
|
1591
|
-
return new
|
|
1465
|
+
return new AbilityDSLToken(AbilityDSLToken.ALL, word, startLine, startColumn);
|
|
1592
1466
|
}
|
|
1593
1467
|
if (word === 'any') {
|
|
1594
|
-
return new
|
|
1468
|
+
return new AbilityDSLToken(AbilityDSLToken.ANY, word, startLine, startColumn);
|
|
1595
1469
|
}
|
|
1596
1470
|
if (word === 'of') {
|
|
1597
|
-
return new
|
|
1471
|
+
return new AbilityDSLToken(AbilityDSLToken.OF, word, startLine, startColumn);
|
|
1598
1472
|
}
|
|
1599
1473
|
if (word === 'if') {
|
|
1600
|
-
return new
|
|
1474
|
+
return new AbilityDSLToken(AbilityDSLToken.IF, word, startLine, startColumn);
|
|
1601
1475
|
}
|
|
1602
1476
|
// Булевы и null
|
|
1603
1477
|
if (word === 'true' || word === 'false') {
|
|
1604
|
-
return new
|
|
1478
|
+
return new AbilityDSLToken(AbilityDSLToken.BOOLEAN, word, startLine, startColumn);
|
|
1605
1479
|
}
|
|
1606
1480
|
if (word === 'null') {
|
|
1607
|
-
return new
|
|
1481
|
+
return new AbilityDSLToken(AbilityDSLToken.NULL, word, startLine, startColumn);
|
|
1608
1482
|
}
|
|
1609
1483
|
// Остальные ключевые слова (contains, in, equals, greater, less, not, is, or, than, equal)
|
|
1610
|
-
return new
|
|
1484
|
+
return new AbilityDSLToken(AbilityDSLToken.KEYWORD, word, startLine, startColumn);
|
|
1611
1485
|
}
|
|
1612
1486
|
// Если после EFFECT и нет точки — действие (например, "create")
|
|
1613
1487
|
const lastToken = this.tokens[this.tokens.length - 1];
|
|
1614
|
-
if (lastToken?.code ===
|
|
1615
|
-
return new
|
|
1488
|
+
if (lastToken?.code === AbilityDSLToken.EFFECT) {
|
|
1489
|
+
return new AbilityDSLToken(AbilityDSLToken.PERMISSION, word, startLine, startColumn);
|
|
1616
1490
|
}
|
|
1617
1491
|
// Обычный идентификатор
|
|
1618
|
-
return new
|
|
1492
|
+
return new AbilityDSLToken(AbilityDSLToken.IDENTIFIER, word, startLine, startColumn);
|
|
1619
1493
|
}
|
|
1620
1494
|
skipWhitespace() {
|
|
1621
1495
|
while (!this.isAtEnd() && /\s/.test(this.peek())) {
|
|
@@ -1648,33 +1522,73 @@ class AbilityDSLLexer {
|
|
|
1648
1522
|
}
|
|
1649
1523
|
return ch;
|
|
1650
1524
|
}
|
|
1651
|
-
isAtEnd() {
|
|
1652
|
-
return this.pos >= this.input.length;
|
|
1525
|
+
isAtEnd() {
|
|
1526
|
+
return this.pos >= this.input.length;
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
class AbilityDSLSyntaxError extends Error {
|
|
1531
|
+
line;
|
|
1532
|
+
column;
|
|
1533
|
+
context;
|
|
1534
|
+
details;
|
|
1535
|
+
_formattedMessage;
|
|
1536
|
+
_originalStack;
|
|
1537
|
+
constructor(line, column, context, // строка DSL + ^ + соседние строки
|
|
1538
|
+
details) {
|
|
1539
|
+
super(details.split('\n')[0]); // message = только первая строка
|
|
1540
|
+
this.line = line;
|
|
1541
|
+
this.column = column;
|
|
1542
|
+
this.context = context;
|
|
1543
|
+
this.details = details;
|
|
1544
|
+
this.name = 'AbilityDSLSyntaxError';
|
|
1545
|
+
if (Error.captureStackTrace) {
|
|
1546
|
+
Error.captureStackTrace(this, AbilityDSLSyntaxError);
|
|
1547
|
+
}
|
|
1548
|
+
this._originalStack = this.stack;
|
|
1549
|
+
this._formattedMessage = this.formatMessage();
|
|
1550
|
+
Object.defineProperty(this, 'stack', {
|
|
1551
|
+
get: () => this._formattedMessage,
|
|
1552
|
+
configurable: true,
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
static supportsColor() {
|
|
1556
|
+
return typeof process !== 'undefined' && process.stdout?.isTTY;
|
|
1557
|
+
}
|
|
1558
|
+
formatMessage() {
|
|
1559
|
+
const useColor = AbilityDSLSyntaxError.supportsColor();
|
|
1560
|
+
const BOLD = useColor ? '\x1b[1m' : '';
|
|
1561
|
+
const RED = useColor ? '\x1b[31m' : '';
|
|
1562
|
+
const ORANGE = useColor ? '\x1b[33;1m' : '';
|
|
1563
|
+
const GRAY = useColor ? '\x1b[90m' : '';
|
|
1564
|
+
const RESET = useColor ? '\x1b[0m' : '';
|
|
1565
|
+
const lines = this.context.split('\n');
|
|
1566
|
+
// Find line with ^
|
|
1567
|
+
const pointerIndex = lines.findIndex(l => l.includes('^') || l.includes('~'));
|
|
1568
|
+
const commentIndex = lines.findIndex(l => l.trim().includes('#'));
|
|
1569
|
+
const formattedLines = lines.map((line, idx) => {
|
|
1570
|
+
if (idx === pointerIndex - 1) {
|
|
1571
|
+
// Error line
|
|
1572
|
+
return `${BOLD}${ORANGE}${line}${RESET}`;
|
|
1573
|
+
}
|
|
1574
|
+
if (idx === pointerIndex) {
|
|
1575
|
+
// Error with ~~~~~
|
|
1576
|
+
return `${RED}${line}${RESET}`;
|
|
1577
|
+
}
|
|
1578
|
+
// Comments # ...
|
|
1579
|
+
if (idx === commentIndex) {
|
|
1580
|
+
return `${GRAY}${line}${RESET}`;
|
|
1581
|
+
}
|
|
1582
|
+
return line;
|
|
1583
|
+
});
|
|
1584
|
+
const contextBlock = formattedLines.join('\n');
|
|
1585
|
+
return `${BOLD}${RED}${this.name}: ${this.details}${RESET}\n\n` + contextBlock;
|
|
1586
|
+
}
|
|
1587
|
+
toString() {
|
|
1588
|
+
return this._formattedMessage;
|
|
1653
1589
|
}
|
|
1654
1590
|
}
|
|
1655
|
-
exports.AbilityDSLLexer = AbilityDSLLexer;
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
/***/ }),
|
|
1659
1591
|
|
|
1660
|
-
/***/ 577:
|
|
1661
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
1665
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
1666
|
-
};
|
|
1667
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
1668
|
-
exports.AbilityDSLParser = void 0;
|
|
1669
|
-
const AbilityCompare_1 = __importDefault(__webpack_require__(413));
|
|
1670
|
-
const AbilityCondition_1 = __importDefault(__webpack_require__(167));
|
|
1671
|
-
const AbilityPolicy_1 = __importDefault(__webpack_require__(278));
|
|
1672
|
-
const AbilityPolicyEffect_1 = __importDefault(__webpack_require__(179));
|
|
1673
|
-
const AbilityRule_1 = __importDefault(__webpack_require__(306));
|
|
1674
|
-
const AbilityRuleSet_1 = __importDefault(__webpack_require__(56));
|
|
1675
|
-
const AbilityDSLLexer_1 = __webpack_require__(10);
|
|
1676
|
-
const AbilityDSLToken_1 = __webpack_require__(325);
|
|
1677
|
-
const AbilityDSLSyntaxError_1 = __webpack_require__(883);
|
|
1678
1592
|
/**
|
|
1679
1593
|
* Parser for the Ability DSL.
|
|
1680
1594
|
*
|
|
@@ -1708,7 +1622,7 @@ class AbilityDSLParser {
|
|
|
1708
1622
|
*/
|
|
1709
1623
|
parse() {
|
|
1710
1624
|
// Tokenize the entire DSL string.
|
|
1711
|
-
this.tokens = new
|
|
1625
|
+
this.tokens = new AbilityDSLLexer(this.dsl).tokenize();
|
|
1712
1626
|
this.pos = 0;
|
|
1713
1627
|
const policies = [];
|
|
1714
1628
|
// Keep parsing until we've consumed all tokens.
|
|
@@ -1736,29 +1650,29 @@ class AbilityDSLParser {
|
|
|
1736
1650
|
this.consumeLeadingComments();
|
|
1737
1651
|
const meta = this.takeAnnotations();
|
|
1738
1652
|
// Effect: "permit" or "deny"
|
|
1739
|
-
const effectToken = this.consume(
|
|
1653
|
+
const effectToken = this.consume(AbilityDSLToken.EFFECT, 'Expected effect');
|
|
1740
1654
|
const effect = effectToken.value;
|
|
1741
1655
|
// Permission: e.g. "order.update"
|
|
1742
|
-
const permissionToken = this.consume(
|
|
1656
|
+
const permissionToken = this.consume(AbilityDSLToken.PERMISSION, 'Expected permission');
|
|
1743
1657
|
const permission = permissionToken.value;
|
|
1744
1658
|
if (!permission.startsWith('permission.')) {
|
|
1745
1659
|
return this.syntaxError(`Unexpected token. The permission key, must be starts with prefix \`permission.\`, but got \`${permission}\`.\nDid you mean \`permission.${permission}\`?`, permissionToken);
|
|
1746
1660
|
}
|
|
1747
1661
|
// "if" keyword
|
|
1748
|
-
this.consume(
|
|
1662
|
+
this.consume(AbilityDSLToken.IF, 'Expected "if"');
|
|
1749
1663
|
// Group selector: "all" or "any" – determines how the top‑level rule sets are combined.
|
|
1750
|
-
const compareToken = this.consumeOneOf([
|
|
1751
|
-
const compareMethod = compareToken.code ===
|
|
1664
|
+
const compareToken = this.consumeOneOf([AbilityDSLToken.ALL, AbilityDSLToken.ANY], 'Expected "all" or "any"');
|
|
1665
|
+
const compareMethod = compareToken.code === AbilityDSLToken.ALL ? AbilityCompare.and : AbilityCompare.or;
|
|
1752
1666
|
// Colon after the group keyword
|
|
1753
|
-
this.consume(
|
|
1667
|
+
this.consume(AbilityDSLToken.COLON, 'Expected ":"');
|
|
1754
1668
|
// Parse the list of rule sets (each "all of:" or "any of:" block)
|
|
1755
1669
|
const ruleSets = this.parseRuleSets(compareMethod);
|
|
1756
1670
|
// Construct the policy instance.
|
|
1757
|
-
return new
|
|
1671
|
+
return new AbilityPolicy({
|
|
1758
1672
|
id: `${effect}:${permission}:${Math.random()}`,
|
|
1759
1673
|
name: meta.name ?? `${effect} ${permission}`,
|
|
1760
1674
|
permission: permission.replace(/^permission\./, ''),
|
|
1761
|
-
effect: effect === 'permit' ?
|
|
1675
|
+
effect: effect === 'permit' ? AbilityPolicyEffect.permit : AbilityPolicyEffect.deny,
|
|
1762
1676
|
compareMethod,
|
|
1763
1677
|
}).addRuleSets(ruleSets);
|
|
1764
1678
|
}
|
|
@@ -1779,7 +1693,7 @@ class AbilityDSLParser {
|
|
|
1779
1693
|
}
|
|
1780
1694
|
// Иначе — implicit group (all-of по умолчанию)
|
|
1781
1695
|
const meta = this.takeAnnotations();
|
|
1782
|
-
const group = new
|
|
1696
|
+
const group = new AbilityRuleSet({
|
|
1783
1697
|
compareMethod: policyCompareMethod,
|
|
1784
1698
|
name: meta.name,
|
|
1785
1699
|
});
|
|
@@ -1789,7 +1703,7 @@ class AbilityDSLParser {
|
|
|
1789
1703
|
if (this.isStartOfGroup() || this.isStartOfPolicy()) {
|
|
1790
1704
|
break;
|
|
1791
1705
|
}
|
|
1792
|
-
if (this.check(
|
|
1706
|
+
if (this.check(AbilityDSLToken.IDENTIFIER)) {
|
|
1793
1707
|
group.addRule(this.parseRule());
|
|
1794
1708
|
}
|
|
1795
1709
|
else {
|
|
@@ -1806,19 +1720,19 @@ class AbilityDSLParser {
|
|
|
1806
1720
|
parseGroup() {
|
|
1807
1721
|
this.consumeLeadingComments();
|
|
1808
1722
|
const meta = this.takeAnnotations();
|
|
1809
|
-
const compareToken = this.consumeOneOf([
|
|
1810
|
-
const compareMethod = compareToken.code ===
|
|
1811
|
-
if (this.check(
|
|
1723
|
+
const compareToken = this.consumeOneOf([AbilityDSLToken.ALL, AbilityDSLToken.ANY], 'Expected "all" or "any"');
|
|
1724
|
+
const compareMethod = compareToken.code === AbilityDSLToken.ALL ? AbilityCompare.and : AbilityCompare.or;
|
|
1725
|
+
if (this.check(AbilityDSLToken.OF)) {
|
|
1812
1726
|
this.advance();
|
|
1813
1727
|
}
|
|
1814
|
-
this.consume(
|
|
1815
|
-
const group = new
|
|
1728
|
+
this.consume(AbilityDSLToken.COLON, 'Expected ":"');
|
|
1729
|
+
const group = new AbilityRuleSet({ compareMethod, name: meta.name });
|
|
1816
1730
|
while (!this.isAtEnd()) {
|
|
1817
1731
|
this.consumeLeadingComments();
|
|
1818
1732
|
if (this.isStartOfGroup() || this.isStartOfPolicy()) {
|
|
1819
1733
|
break;
|
|
1820
1734
|
}
|
|
1821
|
-
if (this.check(
|
|
1735
|
+
if (this.check(AbilityDSLToken.IDENTIFIER)) {
|
|
1822
1736
|
group.addRule(this.parseRule());
|
|
1823
1737
|
}
|
|
1824
1738
|
else {
|
|
@@ -1836,19 +1750,19 @@ class AbilityDSLParser {
|
|
|
1836
1750
|
parseRule() {
|
|
1837
1751
|
this.consumeLeadingComments();
|
|
1838
1752
|
const meta = this.takeAnnotations();
|
|
1839
|
-
if (!this.check(
|
|
1753
|
+
if (!this.check(AbilityDSLToken.IDENTIFIER)) {
|
|
1840
1754
|
this.syntaxError(`Expected identifier, got ${this.peek().code}`, this.peek());
|
|
1841
1755
|
}
|
|
1842
1756
|
// Subject (e.g., "user.roles")
|
|
1843
|
-
const subject = this.consume(
|
|
1757
|
+
const subject = this.consume(AbilityDSLToken.IDENTIFIER, 'Expected field').value;
|
|
1844
1758
|
// Operator (e.g., "contains", "equals", "is not null")
|
|
1845
1759
|
const { condition, operator } = this.parseConditionOperator();
|
|
1846
1760
|
let resource;
|
|
1847
1761
|
let beforePos = this.pos;
|
|
1848
1762
|
// Special operators that don't consume a value token.
|
|
1849
|
-
if (operator ===
|
|
1850
|
-
operator ===
|
|
1851
|
-
operator ===
|
|
1763
|
+
if (operator === AbilityDSLToken.EQ_NULL ||
|
|
1764
|
+
operator === AbilityDSLToken.NOT_EQ_NULL ||
|
|
1765
|
+
operator === AbilityDSLToken.NULL) {
|
|
1852
1766
|
resource = null;
|
|
1853
1767
|
}
|
|
1854
1768
|
else {
|
|
@@ -1860,11 +1774,11 @@ class AbilityDSLParser {
|
|
|
1860
1774
|
this.consumeLeadingComments();
|
|
1861
1775
|
const resourceToken = this.tokens[beforePos];
|
|
1862
1776
|
if (typeof resource === 'string' &&
|
|
1863
|
-
resourceToken.code ===
|
|
1777
|
+
resourceToken.code === AbilityDSLToken.IDENTIFIER &&
|
|
1864
1778
|
!resourceToken.value.includes('.')) {
|
|
1865
|
-
this.syntaxError(`Expected comparison operator or value, got \`${resource}\``, this.tokens[beforePos], [
|
|
1779
|
+
this.syntaxError(`Expected comparison operator or value, got \`${resource}\``, this.tokens[beforePos], [AbilityDSLToken.KEYWORD]);
|
|
1866
1780
|
}
|
|
1867
|
-
return new
|
|
1781
|
+
return new AbilityRule({
|
|
1868
1782
|
subject,
|
|
1869
1783
|
resource,
|
|
1870
1784
|
condition,
|
|
@@ -1882,32 +1796,32 @@ class AbilityDSLParser {
|
|
|
1882
1796
|
const savedPos = this.pos;
|
|
1883
1797
|
// "length equals"
|
|
1884
1798
|
if (this.matchWord('length') && this.matchWord('equals')) {
|
|
1885
|
-
return { condition:
|
|
1799
|
+
return { condition: AbilityCondition.length_equals, operator: AbilityDSLToken.LEN_EQ };
|
|
1886
1800
|
}
|
|
1887
1801
|
this.pos = savedPos;
|
|
1888
1802
|
// "length ="
|
|
1889
1803
|
if (this.matchWord('length') && this.matchSymbol('=')) {
|
|
1890
|
-
return { condition:
|
|
1804
|
+
return { condition: AbilityCondition.length_equals, operator: AbilityDSLToken.LEN_EQ };
|
|
1891
1805
|
}
|
|
1892
1806
|
this.pos = savedPos;
|
|
1893
1807
|
// "length greater than"
|
|
1894
1808
|
if (this.matchWord('length') && this.matchWord('greater') && this.matchWord('than')) {
|
|
1895
|
-
return { condition:
|
|
1809
|
+
return { condition: AbilityCondition.length_greater_than, operator: AbilityDSLToken.LEN_GT };
|
|
1896
1810
|
}
|
|
1897
1811
|
this.pos = savedPos;
|
|
1898
1812
|
// "length >"
|
|
1899
1813
|
if (this.matchWord('length') && this.matchSymbol('>')) {
|
|
1900
|
-
return { condition:
|
|
1814
|
+
return { condition: AbilityCondition.length_greater_than, operator: AbilityDSLToken.LEN_GT };
|
|
1901
1815
|
}
|
|
1902
1816
|
this.pos = savedPos;
|
|
1903
1817
|
// "length less than"
|
|
1904
1818
|
if (this.matchWord('length') && this.matchWord('less') && this.matchWord('than')) {
|
|
1905
|
-
return { condition:
|
|
1819
|
+
return { condition: AbilityCondition.length_less_than, operator: AbilityDSLToken.LEN_LT };
|
|
1906
1820
|
}
|
|
1907
1821
|
this.pos = savedPos;
|
|
1908
1822
|
// "length <"
|
|
1909
1823
|
if (this.matchWord('length') && this.matchSymbol('<')) {
|
|
1910
|
-
return { condition:
|
|
1824
|
+
return { condition: AbilityCondition.length_less_than, operator: AbilityDSLToken.LEN_LT };
|
|
1911
1825
|
}
|
|
1912
1826
|
this.pos = savedPos;
|
|
1913
1827
|
// "greater than or equal"
|
|
@@ -1915,12 +1829,12 @@ class AbilityDSLParser {
|
|
|
1915
1829
|
this.matchWord('than') &&
|
|
1916
1830
|
this.matchWord('or') &&
|
|
1917
1831
|
this.matchWord('equal')) {
|
|
1918
|
-
return { condition:
|
|
1832
|
+
return { condition: AbilityCondition.greater_or_equal, operator: AbilityDSLToken.GTE };
|
|
1919
1833
|
}
|
|
1920
1834
|
this.pos = savedPos;
|
|
1921
1835
|
// greater than
|
|
1922
1836
|
if (this.matchWord('greater') && this.matchWord('than')) {
|
|
1923
|
-
return { condition:
|
|
1837
|
+
return { condition: AbilityCondition.greater_than, operator: AbilityDSLToken.GT };
|
|
1924
1838
|
}
|
|
1925
1839
|
this.pos = savedPos;
|
|
1926
1840
|
// less than or equal
|
|
@@ -1928,143 +1842,141 @@ class AbilityDSLParser {
|
|
|
1928
1842
|
this.matchWord('than') &&
|
|
1929
1843
|
this.matchWord('or') &&
|
|
1930
1844
|
this.matchWord('equal')) {
|
|
1931
|
-
return { condition:
|
|
1845
|
+
return { condition: AbilityCondition.less_or_equal, operator: AbilityDSLToken.LTE };
|
|
1932
1846
|
}
|
|
1933
1847
|
this.pos = savedPos;
|
|
1934
1848
|
// less than
|
|
1935
1849
|
if (this.matchWord('less') && this.matchWord('than')) {
|
|
1936
|
-
return { condition:
|
|
1850
|
+
return { condition: AbilityCondition.less_than, operator: AbilityDSLToken.LT };
|
|
1937
1851
|
}
|
|
1938
1852
|
this.pos = savedPos;
|
|
1939
1853
|
// "not contains"
|
|
1940
1854
|
if (this.matchWord('not') && this.matchWord('contains')) {
|
|
1941
1855
|
return {
|
|
1942
|
-
condition:
|
|
1943
|
-
operator:
|
|
1856
|
+
condition: AbilityCondition.not_contains,
|
|
1857
|
+
operator: AbilityDSLToken.NOT_CONTAINS,
|
|
1944
1858
|
};
|
|
1945
1859
|
}
|
|
1946
1860
|
this.pos = savedPos;
|
|
1947
1861
|
// "not includes"
|
|
1948
1862
|
if (this.matchWord('not') && this.matchWord('includes')) {
|
|
1949
1863
|
return {
|
|
1950
|
-
condition:
|
|
1951
|
-
operator:
|
|
1864
|
+
condition: AbilityCondition.not_contains,
|
|
1865
|
+
operator: AbilityDSLToken.NOT_CONTAINS,
|
|
1952
1866
|
};
|
|
1953
1867
|
}
|
|
1954
1868
|
this.pos = savedPos;
|
|
1955
1869
|
// "not includes"
|
|
1956
1870
|
if (this.matchWord('not') && this.matchWord('has')) {
|
|
1957
1871
|
return {
|
|
1958
|
-
condition:
|
|
1959
|
-
operator:
|
|
1872
|
+
condition: AbilityCondition.not_contains,
|
|
1873
|
+
operator: AbilityDSLToken.NOT_CONTAINS,
|
|
1960
1874
|
};
|
|
1961
1875
|
}
|
|
1962
1876
|
this.pos = savedPos;
|
|
1963
1877
|
// "is equals"
|
|
1964
1878
|
if (this.matchWord('is') && this.matchWord('equals')) {
|
|
1965
|
-
return { condition:
|
|
1879
|
+
return { condition: AbilityCondition.equals, operator: AbilityDSLToken.EQ };
|
|
1966
1880
|
}
|
|
1967
1881
|
this.pos = savedPos;
|
|
1968
1882
|
// not equal
|
|
1969
1883
|
if (this.matchWord('not') && this.matchWord('equals')) {
|
|
1970
|
-
return { condition:
|
|
1884
|
+
return { condition: AbilityCondition.not_equals, operator: AbilityDSLToken.NOT_EQ };
|
|
1971
1885
|
}
|
|
1972
1886
|
this.pos = savedPos;
|
|
1973
1887
|
// is not equals
|
|
1974
1888
|
if (this.matchWord('is') && this.matchWord('not') && this.matchWord('equals')) {
|
|
1975
|
-
return { condition:
|
|
1889
|
+
return { condition: AbilityCondition.not_equals, operator: AbilityDSLToken.NOT_EQ };
|
|
1976
1890
|
}
|
|
1977
1891
|
this.pos = savedPos;
|
|
1978
1892
|
// is in
|
|
1979
1893
|
if (this.matchWord('is') && this.matchWord('in')) {
|
|
1980
|
-
return { condition:
|
|
1894
|
+
return { condition: AbilityCondition.in, operator: AbilityDSLToken.IN };
|
|
1981
1895
|
}
|
|
1982
1896
|
this.pos = savedPos;
|
|
1983
1897
|
// not in
|
|
1984
1898
|
if (this.matchWord('not') && this.matchWord('in')) {
|
|
1985
|
-
return { condition:
|
|
1899
|
+
return { condition: AbilityCondition.not_in, operator: AbilityDSLToken.NOT_IN };
|
|
1986
1900
|
}
|
|
1987
1901
|
this.pos = savedPos;
|
|
1988
1902
|
// is not null
|
|
1989
1903
|
if (this.matchWord('is') && this.matchWord('not')) {
|
|
1990
|
-
if (this.check(
|
|
1904
|
+
if (this.check(AbilityDSLToken.NULL)) {
|
|
1991
1905
|
this.advance();
|
|
1992
1906
|
return {
|
|
1993
|
-
condition:
|
|
1994
|
-
operator:
|
|
1907
|
+
condition: AbilityCondition.not_equals,
|
|
1908
|
+
operator: AbilityDSLToken.NOT_EQ_NULL,
|
|
1995
1909
|
};
|
|
1996
1910
|
}
|
|
1997
1911
|
}
|
|
1998
1912
|
this.pos = savedPos;
|
|
1999
1913
|
// is null
|
|
2000
1914
|
if (this.matchWord('is') && this.matchWord('null')) {
|
|
2001
|
-
if (this.check(
|
|
1915
|
+
if (this.check(AbilityDSLToken.NULL)) {
|
|
2002
1916
|
this.advance();
|
|
2003
1917
|
return {
|
|
2004
|
-
condition:
|
|
2005
|
-
operator:
|
|
1918
|
+
condition: AbilityCondition.equals,
|
|
1919
|
+
operator: AbilityDSLToken.EQ_NULL,
|
|
2006
1920
|
};
|
|
2007
1921
|
}
|
|
2008
1922
|
}
|
|
2009
1923
|
this.pos = savedPos;
|
|
2010
1924
|
// Single token (symbol or keyword)
|
|
2011
1925
|
const token = this.peek();
|
|
2012
|
-
if (token.code !==
|
|
2013
|
-
token.code !==
|
|
2014
|
-
token.code !==
|
|
1926
|
+
if (token.code !== AbilityDSLToken.SYMBOL &&
|
|
1927
|
+
token.code !== AbilityDSLToken.KEYWORD &&
|
|
1928
|
+
token.code !== AbilityDSLToken.NULL) {
|
|
2015
1929
|
this.syntaxError(`Expected comparison operator, got \`${token.value}\``, token, [
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
1930
|
+
AbilityDSLToken.SYMBOL,
|
|
1931
|
+
AbilityDSLToken.KEYWORD,
|
|
1932
|
+
AbilityDSLToken.NULL,
|
|
2019
1933
|
]);
|
|
2020
1934
|
}
|
|
2021
1935
|
this.advance();
|
|
2022
1936
|
switch (token.code) {
|
|
2023
|
-
case
|
|
1937
|
+
case AbilityDSLToken.SYMBOL:
|
|
2024
1938
|
if (token.value === '=' || token.value === '==')
|
|
2025
|
-
return { condition:
|
|
1939
|
+
return { condition: AbilityCondition.equals, operator: AbilityDSLToken.EQ };
|
|
2026
1940
|
if (token.value === '!=' || token.value === '<>')
|
|
2027
|
-
return { condition:
|
|
1941
|
+
return { condition: AbilityCondition.not_equals, operator: AbilityDSLToken.NOT_EQ };
|
|
2028
1942
|
if (token.value === '>')
|
|
2029
|
-
return { condition:
|
|
1943
|
+
return { condition: AbilityCondition.greater_than, operator: AbilityDSLToken.GT };
|
|
2030
1944
|
if (token.value === '<')
|
|
2031
|
-
return { condition:
|
|
1945
|
+
return { condition: AbilityCondition.less_than, operator: AbilityDSLToken.LT };
|
|
2032
1946
|
if (token.value === '>=')
|
|
2033
|
-
return { condition:
|
|
1947
|
+
return { condition: AbilityCondition.greater_or_equal, operator: AbilityDSLToken.GTE };
|
|
2034
1948
|
if (token.value === '<=')
|
|
2035
|
-
return { condition:
|
|
1949
|
+
return { condition: AbilityCondition.less_or_equal, operator: AbilityDSLToken.LTE };
|
|
2036
1950
|
break;
|
|
2037
|
-
case
|
|
1951
|
+
case AbilityDSLToken.KEYWORD:
|
|
2038
1952
|
if (token.value === 'contains' || token.value === 'includes' || token.value === 'has')
|
|
2039
|
-
return { condition:
|
|
1953
|
+
return { condition: AbilityCondition.contains, operator: AbilityDSLToken.CONTAINS };
|
|
2040
1954
|
if (token.value === 'in')
|
|
2041
|
-
return { condition:
|
|
1955
|
+
return { condition: AbilityCondition.in, operator: AbilityDSLToken.IN };
|
|
2042
1956
|
if (token.value === 'equals')
|
|
2043
|
-
return { condition:
|
|
1957
|
+
return { condition: AbilityCondition.equals, operator: AbilityDSLToken.EQ };
|
|
2044
1958
|
if (token.value === 'gte') {
|
|
2045
|
-
return { condition:
|
|
1959
|
+
return { condition: AbilityCondition.greater_or_equal, operator: AbilityDSLToken.GTE };
|
|
2046
1960
|
}
|
|
2047
1961
|
if (token.value === 'greater' || token.value === 'gt') {
|
|
2048
1962
|
// If we come here, it means "greater" without "than" – treat as '>'
|
|
2049
|
-
return { condition:
|
|
1963
|
+
return { condition: AbilityCondition.greater_than, operator: AbilityDSLToken.GT };
|
|
2050
1964
|
}
|
|
2051
1965
|
if (token.value === 'less' || token.value === 'lt') {
|
|
2052
|
-
return { condition:
|
|
1966
|
+
return { condition: AbilityCondition.less_than, operator: AbilityDSLToken.LT };
|
|
2053
1967
|
}
|
|
2054
1968
|
if (token.value === 'lte') {
|
|
2055
|
-
return { condition:
|
|
1969
|
+
return { condition: AbilityCondition.less_or_equal, operator: AbilityDSLToken.LTE };
|
|
2056
1970
|
}
|
|
2057
1971
|
if (token.value === 'is') {
|
|
2058
1972
|
// "is" alone -> equals
|
|
2059
|
-
return { condition:
|
|
1973
|
+
return { condition: AbilityCondition.equals, operator: AbilityDSLToken.EQ };
|
|
2060
1974
|
}
|
|
2061
1975
|
break;
|
|
2062
|
-
default:
|
|
2063
|
-
break;
|
|
2064
1976
|
}
|
|
2065
1977
|
return this.syntaxError(`Unexpected operator token \`${token.value}\``, token, [
|
|
2066
|
-
|
|
2067
|
-
|
|
1978
|
+
AbilityDSLToken.SYMBOL,
|
|
1979
|
+
AbilityDSLToken.KEYWORD,
|
|
2068
1980
|
]);
|
|
2069
1981
|
}
|
|
2070
1982
|
/**
|
|
@@ -2077,7 +1989,7 @@ class AbilityDSLParser {
|
|
|
2077
1989
|
return false;
|
|
2078
1990
|
}
|
|
2079
1991
|
const token = this.peek();
|
|
2080
|
-
if ((token.code ===
|
|
1992
|
+
if ((token.code === AbilityDSLToken.KEYWORD || token.code === AbilityDSLToken.IDENTIFIER) &&
|
|
2081
1993
|
token.value === word) {
|
|
2082
1994
|
this.advance();
|
|
2083
1995
|
return true;
|
|
@@ -2088,7 +2000,7 @@ class AbilityDSLParser {
|
|
|
2088
2000
|
if (this.isAtEnd())
|
|
2089
2001
|
return false;
|
|
2090
2002
|
const token = this.peek();
|
|
2091
|
-
if (token.code ===
|
|
2003
|
+
if (token.code === AbilityDSLToken.SYMBOL && token.value === symbol) {
|
|
2092
2004
|
this.advance();
|
|
2093
2005
|
return true;
|
|
2094
2006
|
}
|
|
@@ -2103,32 +2015,32 @@ class AbilityDSLParser {
|
|
|
2103
2015
|
*/
|
|
2104
2016
|
parseValue() {
|
|
2105
2017
|
// Arrays start with a left bracket
|
|
2106
|
-
if (this.check(
|
|
2018
|
+
if (this.check(AbilityDSLToken.LBRACKET)) {
|
|
2107
2019
|
this.advance();
|
|
2108
2020
|
return this.parseArray();
|
|
2109
2021
|
}
|
|
2110
2022
|
// Ensure we are not about to read a structural token as a value.
|
|
2111
2023
|
const token = this.peek();
|
|
2112
|
-
if (token.code ===
|
|
2113
|
-
token.code ===
|
|
2114
|
-
token.code ===
|
|
2024
|
+
if (token.code === AbilityDSLToken.ALL ||
|
|
2025
|
+
token.code === AbilityDSLToken.ANY ||
|
|
2026
|
+
token.code === AbilityDSLToken.EFFECT) {
|
|
2115
2027
|
this.syntaxError(`Unexpected ${token.code} in value position`, token);
|
|
2116
2028
|
}
|
|
2117
2029
|
this.advance();
|
|
2118
2030
|
// CHECK THIS SWITCH COMPARE
|
|
2119
2031
|
switch (token.code) {
|
|
2120
|
-
case
|
|
2032
|
+
case AbilityDSLToken.STRING:
|
|
2121
2033
|
return token.value;
|
|
2122
|
-
case
|
|
2034
|
+
case AbilityDSLToken.NUMBER:
|
|
2123
2035
|
return Number(token.value);
|
|
2124
|
-
case
|
|
2036
|
+
case AbilityDSLToken.BOOLEAN:
|
|
2125
2037
|
return token.value === 'true';
|
|
2126
|
-
case
|
|
2038
|
+
case AbilityDSLToken.NULL:
|
|
2127
2039
|
return null;
|
|
2128
|
-
case
|
|
2040
|
+
case AbilityDSLToken.IDENTIFIER:
|
|
2129
2041
|
return token.value;
|
|
2130
2042
|
default: {
|
|
2131
|
-
this.syntaxError(`Unexpected value token "${token.value}"`, token ?? this.tokens[this.tokens.length - 1], [
|
|
2043
|
+
this.syntaxError(`Unexpected value token "${token.value}"`, token ?? this.tokens[this.tokens.length - 1], [AbilityDSLToken.KEYWORD]);
|
|
2132
2044
|
}
|
|
2133
2045
|
}
|
|
2134
2046
|
}
|
|
@@ -2139,11 +2051,11 @@ class AbilityDSLParser {
|
|
|
2139
2051
|
parseArray() {
|
|
2140
2052
|
const arr = [];
|
|
2141
2053
|
// Handle empty array
|
|
2142
|
-
if (this.check(
|
|
2054
|
+
if (this.check(AbilityDSLToken.RBRACKET)) {
|
|
2143
2055
|
this.advance();
|
|
2144
2056
|
return arr;
|
|
2145
2057
|
}
|
|
2146
|
-
while (!this.isAtEnd() && !this.check(
|
|
2058
|
+
while (!this.isAtEnd() && !this.check(AbilityDSLToken.RBRACKET)) {
|
|
2147
2059
|
const value = this.parseValue();
|
|
2148
2060
|
// Flatten nested arrays if they appear (though grammar doesn't currently allow nesting).
|
|
2149
2061
|
if (Array.isArray(value)) {
|
|
@@ -2159,18 +2071,18 @@ class AbilityDSLParser {
|
|
|
2159
2071
|
this.syntaxError('Unexpected null in array', this.peek());
|
|
2160
2072
|
}
|
|
2161
2073
|
// Optional comma between elements
|
|
2162
|
-
if (this.check(
|
|
2074
|
+
if (this.check(AbilityDSLToken.COMMA)) {
|
|
2163
2075
|
this.advance();
|
|
2164
2076
|
}
|
|
2165
2077
|
}
|
|
2166
|
-
this.consume(
|
|
2078
|
+
this.consume(AbilityDSLToken.RBRACKET, 'Expected "]"');
|
|
2167
2079
|
return arr;
|
|
2168
2080
|
}
|
|
2169
2081
|
// -------------------------------------------------------------------------
|
|
2170
2082
|
// #region Annotations and comments
|
|
2171
2083
|
// -------------------------------------------------------------------------
|
|
2172
2084
|
consumeLeadingComments() {
|
|
2173
|
-
while (this.check(
|
|
2085
|
+
while (this.check(AbilityDSLToken.COMMENT)) {
|
|
2174
2086
|
const token = this.advance();
|
|
2175
2087
|
this.processCommentToken(token);
|
|
2176
2088
|
}
|
|
@@ -2220,7 +2132,7 @@ class AbilityDSLParser {
|
|
|
2220
2132
|
const detailsMsg = `${details}\nDetails: Unexpected value token \`${actual}\``;
|
|
2221
2133
|
finalDetails = suggestion ? `${detailsMsg} Did you mean \`${suggestion}\`?` : detailsMsg;
|
|
2222
2134
|
}
|
|
2223
|
-
throw new
|
|
2135
|
+
throw new AbilityDSLSyntaxError(token.line, token.column, context + '\n', finalDetails);
|
|
2224
2136
|
}
|
|
2225
2137
|
getLine(lineNumber) {
|
|
2226
2138
|
return this.dsl.split(/\r?\n/)[lineNumber - 1] ?? '';
|
|
@@ -2269,7 +2181,7 @@ class AbilityDSLParser {
|
|
|
2269
2181
|
}
|
|
2270
2182
|
}
|
|
2271
2183
|
const expected = types.map(t => t).join(', ');
|
|
2272
|
-
const actual = token ? token.value :
|
|
2184
|
+
const actual = token ? token.value : AbilityDSLToken.EOF;
|
|
2273
2185
|
const suggestion = this.suggest(actual, types);
|
|
2274
2186
|
const details = `${message}\nDetails: Unexpected token \`${actual}\`, expected one of: ${expected}.`;
|
|
2275
2187
|
const finalMsg = suggestion ? `${details} Did you mean \`${suggestion}\`?` : details;
|
|
@@ -2281,7 +2193,7 @@ class AbilityDSLParser {
|
|
|
2281
2193
|
return this.advance();
|
|
2282
2194
|
}
|
|
2283
2195
|
const expected = type;
|
|
2284
|
-
const actual = token ? token.value :
|
|
2196
|
+
const actual = token ? token.value : AbilityDSLToken.EOF;
|
|
2285
2197
|
const suggestion = this.suggest(actual, [type]);
|
|
2286
2198
|
const details = `${message}\nDetails: Unexpected token \`${actual}\`, expected "${expected}".`;
|
|
2287
2199
|
const finalMsg = suggestion ? `${details} Did you mean \`${suggestion}\`?` : details;
|
|
@@ -2293,10 +2205,10 @@ class AbilityDSLParser {
|
|
|
2293
2205
|
return this.peek().code === type;
|
|
2294
2206
|
}
|
|
2295
2207
|
isStartOfPolicy() {
|
|
2296
|
-
return this.check(
|
|
2208
|
+
return this.check(AbilityDSLToken.EFFECT);
|
|
2297
2209
|
}
|
|
2298
2210
|
isStartOfGroup() {
|
|
2299
|
-
return this.check(
|
|
2211
|
+
return this.check(AbilityDSLToken.ALL) || this.check(AbilityDSLToken.ANY);
|
|
2300
2212
|
}
|
|
2301
2213
|
advance() {
|
|
2302
2214
|
return this.tokens[this.pos++];
|
|
@@ -2305,296 +2217,29 @@ class AbilityDSLParser {
|
|
|
2305
2217
|
return this.tokens[this.pos];
|
|
2306
2218
|
}
|
|
2307
2219
|
isAtEnd() {
|
|
2308
|
-
return this.peek().code ===
|
|
2309
|
-
}
|
|
2310
|
-
}
|
|
2311
|
-
exports.AbilityDSLParser = AbilityDSLParser;
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
/***/ }),
|
|
2315
|
-
|
|
2316
|
-
/***/ 883:
|
|
2317
|
-
/***/ ((__unused_webpack_module, exports) => {
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2321
|
-
exports.AbilityDSLSyntaxError = void 0;
|
|
2322
|
-
class AbilityDSLSyntaxError extends Error {
|
|
2323
|
-
line;
|
|
2324
|
-
column;
|
|
2325
|
-
context;
|
|
2326
|
-
details;
|
|
2327
|
-
_formattedMessage;
|
|
2328
|
-
_originalStack;
|
|
2329
|
-
constructor(line, column, context, // строка DSL + ^ + соседние строки
|
|
2330
|
-
details) {
|
|
2331
|
-
super(details.split('\n')[0]); // message = только первая строка
|
|
2332
|
-
this.line = line;
|
|
2333
|
-
this.column = column;
|
|
2334
|
-
this.context = context;
|
|
2335
|
-
this.details = details;
|
|
2336
|
-
this.name = 'AbilityDSLSyntaxError';
|
|
2337
|
-
if (Error.captureStackTrace) {
|
|
2338
|
-
Error.captureStackTrace(this, AbilityDSLSyntaxError);
|
|
2339
|
-
}
|
|
2340
|
-
this._originalStack = this.stack;
|
|
2341
|
-
this._formattedMessage = this.formatMessage();
|
|
2342
|
-
Object.defineProperty(this, 'stack', {
|
|
2343
|
-
get: () => this._formattedMessage,
|
|
2344
|
-
configurable: true,
|
|
2345
|
-
});
|
|
2346
|
-
}
|
|
2347
|
-
static supportsColor() {
|
|
2348
|
-
return typeof process !== 'undefined' && process.stdout?.isTTY;
|
|
2349
|
-
}
|
|
2350
|
-
formatMessage() {
|
|
2351
|
-
const useColor = AbilityDSLSyntaxError.supportsColor();
|
|
2352
|
-
const BOLD = useColor ? '\x1b[1m' : '';
|
|
2353
|
-
const RED = useColor ? '\x1b[31m' : '';
|
|
2354
|
-
const ORANGE = useColor ? '\x1b[33;1m' : '';
|
|
2355
|
-
const GRAY = useColor ? '\x1b[90m' : '';
|
|
2356
|
-
const RESET = useColor ? '\x1b[0m' : '';
|
|
2357
|
-
const lines = this.context.split('\n');
|
|
2358
|
-
// Find line with ^
|
|
2359
|
-
const pointerIndex = lines.findIndex(l => l.includes('^') || l.includes('~'));
|
|
2360
|
-
const commentIndex = lines.findIndex(l => l.trim().includes('#'));
|
|
2361
|
-
const formattedLines = lines.map((line, idx) => {
|
|
2362
|
-
if (idx === pointerIndex - 1) {
|
|
2363
|
-
// Error line
|
|
2364
|
-
return `${BOLD}${ORANGE}${line}${RESET}`;
|
|
2365
|
-
}
|
|
2366
|
-
if (idx === pointerIndex) {
|
|
2367
|
-
// Error with ~~~~~
|
|
2368
|
-
return `${RED}${line}${RESET}`;
|
|
2369
|
-
}
|
|
2370
|
-
// Comments # ...
|
|
2371
|
-
if (idx === commentIndex) {
|
|
2372
|
-
return `${GRAY}${line}${RESET}`;
|
|
2373
|
-
}
|
|
2374
|
-
return line;
|
|
2375
|
-
});
|
|
2376
|
-
const contextBlock = formattedLines.join('\n');
|
|
2377
|
-
return `${BOLD}${RED}${this.name}: ${this.details}${RESET}\n\n` + contextBlock;
|
|
2378
|
-
}
|
|
2379
|
-
toString() {
|
|
2380
|
-
return this._formattedMessage;
|
|
2220
|
+
return this.peek().code === AbilityDSLToken.EOF;
|
|
2381
2221
|
}
|
|
2382
2222
|
}
|
|
2383
|
-
exports.AbilityDSLSyntaxError = AbilityDSLSyntaxError;
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
/***/ }),
|
|
2387
|
-
|
|
2388
|
-
/***/ 325:
|
|
2389
|
-
/***/ (function(__unused_webpack_module, exports, __webpack_require__) {
|
|
2390
|
-
|
|
2391
2223
|
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
exports.
|
|
2397
|
-
const AbilityCode_1 = __importDefault(__webpack_require__(301));
|
|
2398
|
-
/**
|
|
2399
|
-
* Represents a single token produced by the Ability DSL lexer.
|
|
2400
|
-
* Each token carries a type (e.g., EFFECT, IDENTIFIER, STRING) and its raw string value.
|
|
2401
|
-
*/
|
|
2402
|
-
class AbilityDSLToken extends AbilityCode_1.default {
|
|
2403
|
-
/** The literal text of the token as it appeared in the input (e.g., "permit", "user.roles", "admin"). */
|
|
2404
|
-
value = '';
|
|
2405
|
-
/** The line number in DSL */
|
|
2406
|
-
line = 1;
|
|
2407
|
-
/** The column in dsl */
|
|
2408
|
-
column = 1;
|
|
2409
|
-
constructor(type, value, line, column) {
|
|
2410
|
-
super(type);
|
|
2411
|
-
this.value = value;
|
|
2412
|
-
this.line = line;
|
|
2413
|
-
this.column = column;
|
|
2414
|
-
}
|
|
2415
|
-
/**
|
|
2416
|
-
* Returns a human-readable representation of the token, useful for debugging.
|
|
2417
|
-
* Example output: "AbilityDSLToken([EFFECT] permit"
|
|
2418
|
-
*/
|
|
2419
|
-
toString() {
|
|
2420
|
-
return `AbilityDSLToken([${this.code}] "${this.value}" at ${this.line}:${this.column})`;
|
|
2421
|
-
}
|
|
2422
|
-
static EFFECT = 'EFFECT';
|
|
2423
|
-
static IF = 'IF';
|
|
2424
|
-
static PERMISSION = 'PERMISSION';
|
|
2425
|
-
static IDENTIFIER = 'IDENTIFIER';
|
|
2426
|
-
static COLON = 'COLON';
|
|
2427
|
-
static COMMA = 'COMMA';
|
|
2428
|
-
static DOT = 'DOT';
|
|
2429
|
-
static LBRACKET = 'LBRACKET';
|
|
2430
|
-
static RBRACKET = 'RBRACKET';
|
|
2431
|
-
static ALL = 'ALL';
|
|
2432
|
-
static ANY = 'ANY';
|
|
2433
|
-
static OF = 'OF';
|
|
2434
|
-
static EOF = 'EOF';
|
|
2435
|
-
static COMMENT = 'COMMENT';
|
|
2436
|
-
static EQ = 'EQ';
|
|
2437
|
-
static CONTAINS = 'CONTAINS';
|
|
2438
|
-
static IN = 'IN';
|
|
2439
|
-
static NOT_IN = 'NOT_IN';
|
|
2440
|
-
static NOT_CONTAINS = 'NOT_CONTAINS';
|
|
2441
|
-
static GT = 'GT';
|
|
2442
|
-
static GTE = 'GTE';
|
|
2443
|
-
static LT = 'LT';
|
|
2444
|
-
static LTE = 'LTE';
|
|
2445
|
-
static NULL = 'NULL';
|
|
2446
|
-
static EQ_NULL = 'EQ_NULL';
|
|
2447
|
-
static NOT_EQ_NULL = 'NOT_EQ_NULL';
|
|
2448
|
-
static LEN_GT = 'LEN_GT';
|
|
2449
|
-
static LEN_LT = 'LEN_LT';
|
|
2450
|
-
static LEN_EQ = 'LEN_EQ';
|
|
2451
|
-
static NOT_EQ = 'NOT_EQ';
|
|
2452
|
-
static STRING = 'STRING';
|
|
2453
|
-
static NUMBER = 'NUMBER';
|
|
2454
|
-
static BOOLEAN = 'BOOLEAN';
|
|
2455
|
-
static SYMBOL = 'SYMBOL';
|
|
2456
|
-
static KEYWORD = 'KEYWORD';
|
|
2457
|
-
}
|
|
2224
|
+
exports.AbilityCode = AbilityCode;
|
|
2225
|
+
exports.AbilityCompare = AbilityCompare;
|
|
2226
|
+
exports.AbilityCondition = AbilityCondition;
|
|
2227
|
+
exports.AbilityDSLLexer = AbilityDSLLexer;
|
|
2228
|
+
exports.AbilityDSLParser = AbilityDSLParser;
|
|
2458
2229
|
exports.AbilityDSLToken = AbilityDSLToken;
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
2468
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
2469
|
-
};
|
|
2470
|
-
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
|
2471
|
-
exports.AbilityJSONParser = void 0;
|
|
2472
|
-
const AbilityCondition_1 = __importDefault(__webpack_require__(167));
|
|
2473
|
-
const AbilityRule_1 = __webpack_require__(306);
|
|
2474
|
-
const AbilityRuleSet_1 = __webpack_require__(56);
|
|
2475
|
-
const AbilityCompare_1 = __importDefault(__webpack_require__(413));
|
|
2476
|
-
const AbilityPolicyEffect_1 = __importDefault(__webpack_require__(179));
|
|
2477
|
-
const AbilityPolicy_1 = __webpack_require__(278);
|
|
2478
|
-
class AbilityJSONParser {
|
|
2479
|
-
/**
|
|
2480
|
-
* Parses an array of policy configurations into an array of AbilityPolicy instances.
|
|
2481
|
-
* @param configs - Array of policy configurations
|
|
2482
|
-
* @returns Array of AbilityPolicy instances
|
|
2483
|
-
*/
|
|
2484
|
-
static parse(configs) {
|
|
2485
|
-
return configs.map(config => AbilityJSONParser.parsePolicy(config));
|
|
2486
|
-
}
|
|
2487
|
-
static parsePolicy(config) {
|
|
2488
|
-
const { id, name, ruleSet, compareMethod, permission, effect } = config;
|
|
2489
|
-
// Create the empty policy
|
|
2490
|
-
const policy = new AbilityPolicy_1.AbilityPolicy({
|
|
2491
|
-
name,
|
|
2492
|
-
id,
|
|
2493
|
-
permission: permission,
|
|
2494
|
-
effect: new AbilityPolicyEffect_1.default(effect),
|
|
2495
|
-
});
|
|
2496
|
-
policy.compareMethod = new AbilityCompare_1.default(compareMethod);
|
|
2497
|
-
ruleSet.forEach(ruleSetConfig => {
|
|
2498
|
-
policy.addRuleSet(AbilityRuleSet_1.AbilityRuleSet.fromJSON(ruleSetConfig));
|
|
2499
|
-
});
|
|
2500
|
-
return policy;
|
|
2501
|
-
}
|
|
2502
|
-
static parseRule(config) {
|
|
2503
|
-
const { id, name, subject, resource, condition } = config;
|
|
2504
|
-
return new AbilityRule_1.AbilityRule({
|
|
2505
|
-
id,
|
|
2506
|
-
name,
|
|
2507
|
-
subject,
|
|
2508
|
-
resource,
|
|
2509
|
-
condition: new AbilityCondition_1.default(condition),
|
|
2510
|
-
});
|
|
2511
|
-
}
|
|
2512
|
-
/**
|
|
2513
|
-
* Parse the config JSON format to Group class instance
|
|
2514
|
-
*/
|
|
2515
|
-
static parseRuleSet(config) {
|
|
2516
|
-
const { id, name, rules, compareMethod } = config;
|
|
2517
|
-
const ruleSet = new AbilityRuleSet_1.AbilityRuleSet({
|
|
2518
|
-
compareMethod: new AbilityCompare_1.default(compareMethod),
|
|
2519
|
-
name,
|
|
2520
|
-
id,
|
|
2521
|
-
});
|
|
2522
|
-
// Adding rules if exists
|
|
2523
|
-
if (rules && rules.length > 0) {
|
|
2524
|
-
const abilityRules = rules.map(ruleConfig => AbilityRule_1.AbilityRule.fromJSON(ruleConfig));
|
|
2525
|
-
ruleSet.addRules(abilityRules);
|
|
2526
|
-
}
|
|
2527
|
-
return ruleSet;
|
|
2528
|
-
}
|
|
2529
|
-
static ruleToJSON(rule) {
|
|
2530
|
-
return {
|
|
2531
|
-
id: rule.id,
|
|
2532
|
-
name: rule.name,
|
|
2533
|
-
subject: rule.subject,
|
|
2534
|
-
resource: rule.resource,
|
|
2535
|
-
condition: rule.condition.code,
|
|
2536
|
-
};
|
|
2537
|
-
}
|
|
2538
|
-
static ruleSetToJSON(ruleSet) {
|
|
2539
|
-
return {
|
|
2540
|
-
id: ruleSet.id.toString(),
|
|
2541
|
-
name: ruleSet.name.toString(),
|
|
2542
|
-
compareMethod: ruleSet.compareMethod.code.toString(),
|
|
2543
|
-
rules: ruleSet.rules.map(rule => AbilityJSONParser.ruleToJSON(rule)),
|
|
2544
|
-
};
|
|
2545
|
-
}
|
|
2546
|
-
static policyToJSON(policy) {
|
|
2547
|
-
return {
|
|
2548
|
-
id: policy.id.toString(),
|
|
2549
|
-
name: policy.name.toString(),
|
|
2550
|
-
compareMethod: policy.compareMethod.code.toString(),
|
|
2551
|
-
ruleSet: policy.ruleSet.map(ruleSet => AbilityJSONParser.ruleSetToJSON(ruleSet)),
|
|
2552
|
-
permission: policy.permission,
|
|
2553
|
-
effect: policy.effect.code,
|
|
2554
|
-
};
|
|
2555
|
-
}
|
|
2556
|
-
static toJSON(policies) {
|
|
2557
|
-
return policies.map(policy => AbilityJSONParser.policyToJSON(policy));
|
|
2558
|
-
}
|
|
2559
|
-
}
|
|
2230
|
+
exports.AbilityError = AbilityError;
|
|
2231
|
+
exports.AbilityExplain = AbilityExplain;
|
|
2232
|
+
exports.AbilityExplainPolicy = AbilityExplainPolicy;
|
|
2233
|
+
exports.AbilityExplainRule = AbilityExplainRule;
|
|
2234
|
+
exports.AbilityExplainRuleSet = AbilityExplainRuleSet;
|
|
2235
|
+
exports.AbilityInMemoryCache = AbilityInMemoryCache;
|
|
2560
2236
|
exports.AbilityJSONParser = AbilityJSONParser;
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
/******/ // The require function
|
|
2571
|
-
/******/ function __webpack_require__(moduleId) {
|
|
2572
|
-
/******/ // Check if module is in cache
|
|
2573
|
-
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
2574
|
-
/******/ if (cachedModule !== undefined) {
|
|
2575
|
-
/******/ return cachedModule.exports;
|
|
2576
|
-
/******/ }
|
|
2577
|
-
/******/ // Create a new module (and put it into the cache)
|
|
2578
|
-
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
2579
|
-
/******/ // no module.id needed
|
|
2580
|
-
/******/ // no module.loaded needed
|
|
2581
|
-
/******/ exports: {}
|
|
2582
|
-
/******/ };
|
|
2583
|
-
/******/
|
|
2584
|
-
/******/ // Execute the module function
|
|
2585
|
-
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
2586
|
-
/******/
|
|
2587
|
-
/******/ // Return the exports of the module
|
|
2588
|
-
/******/ return module.exports;
|
|
2589
|
-
/******/ }
|
|
2590
|
-
/******/
|
|
2591
|
-
/************************************************************************/
|
|
2592
|
-
/******/
|
|
2593
|
-
/******/ // startup
|
|
2594
|
-
/******/ // Load entry module and return exports
|
|
2595
|
-
/******/ // This entry module is referenced by other modules so it can't be inlined
|
|
2596
|
-
/******/ var __webpack_exports__ = __webpack_require__(156);
|
|
2597
|
-
/******/ module.exports = __webpack_exports__;
|
|
2598
|
-
/******/
|
|
2599
|
-
/******/ })()
|
|
2600
|
-
;
|
|
2237
|
+
exports.AbilityMatch = AbilityMatch;
|
|
2238
|
+
exports.AbilityParser = AbilityParser;
|
|
2239
|
+
exports.AbilityParserError = AbilityParserError;
|
|
2240
|
+
exports.AbilityPolicy = AbilityPolicy;
|
|
2241
|
+
exports.AbilityPolicyEffect = AbilityPolicyEffect;
|
|
2242
|
+
exports.AbilityResolver = AbilityResolver;
|
|
2243
|
+
exports.AbilityResult = AbilityResult;
|
|
2244
|
+
exports.AbilityRule = AbilityRule;
|
|
2245
|
+
exports.AbilityRuleSet = AbilityRuleSet;
|