@via-profit/ability 3.6.1 → 3.6.4
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/dist/index.d.ts +53 -2
- package/dist/index.js +205 -52
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -218,14 +218,16 @@ type AbilityExplainConfig = {
|
|
|
218
218
|
readonly type: AbilityExplainType;
|
|
219
219
|
readonly name: string;
|
|
220
220
|
readonly match: AbilityMatchType;
|
|
221
|
+
readonly debugInfo?: string;
|
|
221
222
|
};
|
|
222
223
|
declare class AbilityExplain {
|
|
223
224
|
readonly type: AbilityExplainType;
|
|
224
225
|
readonly children: AbilityExplain[];
|
|
225
226
|
readonly name: string;
|
|
226
227
|
readonly match: AbilityMatchType;
|
|
228
|
+
readonly debugInfo?: string;
|
|
227
229
|
constructor(config: AbilityExplainConfig, children?: AbilityExplain[]);
|
|
228
|
-
toString(
|
|
230
|
+
toString(indentPrefix?: string, isLast?: boolean): string;
|
|
229
231
|
}
|
|
230
232
|
declare class AbilityExplainRule extends AbilityExplain {
|
|
231
233
|
constructor(rule: AbilityRule);
|
|
@@ -394,10 +396,40 @@ declare abstract class AbilityStrategy<Resource extends ResourceObject = Record<
|
|
|
394
396
|
readonly policies: readonly AbilityPolicy<Resource, Environment>[];
|
|
395
397
|
private readonly matched;
|
|
396
398
|
constructor(policies: readonly AbilityPolicy<Resource, Environment>[]);
|
|
399
|
+
/**
|
|
400
|
+
* Executes the strategy’s decision logic and returns the final policy effect
|
|
401
|
+
* (permit or deny).
|
|
402
|
+
*
|
|
403
|
+
* Each concrete strategy must implement its own evaluation rules:
|
|
404
|
+
* - how matched policies are interpreted,
|
|
405
|
+
* - how priority, order, or overrides are applied,
|
|
406
|
+
* - and how the final effect is determined.
|
|
407
|
+
*
|
|
408
|
+
* The implementation must also record the policy that actually determined
|
|
409
|
+
* the outcome (if any), so that decisivePolicy() can later expose it.
|
|
410
|
+
*/
|
|
397
411
|
abstract evaluate(): AbilityPolicyEffectType;
|
|
412
|
+
/**
|
|
413
|
+
* Returns the policy that directly determined the final decision of the strategy.
|
|
414
|
+
*
|
|
415
|
+
* This is:
|
|
416
|
+
* - the specific policy chosen by the strategy’s evaluation rules, or
|
|
417
|
+
* - null if the decision was made by default (e.g., no matched policies,
|
|
418
|
+
* or the strategy cannot identify a single decisive policy).
|
|
419
|
+
*
|
|
420
|
+
* This method performs no computation; it simply exposes the result stored
|
|
421
|
+
* during evaluate(), enabling explainability and debugging tools to show
|
|
422
|
+
* why a decision was made.
|
|
423
|
+
*/
|
|
424
|
+
abstract decisivePolicy(): AbilityPolicy<Resource, Environment> | null;
|
|
398
425
|
matchedPolicies(): readonly AbilityPolicy<Resource, Environment, string>[];
|
|
426
|
+
hasMatched(): boolean;
|
|
399
427
|
protected firstMatched(): AbilityPolicy<Resource, Environment> | null;
|
|
400
428
|
protected lastMatched(): AbilityPolicy<Resource, Environment> | null;
|
|
429
|
+
protected firstDenied(): AbilityPolicy<Resource, Environment> | null;
|
|
430
|
+
protected firstPermitted(): AbilityPolicy<Resource, Environment> | null;
|
|
431
|
+
protected getPermitPolicies(): AbilityPolicy<Resource, Environment, string>[];
|
|
432
|
+
protected getDenyPolicies(): AbilityPolicy<Resource, Environment, string>[];
|
|
401
433
|
protected hasPermit(): boolean;
|
|
402
434
|
protected hasDeny(): boolean;
|
|
403
435
|
isAllowed(): boolean;
|
|
@@ -414,7 +446,9 @@ declare class AbilityResult<R extends ResourceObject = Record<string, unknown>,
|
|
|
414
446
|
*
|
|
415
447
|
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
416
448
|
*/
|
|
417
|
-
explain():
|
|
449
|
+
explain(): string;
|
|
450
|
+
decisive(): AbilityPolicy<R, E, string> | null;
|
|
451
|
+
explainDecisive(): string | null;
|
|
418
452
|
isAllowed: () => boolean;
|
|
419
453
|
isDenied: () => boolean;
|
|
420
454
|
}
|
|
@@ -551,6 +585,7 @@ declare class AbilityDSLParser<R extends ResourceObject = Record<string, unknown
|
|
|
551
585
|
private takeAnnotations;
|
|
552
586
|
private isStartOfPolicy;
|
|
553
587
|
private isStartOfGroup;
|
|
588
|
+
private isStartOfRule;
|
|
554
589
|
private isStartOfExcept;
|
|
555
590
|
private isStartOfAlias;
|
|
556
591
|
}
|
|
@@ -656,7 +691,9 @@ declare function ability<R extends ResourceObject = Record<string, unknown>, E e
|
|
|
656
691
|
* Result: deny (because not all policies permitted)
|
|
657
692
|
*/
|
|
658
693
|
declare class AllMustPermitStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
694
|
+
private _decisive;
|
|
659
695
|
evaluate(): AbilityPolicyEffectType;
|
|
696
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
660
697
|
}
|
|
661
698
|
|
|
662
699
|
/**
|
|
@@ -677,7 +714,9 @@ declare class AllMustPermitStrategy<R extends ResourceObject, E extends Environm
|
|
|
677
714
|
* Result: permit (because at least one policy permitted)
|
|
678
715
|
*/
|
|
679
716
|
declare class AnyPermitStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
717
|
+
private _decisive;
|
|
680
718
|
evaluate(): AbilityPolicyEffectType;
|
|
719
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
681
720
|
}
|
|
682
721
|
|
|
683
722
|
/**
|
|
@@ -699,7 +738,9 @@ declare class AnyPermitStrategy<R extends ResourceObject, E extends EnvironmentO
|
|
|
699
738
|
* Result: deny (because deny overrides everything)
|
|
700
739
|
*/
|
|
701
740
|
declare class DenyOverridesStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
741
|
+
private _decisive;
|
|
702
742
|
evaluate(): AbilityPolicyEffectType;
|
|
743
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
703
744
|
}
|
|
704
745
|
|
|
705
746
|
/**
|
|
@@ -720,7 +761,9 @@ declare class DenyOverridesStrategy<R extends ResourceObject, E extends Environm
|
|
|
720
761
|
* Result: permit (P2 is the first applicable)
|
|
721
762
|
*/
|
|
722
763
|
declare class FirstMatchStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
764
|
+
private _decisive;
|
|
723
765
|
evaluate(): AbilityPolicyEffectType;
|
|
766
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
724
767
|
}
|
|
725
768
|
|
|
726
769
|
/**
|
|
@@ -740,7 +783,9 @@ declare class FirstMatchStrategy<R extends ResourceObject, E extends Environment
|
|
|
740
783
|
* Result: deny (more than one applicable policy)
|
|
741
784
|
*/
|
|
742
785
|
declare class OnlyOneApplicableStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
786
|
+
private _decisive;
|
|
743
787
|
evaluate(): AbilityPolicyEffectType;
|
|
788
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
744
789
|
}
|
|
745
790
|
|
|
746
791
|
/**
|
|
@@ -762,7 +807,9 @@ declare class OnlyOneApplicableStrategy<R extends ResourceObject, E extends Envi
|
|
|
762
807
|
* Result: permit (permit overrides deny)
|
|
763
808
|
*/
|
|
764
809
|
declare class PermitOverridesStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
810
|
+
private _decisive;
|
|
765
811
|
evaluate(): AbilityPolicyEffectType;
|
|
812
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
766
813
|
}
|
|
767
814
|
|
|
768
815
|
/**
|
|
@@ -782,7 +829,9 @@ declare class PermitOverridesStrategy<R extends ResourceObject, E extends Enviro
|
|
|
782
829
|
* Result: permit (P3 is the last applicable)
|
|
783
830
|
*/
|
|
784
831
|
declare class SequentialLastMatchStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
832
|
+
private _decisive;
|
|
785
833
|
evaluate(): AbilityPolicyEffectType;
|
|
834
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
786
835
|
}
|
|
787
836
|
|
|
788
837
|
/**
|
|
@@ -803,7 +852,9 @@ declare class SequentialLastMatchStrategy<R extends ResourceObject, E extends En
|
|
|
803
852
|
* Result: permit (P2 has higher priority)
|
|
804
853
|
*/
|
|
805
854
|
declare class PriorityStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
855
|
+
private _decisive;
|
|
806
856
|
evaluate(): AbilityPolicyEffectType;
|
|
857
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
807
858
|
}
|
|
808
859
|
|
|
809
860
|
export { AbilityCompare, AbilityCondition, AbilityDSLLexer, AbilityDSLParser, AbilityDSLToken, AbilityError, AbilityExplain, AbilityExplainPolicy, AbilityExplainRule, AbilityExplainRuleSet, AbilityJSONParser, AbilityMatch, AbilityParserError, AbilityPolicy, AbilityPolicyEffect, AbilityResolver, AbilityResult, AbilityRule, AbilityRuleSet, AbilityStrategy, AbilityTypeGenerator, AllMustPermitStrategy, AnyPermitStrategy, DenyOverridesStrategy, FirstMatchStrategy, OnlyOneApplicableStrategy, PermitOverridesStrategy, PriorityStrategy, SequentialLastMatchStrategy, TokenTypes, ability, fromLiteral, isConditionEqual, isConditionNotEqual, toLiteral };
|
package/dist/index.js
CHANGED
|
@@ -126,27 +126,53 @@ const AbilityMatch = {
|
|
|
126
126
|
disabled: brand$2('disabled'),
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
+
const colors = {
|
|
130
|
+
reset: '\x1b[0m',
|
|
131
|
+
green: '\x1b[32m',
|
|
132
|
+
red: '\x1b[31m',
|
|
133
|
+
blue: '\x1b[34m',
|
|
134
|
+
yellow: '\x1b[33m',
|
|
135
|
+
white: '\x1b[37m',
|
|
136
|
+
gray: '\x1b[90m',
|
|
137
|
+
};
|
|
129
138
|
class AbilityExplain {
|
|
130
139
|
type;
|
|
131
140
|
children;
|
|
132
141
|
name;
|
|
133
142
|
match;
|
|
143
|
+
debugInfo;
|
|
134
144
|
constructor(config, children = []) {
|
|
135
145
|
this.type = config.type;
|
|
136
146
|
this.children = children;
|
|
137
147
|
this.name = config.name;
|
|
138
148
|
this.match = config.match;
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
this.debugInfo = config.debugInfo;
|
|
150
|
+
}
|
|
151
|
+
toString(indentPrefix = '', isLast = true) {
|
|
152
|
+
const isMatch = this.match === AbilityMatch.match;
|
|
153
|
+
const mark = isMatch ? `${colors.green}✓${colors.reset}` : `${colors.red}✗${colors.reset}`;
|
|
154
|
+
let label;
|
|
155
|
+
switch (this.type) {
|
|
156
|
+
case 'policy':
|
|
157
|
+
label = `${colors.blue}POLICY${colors.reset}`;
|
|
158
|
+
break;
|
|
159
|
+
case 'ruleSet':
|
|
160
|
+
label = `${colors.yellow}RULESET${colors.reset}`;
|
|
161
|
+
break;
|
|
162
|
+
default:
|
|
163
|
+
label = `${colors.white}RULE${colors.reset}`;
|
|
146
164
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
165
|
+
const branch = indentPrefix.length === 0
|
|
166
|
+
? ''
|
|
167
|
+
: isLast
|
|
168
|
+
? `${colors.gray}└─${colors.reset} `
|
|
169
|
+
: `${colors.gray}├─${colors.reset} `;
|
|
170
|
+
let out = `${indentPrefix}${branch}${label} ${this.name} — ${mark}`;
|
|
171
|
+
if (this.debugInfo)
|
|
172
|
+
out += ` ${colors.gray}(${this.debugInfo})${colors.reset}`;
|
|
173
|
+
const nextIndent = indentPrefix + (isLast ? ' ' : `${colors.gray}│ ${colors.reset}`);
|
|
174
|
+
this.children.forEach((child, idx) => {
|
|
175
|
+
out += '\n' + child.toString(nextIndent, idx === this.children.length - 1);
|
|
150
176
|
});
|
|
151
177
|
return out;
|
|
152
178
|
}
|
|
@@ -195,9 +221,21 @@ class AbilityResult {
|
|
|
195
221
|
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
196
222
|
*/
|
|
197
223
|
explain() {
|
|
198
|
-
return this.strategy.policies
|
|
199
|
-
|
|
200
|
-
|
|
224
|
+
return this.strategy.policies
|
|
225
|
+
.map(policy => {
|
|
226
|
+
return new AbilityExplainPolicy(policy).toString();
|
|
227
|
+
})
|
|
228
|
+
.join('\n');
|
|
229
|
+
}
|
|
230
|
+
decisive() {
|
|
231
|
+
return this.strategy.decisivePolicy();
|
|
232
|
+
}
|
|
233
|
+
explainDecisive() {
|
|
234
|
+
const policy = this.decisive();
|
|
235
|
+
if (!policy) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
return new AbilityExplainPolicy(policy).toString();
|
|
201
239
|
}
|
|
202
240
|
isAllowed = () => {
|
|
203
241
|
return this.strategy.isAllowed();
|
|
@@ -287,24 +325,25 @@ class AbilityResolver {
|
|
|
287
325
|
.toLowerCase(); // optional: make case-insensitive
|
|
288
326
|
}
|
|
289
327
|
static matchPermissions(policySegments, inputSegments) {
|
|
290
|
-
|
|
291
|
-
for (
|
|
328
|
+
let i = 0;
|
|
329
|
+
for (; i < policySegments.length; i++) {
|
|
292
330
|
const pSeg = policySegments[i];
|
|
293
331
|
const iSeg = inputSegments[i];
|
|
294
|
-
|
|
295
|
-
return false;
|
|
296
|
-
}
|
|
332
|
+
// '*' — глобальный wildcard: матчим всё, что дальше
|
|
297
333
|
if (pSeg === '*') {
|
|
298
|
-
|
|
334
|
+
return true;
|
|
299
335
|
}
|
|
336
|
+
// input закончился раньше — mismatch
|
|
300
337
|
if (iSeg === undefined) {
|
|
301
338
|
return false;
|
|
302
339
|
}
|
|
340
|
+
// обычное сравнение
|
|
303
341
|
if (pSeg !== iSeg) {
|
|
304
342
|
return false;
|
|
305
343
|
}
|
|
306
344
|
}
|
|
307
|
-
|
|
345
|
+
// Если политика закончилась, но input длиннее — match только если последний сегмент был '*'
|
|
346
|
+
return i === inputSegments.length;
|
|
308
347
|
}
|
|
309
348
|
}
|
|
310
349
|
|
|
@@ -1500,32 +1539,32 @@ class AbilityJSONParser {
|
|
|
1500
1539
|
return {
|
|
1501
1540
|
id: rule.id,
|
|
1502
1541
|
name: rule.name,
|
|
1542
|
+
disabled: rule.disabled,
|
|
1503
1543
|
subject: rule.subject,
|
|
1504
1544
|
resource: rule.resource,
|
|
1505
1545
|
condition: rule.condition,
|
|
1506
|
-
disabled: rule.disabled,
|
|
1507
1546
|
};
|
|
1508
1547
|
}
|
|
1509
1548
|
static ruleSetToJSON(ruleSet) {
|
|
1510
1549
|
return {
|
|
1511
1550
|
id: ruleSet.id.toString(),
|
|
1512
1551
|
name: ruleSet.name.toString(),
|
|
1552
|
+
disabled: ruleSet.disabled,
|
|
1513
1553
|
compareMethod: ruleSet.compareMethod,
|
|
1514
1554
|
rules: ruleSet.rules.map(rule => AbilityJSONParser.ruleToJSON(rule)),
|
|
1515
|
-
disabled: ruleSet.disabled,
|
|
1516
1555
|
};
|
|
1517
1556
|
}
|
|
1518
1557
|
static policyToJSON(policy) {
|
|
1519
1558
|
return {
|
|
1520
1559
|
id: policy.id.toString(),
|
|
1521
1560
|
name: policy.name.toString(),
|
|
1522
|
-
|
|
1523
|
-
|
|
1561
|
+
disabled: policy.disabled,
|
|
1562
|
+
priority: policy.priority,
|
|
1524
1563
|
permission: policy.permission,
|
|
1525
1564
|
effect: policy.effect,
|
|
1526
|
-
|
|
1527
|
-
disabled: policy.disabled,
|
|
1565
|
+
compareMethod: policy.compareMethod,
|
|
1528
1566
|
tags: policy.tags,
|
|
1567
|
+
ruleSet: policy.ruleSet.map(ruleSet => AbilityJSONParser.ruleSetToJSON(ruleSet)),
|
|
1529
1568
|
};
|
|
1530
1569
|
}
|
|
1531
1570
|
static toJSON(policies) {
|
|
@@ -2313,28 +2352,34 @@ class AbilityDSLParser {
|
|
|
2313
2352
|
*/
|
|
2314
2353
|
parseRuleSets(policyCompareMethod) {
|
|
2315
2354
|
const sets = [];
|
|
2355
|
+
this.consumeLeadingComments();
|
|
2356
|
+
this.consumeLeadingAnnotations();
|
|
2316
2357
|
while (!this.stream.eof() && !this.isStartOfPolicy()) {
|
|
2317
|
-
|
|
2318
|
-
this.consumeLeadingAnnotations();
|
|
2319
|
-
// Если начинается новая except группа — парсим её
|
|
2358
|
+
// maybe except ruleSet
|
|
2320
2359
|
if (this.isStartOfExcept()) {
|
|
2321
2360
|
sets.push(this.parseExceptGroup(policyCompareMethod));
|
|
2322
2361
|
continue;
|
|
2323
2362
|
}
|
|
2324
|
-
//
|
|
2363
|
+
// maybe ruleSet
|
|
2325
2364
|
if (this.isStartOfGroup()) {
|
|
2326
2365
|
sets.push(this.parseGroup());
|
|
2327
2366
|
continue;
|
|
2328
2367
|
}
|
|
2329
|
-
|
|
2368
|
+
// implicit ruleSet
|
|
2369
|
+
// if (!this.isStartOfRule()) {
|
|
2370
|
+
// this.consumeLeadingComments();
|
|
2371
|
+
// this.consumeLeadingAnnotations();
|
|
2372
|
+
// }
|
|
2373
|
+
// is implicit group
|
|
2374
|
+
// const annotation = this.takeAnnotations('ruleSet');
|
|
2330
2375
|
const group = new AbilityRuleSet({
|
|
2331
|
-
id: annotation.id?.value || null,
|
|
2376
|
+
// id: annotation.id?.value || null,
|
|
2332
2377
|
compareMethod: policyCompareMethod,
|
|
2333
|
-
name: annotation.name?.value ?? null,
|
|
2334
|
-
description: annotation.description?.value || null,
|
|
2335
|
-
disabled: annotation.disabled?.value ?? undefined,
|
|
2378
|
+
// name: annotation.name?.value ?? null,
|
|
2379
|
+
// description: annotation.description?.value || null,
|
|
2380
|
+
// disabled: annotation.disabled?.value ?? undefined,
|
|
2336
2381
|
});
|
|
2337
|
-
//
|
|
2382
|
+
// Read rules of implicit-группы
|
|
2338
2383
|
while (!this.stream.eof()) {
|
|
2339
2384
|
this.consumeLeadingComments();
|
|
2340
2385
|
this.consumeLeadingAnnotations();
|
|
@@ -2935,6 +2980,11 @@ class AbilityDSLParser {
|
|
|
2935
2980
|
isStartOfGroup() {
|
|
2936
2981
|
return this.stream.check(TokenTypes.ALL) || this.stream.check(TokenTypes.ANY);
|
|
2937
2982
|
}
|
|
2983
|
+
isStartOfRule() {
|
|
2984
|
+
return (this.stream.check(TokenTypes.IDENTIFIER) ||
|
|
2985
|
+
this.stream.check(TokenTypes.ALWAYS) ||
|
|
2986
|
+
this.stream.check(TokenTypes.NEVER));
|
|
2987
|
+
}
|
|
2938
2988
|
isStartOfExcept() {
|
|
2939
2989
|
return this.stream.check(TokenTypes.EXCEPT);
|
|
2940
2990
|
}
|
|
@@ -2958,17 +3008,33 @@ class AbilityStrategy {
|
|
|
2958
3008
|
matchedPolicies() {
|
|
2959
3009
|
return this.matched;
|
|
2960
3010
|
}
|
|
3011
|
+
hasMatched() {
|
|
3012
|
+
return this.matched.length > 0;
|
|
3013
|
+
}
|
|
2961
3014
|
firstMatched() {
|
|
2962
|
-
return this.
|
|
3015
|
+
return this.matchedPolicies()[0] ?? null;
|
|
2963
3016
|
}
|
|
2964
3017
|
lastMatched() {
|
|
2965
|
-
|
|
3018
|
+
const list = this.matchedPolicies();
|
|
3019
|
+
return list.length > 0 ? list[list.length - 1] : null;
|
|
3020
|
+
}
|
|
3021
|
+
firstDenied() {
|
|
3022
|
+
return this.getDenyPolicies()[0] ?? null;
|
|
3023
|
+
}
|
|
3024
|
+
firstPermitted() {
|
|
3025
|
+
return this.getPermitPolicies()[0] ?? null;
|
|
3026
|
+
}
|
|
3027
|
+
getPermitPolicies() {
|
|
3028
|
+
return this.matched.filter(p => p.effect === AbilityPolicyEffect.permit);
|
|
3029
|
+
}
|
|
3030
|
+
getDenyPolicies() {
|
|
3031
|
+
return this.matched.filter(p => p.effect === AbilityPolicyEffect.deny);
|
|
2966
3032
|
}
|
|
2967
3033
|
hasPermit() {
|
|
2968
|
-
return this.
|
|
3034
|
+
return this.getPermitPolicies().length > 0;
|
|
2969
3035
|
}
|
|
2970
3036
|
hasDeny() {
|
|
2971
|
-
return this.
|
|
3037
|
+
return this.getDenyPolicies().length > 0;
|
|
2972
3038
|
}
|
|
2973
3039
|
isAllowed() {
|
|
2974
3040
|
return this.evaluate() === AbilityPolicyEffect.permit;
|
|
@@ -2996,13 +3062,24 @@ class AbilityStrategy {
|
|
|
2996
3062
|
* Result: deny (because not all policies permitted)
|
|
2997
3063
|
*/
|
|
2998
3064
|
class AllMustPermitStrategy extends AbilityStrategy {
|
|
3065
|
+
_decisive = null;
|
|
2999
3066
|
evaluate() {
|
|
3000
|
-
|
|
3001
|
-
if (
|
|
3067
|
+
// 1. Нет совпавших политик → deny, но решающей политики нет
|
|
3068
|
+
if (!this.hasMatched()) {
|
|
3069
|
+
this._decisive = null;
|
|
3070
|
+
return AbilityPolicyEffect.deny;
|
|
3071
|
+
}
|
|
3072
|
+
// 2. Если есть deny — она решающая
|
|
3073
|
+
const deny = this.firstDenied();
|
|
3074
|
+
if (deny) {
|
|
3075
|
+
this._decisive = deny;
|
|
3002
3076
|
return AbilityPolicyEffect.deny;
|
|
3003
3077
|
}
|
|
3004
|
-
|
|
3005
|
-
return
|
|
3078
|
+
this._decisive = this.firstPermitted();
|
|
3079
|
+
return AbilityPolicyEffect.permit;
|
|
3080
|
+
}
|
|
3081
|
+
decisivePolicy() {
|
|
3082
|
+
return this._decisive;
|
|
3006
3083
|
}
|
|
3007
3084
|
}
|
|
3008
3085
|
|
|
@@ -3024,8 +3101,20 @@ class AllMustPermitStrategy extends AbilityStrategy {
|
|
|
3024
3101
|
* Result: permit (because at least one policy permitted)
|
|
3025
3102
|
*/
|
|
3026
3103
|
class AnyPermitStrategy extends AbilityStrategy {
|
|
3104
|
+
_decisive = null;
|
|
3027
3105
|
evaluate() {
|
|
3028
|
-
|
|
3106
|
+
// 1. Если есть permit — он решающий
|
|
3107
|
+
const permit = this.firstPermitted();
|
|
3108
|
+
if (permit) {
|
|
3109
|
+
this._decisive = permit;
|
|
3110
|
+
return AbilityPolicyEffect.permit;
|
|
3111
|
+
}
|
|
3112
|
+
// 2. Нет permit → deny по умолчанию
|
|
3113
|
+
this._decisive = null;
|
|
3114
|
+
return AbilityPolicyEffect.deny;
|
|
3115
|
+
}
|
|
3116
|
+
decisivePolicy() {
|
|
3117
|
+
return this._decisive;
|
|
3029
3118
|
}
|
|
3030
3119
|
}
|
|
3031
3120
|
|
|
@@ -3048,15 +3137,27 @@ class AnyPermitStrategy extends AbilityStrategy {
|
|
|
3048
3137
|
* Result: deny (because deny overrides everything)
|
|
3049
3138
|
*/
|
|
3050
3139
|
class DenyOverridesStrategy extends AbilityStrategy {
|
|
3140
|
+
_decisive = null;
|
|
3051
3141
|
evaluate() {
|
|
3052
|
-
|
|
3142
|
+
// 1. Если есть deny — он решающий
|
|
3143
|
+
const deny = this.firstDenied();
|
|
3144
|
+
if (deny) {
|
|
3145
|
+
this._decisive = deny;
|
|
3053
3146
|
return AbilityPolicyEffect.deny;
|
|
3054
3147
|
}
|
|
3055
|
-
|
|
3148
|
+
// 2. Если есть permit — он решающий
|
|
3149
|
+
const permit = this.firstPermitted();
|
|
3150
|
+
if (permit) {
|
|
3151
|
+
this._decisive = permit;
|
|
3056
3152
|
return AbilityPolicyEffect.permit;
|
|
3057
3153
|
}
|
|
3154
|
+
// 3. Нет ни permit, ни deny → deny по умолчанию
|
|
3155
|
+
this._decisive = null;
|
|
3058
3156
|
return AbilityPolicyEffect.deny;
|
|
3059
3157
|
}
|
|
3158
|
+
decisivePolicy() {
|
|
3159
|
+
return this._decisive;
|
|
3160
|
+
}
|
|
3060
3161
|
}
|
|
3061
3162
|
|
|
3062
3163
|
/**
|
|
@@ -3077,9 +3178,20 @@ class DenyOverridesStrategy extends AbilityStrategy {
|
|
|
3077
3178
|
* Result: permit (P2 is the first applicable)
|
|
3078
3179
|
*/
|
|
3079
3180
|
class FirstMatchStrategy extends AbilityStrategy {
|
|
3181
|
+
_decisive = null;
|
|
3080
3182
|
evaluate() {
|
|
3081
3183
|
const first = this.firstMatched();
|
|
3082
|
-
|
|
3184
|
+
// Если нет совпавших политик → deny по умолчанию
|
|
3185
|
+
if (!first) {
|
|
3186
|
+
this._decisive = null;
|
|
3187
|
+
return AbilityPolicyEffect.deny;
|
|
3188
|
+
}
|
|
3189
|
+
// Первая совпавшая политика — решающая
|
|
3190
|
+
this._decisive = first;
|
|
3191
|
+
return first.effect;
|
|
3192
|
+
}
|
|
3193
|
+
decisivePolicy() {
|
|
3194
|
+
return this._decisive;
|
|
3083
3195
|
}
|
|
3084
3196
|
}
|
|
3085
3197
|
|
|
@@ -3100,13 +3212,21 @@ class FirstMatchStrategy extends AbilityStrategy {
|
|
|
3100
3212
|
* Result: deny (more than one applicable policy)
|
|
3101
3213
|
*/
|
|
3102
3214
|
class OnlyOneApplicableStrategy extends AbilityStrategy {
|
|
3215
|
+
_decisive = null;
|
|
3103
3216
|
evaluate() {
|
|
3104
3217
|
const matched = this.matchedPolicies();
|
|
3218
|
+
// 1. Ровно одна совпавшая политика → она решающая
|
|
3105
3219
|
if (matched.length === 1) {
|
|
3220
|
+
this._decisive = matched[0];
|
|
3106
3221
|
return matched[0].effect;
|
|
3107
3222
|
}
|
|
3223
|
+
// 2. Иначе deny, решающей политики нет
|
|
3224
|
+
this._decisive = null;
|
|
3108
3225
|
return AbilityPolicyEffect.deny;
|
|
3109
3226
|
}
|
|
3227
|
+
decisivePolicy() {
|
|
3228
|
+
return this._decisive;
|
|
3229
|
+
}
|
|
3110
3230
|
}
|
|
3111
3231
|
|
|
3112
3232
|
/**
|
|
@@ -3128,15 +3248,27 @@ class OnlyOneApplicableStrategy extends AbilityStrategy {
|
|
|
3128
3248
|
* Result: permit (permit overrides deny)
|
|
3129
3249
|
*/
|
|
3130
3250
|
class PermitOverridesStrategy extends AbilityStrategy {
|
|
3251
|
+
_decisive = null;
|
|
3131
3252
|
evaluate() {
|
|
3132
|
-
|
|
3253
|
+
// 1. Если есть permit — он выигрывает
|
|
3254
|
+
const permit = this.matchedPolicies().find(p => p.effect === AbilityPolicyEffect.permit);
|
|
3255
|
+
if (permit) {
|
|
3256
|
+
this._decisive = permit;
|
|
3133
3257
|
return AbilityPolicyEffect.permit;
|
|
3134
3258
|
}
|
|
3135
|
-
|
|
3259
|
+
// 2. Если permit нет — ищем deny
|
|
3260
|
+
const deny = this.matchedPolicies().find(p => p.effect === AbilityPolicyEffect.deny);
|
|
3261
|
+
if (deny) {
|
|
3262
|
+
this._decisive = deny;
|
|
3136
3263
|
return AbilityPolicyEffect.deny;
|
|
3137
3264
|
}
|
|
3265
|
+
// 3. Нет ни permit, ни deny → deny по умолчанию
|
|
3266
|
+
this._decisive = null;
|
|
3138
3267
|
return AbilityPolicyEffect.deny;
|
|
3139
3268
|
}
|
|
3269
|
+
decisivePolicy() {
|
|
3270
|
+
return this._decisive;
|
|
3271
|
+
}
|
|
3140
3272
|
}
|
|
3141
3273
|
|
|
3142
3274
|
/**
|
|
@@ -3156,9 +3288,20 @@ class PermitOverridesStrategy extends AbilityStrategy {
|
|
|
3156
3288
|
* Result: permit (P3 is the last applicable)
|
|
3157
3289
|
*/
|
|
3158
3290
|
class SequentialLastMatchStrategy extends AbilityStrategy {
|
|
3291
|
+
_decisive = null;
|
|
3159
3292
|
evaluate() {
|
|
3160
3293
|
const last = this.lastMatched();
|
|
3161
|
-
|
|
3294
|
+
// Нет совпавших политик → deny по умолчанию
|
|
3295
|
+
if (!last) {
|
|
3296
|
+
this._decisive = null;
|
|
3297
|
+
return AbilityPolicyEffect.deny;
|
|
3298
|
+
}
|
|
3299
|
+
// Последняя совпавшая политика — решающая
|
|
3300
|
+
this._decisive = last;
|
|
3301
|
+
return last.effect;
|
|
3302
|
+
}
|
|
3303
|
+
decisivePolicy() {
|
|
3304
|
+
return this._decisive;
|
|
3162
3305
|
}
|
|
3163
3306
|
}
|
|
3164
3307
|
|
|
@@ -3180,13 +3323,23 @@ class SequentialLastMatchStrategy extends AbilityStrategy {
|
|
|
3180
3323
|
* Result: permit (P2 has higher priority)
|
|
3181
3324
|
*/
|
|
3182
3325
|
class PriorityStrategy extends AbilityStrategy {
|
|
3326
|
+
_decisive = null;
|
|
3183
3327
|
evaluate() {
|
|
3184
3328
|
const matched = this.matchedPolicies();
|
|
3329
|
+
// 1. Нет совпавших политик → deny, решающей политики нет
|
|
3185
3330
|
if (matched.length === 0) {
|
|
3331
|
+
this._decisive = null;
|
|
3186
3332
|
return AbilityPolicyEffect.deny;
|
|
3187
3333
|
}
|
|
3334
|
+
// 2. Сортируем по приоритету (больший приоритет — выше)
|
|
3188
3335
|
const sorted = [...matched].sort((a, b) => b.priority - a.priority);
|
|
3189
|
-
|
|
3336
|
+
// 3. Самая приоритетная политика — решающая
|
|
3337
|
+
const top = sorted[0];
|
|
3338
|
+
this._decisive = top;
|
|
3339
|
+
return top.effect;
|
|
3340
|
+
}
|
|
3341
|
+
decisivePolicy() {
|
|
3342
|
+
return this._decisive;
|
|
3190
3343
|
}
|
|
3191
3344
|
}
|
|
3192
3345
|
|