@via-profit/ability 3.6.2 → 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 +51 -3
- package/dist/index.js +156 -36
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -218,14 +218,14 @@ type AbilityExplainConfig = {
|
|
|
218
218
|
readonly type: AbilityExplainType;
|
|
219
219
|
readonly name: string;
|
|
220
220
|
readonly match: AbilityMatchType;
|
|
221
|
-
readonly debugInfo?:
|
|
221
|
+
readonly debugInfo?: string;
|
|
222
222
|
};
|
|
223
223
|
declare class AbilityExplain {
|
|
224
224
|
readonly type: AbilityExplainType;
|
|
225
225
|
readonly children: AbilityExplain[];
|
|
226
226
|
readonly name: string;
|
|
227
227
|
readonly match: AbilityMatchType;
|
|
228
|
-
readonly debugInfo?:
|
|
228
|
+
readonly debugInfo?: string;
|
|
229
229
|
constructor(config: AbilityExplainConfig, children?: AbilityExplain[]);
|
|
230
230
|
toString(indentPrefix?: string, isLast?: boolean): string;
|
|
231
231
|
}
|
|
@@ -396,10 +396,40 @@ declare abstract class AbilityStrategy<Resource extends ResourceObject = Record<
|
|
|
396
396
|
readonly policies: readonly AbilityPolicy<Resource, Environment>[];
|
|
397
397
|
private readonly matched;
|
|
398
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
|
+
*/
|
|
399
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;
|
|
400
425
|
matchedPolicies(): readonly AbilityPolicy<Resource, Environment, string>[];
|
|
426
|
+
hasMatched(): boolean;
|
|
401
427
|
protected firstMatched(): AbilityPolicy<Resource, Environment> | null;
|
|
402
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>[];
|
|
403
433
|
protected hasPermit(): boolean;
|
|
404
434
|
protected hasDeny(): boolean;
|
|
405
435
|
isAllowed(): boolean;
|
|
@@ -416,7 +446,9 @@ declare class AbilityResult<R extends ResourceObject = Record<string, unknown>,
|
|
|
416
446
|
*
|
|
417
447
|
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
418
448
|
*/
|
|
419
|
-
explain():
|
|
449
|
+
explain(): string;
|
|
450
|
+
decisive(): AbilityPolicy<R, E, string> | null;
|
|
451
|
+
explainDecisive(): string | null;
|
|
420
452
|
isAllowed: () => boolean;
|
|
421
453
|
isDenied: () => boolean;
|
|
422
454
|
}
|
|
@@ -659,7 +691,9 @@ declare function ability<R extends ResourceObject = Record<string, unknown>, E e
|
|
|
659
691
|
* Result: deny (because not all policies permitted)
|
|
660
692
|
*/
|
|
661
693
|
declare class AllMustPermitStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
694
|
+
private _decisive;
|
|
662
695
|
evaluate(): AbilityPolicyEffectType;
|
|
696
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
663
697
|
}
|
|
664
698
|
|
|
665
699
|
/**
|
|
@@ -680,7 +714,9 @@ declare class AllMustPermitStrategy<R extends ResourceObject, E extends Environm
|
|
|
680
714
|
* Result: permit (because at least one policy permitted)
|
|
681
715
|
*/
|
|
682
716
|
declare class AnyPermitStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
717
|
+
private _decisive;
|
|
683
718
|
evaluate(): AbilityPolicyEffectType;
|
|
719
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
684
720
|
}
|
|
685
721
|
|
|
686
722
|
/**
|
|
@@ -702,7 +738,9 @@ declare class AnyPermitStrategy<R extends ResourceObject, E extends EnvironmentO
|
|
|
702
738
|
* Result: deny (because deny overrides everything)
|
|
703
739
|
*/
|
|
704
740
|
declare class DenyOverridesStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
741
|
+
private _decisive;
|
|
705
742
|
evaluate(): AbilityPolicyEffectType;
|
|
743
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
706
744
|
}
|
|
707
745
|
|
|
708
746
|
/**
|
|
@@ -723,7 +761,9 @@ declare class DenyOverridesStrategy<R extends ResourceObject, E extends Environm
|
|
|
723
761
|
* Result: permit (P2 is the first applicable)
|
|
724
762
|
*/
|
|
725
763
|
declare class FirstMatchStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
764
|
+
private _decisive;
|
|
726
765
|
evaluate(): AbilityPolicyEffectType;
|
|
766
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
727
767
|
}
|
|
728
768
|
|
|
729
769
|
/**
|
|
@@ -743,7 +783,9 @@ declare class FirstMatchStrategy<R extends ResourceObject, E extends Environment
|
|
|
743
783
|
* Result: deny (more than one applicable policy)
|
|
744
784
|
*/
|
|
745
785
|
declare class OnlyOneApplicableStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
786
|
+
private _decisive;
|
|
746
787
|
evaluate(): AbilityPolicyEffectType;
|
|
788
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
747
789
|
}
|
|
748
790
|
|
|
749
791
|
/**
|
|
@@ -765,7 +807,9 @@ declare class OnlyOneApplicableStrategy<R extends ResourceObject, E extends Envi
|
|
|
765
807
|
* Result: permit (permit overrides deny)
|
|
766
808
|
*/
|
|
767
809
|
declare class PermitOverridesStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
810
|
+
private _decisive;
|
|
768
811
|
evaluate(): AbilityPolicyEffectType;
|
|
812
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
769
813
|
}
|
|
770
814
|
|
|
771
815
|
/**
|
|
@@ -785,7 +829,9 @@ declare class PermitOverridesStrategy<R extends ResourceObject, E extends Enviro
|
|
|
785
829
|
* Result: permit (P3 is the last applicable)
|
|
786
830
|
*/
|
|
787
831
|
declare class SequentialLastMatchStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
832
|
+
private _decisive;
|
|
788
833
|
evaluate(): AbilityPolicyEffectType;
|
|
834
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
789
835
|
}
|
|
790
836
|
|
|
791
837
|
/**
|
|
@@ -806,7 +852,9 @@ declare class SequentialLastMatchStrategy<R extends ResourceObject, E extends En
|
|
|
806
852
|
* Result: permit (P2 has higher priority)
|
|
807
853
|
*/
|
|
808
854
|
declare class PriorityStrategy<R extends ResourceObject, E extends EnvironmentObject = Record<string, unknown>> extends AbilityStrategy<R, E> {
|
|
855
|
+
private _decisive;
|
|
809
856
|
evaluate(): AbilityPolicyEffectType;
|
|
857
|
+
decisivePolicy(): AbilityPolicy<R, E, string> | null;
|
|
810
858
|
}
|
|
811
859
|
|
|
812
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
|
@@ -151,24 +151,28 @@ class AbilityExplain {
|
|
|
151
151
|
toString(indentPrefix = '', isLast = true) {
|
|
152
152
|
const isMatch = this.match === AbilityMatch.match;
|
|
153
153
|
const mark = isMatch ? `${colors.green}✓${colors.reset}` : `${colors.red}✗${colors.reset}`;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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}`;
|
|
164
|
+
}
|
|
159
165
|
const branch = indentPrefix.length === 0
|
|
160
166
|
? ''
|
|
161
167
|
: isLast
|
|
162
168
|
? `${colors.gray}└─${colors.reset} `
|
|
163
169
|
: `${colors.gray}├─${colors.reset} `;
|
|
164
170
|
let out = `${indentPrefix}${branch}${label} ${this.name} — ${mark}`;
|
|
165
|
-
if (this.debugInfo)
|
|
171
|
+
if (this.debugInfo)
|
|
166
172
|
out += ` ${colors.gray}(${this.debugInfo})${colors.reset}`;
|
|
167
|
-
}
|
|
168
173
|
const nextIndent = indentPrefix + (isLast ? ' ' : `${colors.gray}│ ${colors.reset}`);
|
|
169
|
-
this.children.forEach((child,
|
|
170
|
-
|
|
171
|
-
out += '\n' + child.toString(nextIndent, last);
|
|
174
|
+
this.children.forEach((child, idx) => {
|
|
175
|
+
out += '\n' + child.toString(nextIndent, idx === this.children.length - 1);
|
|
172
176
|
});
|
|
173
177
|
return out;
|
|
174
178
|
}
|
|
@@ -217,9 +221,21 @@ class AbilityResult {
|
|
|
217
221
|
* Useful for debugging, logging, or building UI tools that visualize permission logic.
|
|
218
222
|
*/
|
|
219
223
|
explain() {
|
|
220
|
-
return this.strategy.policies
|
|
221
|
-
|
|
222
|
-
|
|
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();
|
|
223
239
|
}
|
|
224
240
|
isAllowed = () => {
|
|
225
241
|
return this.strategy.isAllowed();
|
|
@@ -309,24 +325,25 @@ class AbilityResolver {
|
|
|
309
325
|
.toLowerCase(); // optional: make case-insensitive
|
|
310
326
|
}
|
|
311
327
|
static matchPermissions(policySegments, inputSegments) {
|
|
312
|
-
|
|
313
|
-
for (
|
|
328
|
+
let i = 0;
|
|
329
|
+
for (; i < policySegments.length; i++) {
|
|
314
330
|
const pSeg = policySegments[i];
|
|
315
331
|
const iSeg = inputSegments[i];
|
|
316
|
-
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
332
|
+
// '*' — глобальный wildcard: матчим всё, что дальше
|
|
319
333
|
if (pSeg === '*') {
|
|
320
|
-
|
|
334
|
+
return true;
|
|
321
335
|
}
|
|
336
|
+
// input закончился раньше — mismatch
|
|
322
337
|
if (iSeg === undefined) {
|
|
323
338
|
return false;
|
|
324
339
|
}
|
|
340
|
+
// обычное сравнение
|
|
325
341
|
if (pSeg !== iSeg) {
|
|
326
342
|
return false;
|
|
327
343
|
}
|
|
328
344
|
}
|
|
329
|
-
|
|
345
|
+
// Если политика закончилась, но input длиннее — match только если последний сегмент был '*'
|
|
346
|
+
return i === inputSegments.length;
|
|
330
347
|
}
|
|
331
348
|
}
|
|
332
349
|
|
|
@@ -2991,17 +3008,33 @@ class AbilityStrategy {
|
|
|
2991
3008
|
matchedPolicies() {
|
|
2992
3009
|
return this.matched;
|
|
2993
3010
|
}
|
|
3011
|
+
hasMatched() {
|
|
3012
|
+
return this.matched.length > 0;
|
|
3013
|
+
}
|
|
2994
3014
|
firstMatched() {
|
|
2995
|
-
return this.
|
|
3015
|
+
return this.matchedPolicies()[0] ?? null;
|
|
2996
3016
|
}
|
|
2997
3017
|
lastMatched() {
|
|
2998
|
-
|
|
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);
|
|
2999
3032
|
}
|
|
3000
3033
|
hasPermit() {
|
|
3001
|
-
return this.
|
|
3034
|
+
return this.getPermitPolicies().length > 0;
|
|
3002
3035
|
}
|
|
3003
3036
|
hasDeny() {
|
|
3004
|
-
return this.
|
|
3037
|
+
return this.getDenyPolicies().length > 0;
|
|
3005
3038
|
}
|
|
3006
3039
|
isAllowed() {
|
|
3007
3040
|
return this.evaluate() === AbilityPolicyEffect.permit;
|
|
@@ -3029,13 +3062,24 @@ class AbilityStrategy {
|
|
|
3029
3062
|
* Result: deny (because not all policies permitted)
|
|
3030
3063
|
*/
|
|
3031
3064
|
class AllMustPermitStrategy extends AbilityStrategy {
|
|
3065
|
+
_decisive = null;
|
|
3032
3066
|
evaluate() {
|
|
3033
|
-
|
|
3034
|
-
if (
|
|
3067
|
+
// 1. Нет совпавших политик → deny, но решающей политики нет
|
|
3068
|
+
if (!this.hasMatched()) {
|
|
3069
|
+
this._decisive = null;
|
|
3035
3070
|
return AbilityPolicyEffect.deny;
|
|
3036
3071
|
}
|
|
3037
|
-
|
|
3038
|
-
|
|
3072
|
+
// 2. Если есть deny — она решающая
|
|
3073
|
+
const deny = this.firstDenied();
|
|
3074
|
+
if (deny) {
|
|
3075
|
+
this._decisive = deny;
|
|
3076
|
+
return AbilityPolicyEffect.deny;
|
|
3077
|
+
}
|
|
3078
|
+
this._decisive = this.firstPermitted();
|
|
3079
|
+
return AbilityPolicyEffect.permit;
|
|
3080
|
+
}
|
|
3081
|
+
decisivePolicy() {
|
|
3082
|
+
return this._decisive;
|
|
3039
3083
|
}
|
|
3040
3084
|
}
|
|
3041
3085
|
|
|
@@ -3057,8 +3101,20 @@ class AllMustPermitStrategy extends AbilityStrategy {
|
|
|
3057
3101
|
* Result: permit (because at least one policy permitted)
|
|
3058
3102
|
*/
|
|
3059
3103
|
class AnyPermitStrategy extends AbilityStrategy {
|
|
3104
|
+
_decisive = null;
|
|
3060
3105
|
evaluate() {
|
|
3061
|
-
|
|
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;
|
|
3062
3118
|
}
|
|
3063
3119
|
}
|
|
3064
3120
|
|
|
@@ -3081,15 +3137,27 @@ class AnyPermitStrategy extends AbilityStrategy {
|
|
|
3081
3137
|
* Result: deny (because deny overrides everything)
|
|
3082
3138
|
*/
|
|
3083
3139
|
class DenyOverridesStrategy extends AbilityStrategy {
|
|
3140
|
+
_decisive = null;
|
|
3084
3141
|
evaluate() {
|
|
3085
|
-
|
|
3142
|
+
// 1. Если есть deny — он решающий
|
|
3143
|
+
const deny = this.firstDenied();
|
|
3144
|
+
if (deny) {
|
|
3145
|
+
this._decisive = deny;
|
|
3086
3146
|
return AbilityPolicyEffect.deny;
|
|
3087
3147
|
}
|
|
3088
|
-
|
|
3148
|
+
// 2. Если есть permit — он решающий
|
|
3149
|
+
const permit = this.firstPermitted();
|
|
3150
|
+
if (permit) {
|
|
3151
|
+
this._decisive = permit;
|
|
3089
3152
|
return AbilityPolicyEffect.permit;
|
|
3090
3153
|
}
|
|
3154
|
+
// 3. Нет ни permit, ни deny → deny по умолчанию
|
|
3155
|
+
this._decisive = null;
|
|
3091
3156
|
return AbilityPolicyEffect.deny;
|
|
3092
3157
|
}
|
|
3158
|
+
decisivePolicy() {
|
|
3159
|
+
return this._decisive;
|
|
3160
|
+
}
|
|
3093
3161
|
}
|
|
3094
3162
|
|
|
3095
3163
|
/**
|
|
@@ -3110,9 +3178,20 @@ class DenyOverridesStrategy extends AbilityStrategy {
|
|
|
3110
3178
|
* Result: permit (P2 is the first applicable)
|
|
3111
3179
|
*/
|
|
3112
3180
|
class FirstMatchStrategy extends AbilityStrategy {
|
|
3181
|
+
_decisive = null;
|
|
3113
3182
|
evaluate() {
|
|
3114
3183
|
const first = this.firstMatched();
|
|
3115
|
-
|
|
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;
|
|
3116
3195
|
}
|
|
3117
3196
|
}
|
|
3118
3197
|
|
|
@@ -3133,13 +3212,21 @@ class FirstMatchStrategy extends AbilityStrategy {
|
|
|
3133
3212
|
* Result: deny (more than one applicable policy)
|
|
3134
3213
|
*/
|
|
3135
3214
|
class OnlyOneApplicableStrategy extends AbilityStrategy {
|
|
3215
|
+
_decisive = null;
|
|
3136
3216
|
evaluate() {
|
|
3137
3217
|
const matched = this.matchedPolicies();
|
|
3218
|
+
// 1. Ровно одна совпавшая политика → она решающая
|
|
3138
3219
|
if (matched.length === 1) {
|
|
3220
|
+
this._decisive = matched[0];
|
|
3139
3221
|
return matched[0].effect;
|
|
3140
3222
|
}
|
|
3223
|
+
// 2. Иначе deny, решающей политики нет
|
|
3224
|
+
this._decisive = null;
|
|
3141
3225
|
return AbilityPolicyEffect.deny;
|
|
3142
3226
|
}
|
|
3227
|
+
decisivePolicy() {
|
|
3228
|
+
return this._decisive;
|
|
3229
|
+
}
|
|
3143
3230
|
}
|
|
3144
3231
|
|
|
3145
3232
|
/**
|
|
@@ -3161,15 +3248,27 @@ class OnlyOneApplicableStrategy extends AbilityStrategy {
|
|
|
3161
3248
|
* Result: permit (permit overrides deny)
|
|
3162
3249
|
*/
|
|
3163
3250
|
class PermitOverridesStrategy extends AbilityStrategy {
|
|
3251
|
+
_decisive = null;
|
|
3164
3252
|
evaluate() {
|
|
3165
|
-
|
|
3253
|
+
// 1. Если есть permit — он выигрывает
|
|
3254
|
+
const permit = this.matchedPolicies().find(p => p.effect === AbilityPolicyEffect.permit);
|
|
3255
|
+
if (permit) {
|
|
3256
|
+
this._decisive = permit;
|
|
3166
3257
|
return AbilityPolicyEffect.permit;
|
|
3167
3258
|
}
|
|
3168
|
-
|
|
3259
|
+
// 2. Если permit нет — ищем deny
|
|
3260
|
+
const deny = this.matchedPolicies().find(p => p.effect === AbilityPolicyEffect.deny);
|
|
3261
|
+
if (deny) {
|
|
3262
|
+
this._decisive = deny;
|
|
3169
3263
|
return AbilityPolicyEffect.deny;
|
|
3170
3264
|
}
|
|
3265
|
+
// 3. Нет ни permit, ни deny → deny по умолчанию
|
|
3266
|
+
this._decisive = null;
|
|
3171
3267
|
return AbilityPolicyEffect.deny;
|
|
3172
3268
|
}
|
|
3269
|
+
decisivePolicy() {
|
|
3270
|
+
return this._decisive;
|
|
3271
|
+
}
|
|
3173
3272
|
}
|
|
3174
3273
|
|
|
3175
3274
|
/**
|
|
@@ -3189,9 +3288,20 @@ class PermitOverridesStrategy extends AbilityStrategy {
|
|
|
3189
3288
|
* Result: permit (P3 is the last applicable)
|
|
3190
3289
|
*/
|
|
3191
3290
|
class SequentialLastMatchStrategy extends AbilityStrategy {
|
|
3291
|
+
_decisive = null;
|
|
3192
3292
|
evaluate() {
|
|
3193
3293
|
const last = this.lastMatched();
|
|
3194
|
-
|
|
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;
|
|
3195
3305
|
}
|
|
3196
3306
|
}
|
|
3197
3307
|
|
|
@@ -3213,13 +3323,23 @@ class SequentialLastMatchStrategy extends AbilityStrategy {
|
|
|
3213
3323
|
* Result: permit (P2 has higher priority)
|
|
3214
3324
|
*/
|
|
3215
3325
|
class PriorityStrategy extends AbilityStrategy {
|
|
3326
|
+
_decisive = null;
|
|
3216
3327
|
evaluate() {
|
|
3217
3328
|
const matched = this.matchedPolicies();
|
|
3329
|
+
// 1. Нет совпавших политик → deny, решающей политики нет
|
|
3218
3330
|
if (matched.length === 0) {
|
|
3331
|
+
this._decisive = null;
|
|
3219
3332
|
return AbilityPolicyEffect.deny;
|
|
3220
3333
|
}
|
|
3334
|
+
// 2. Сортируем по приоритету (больший приоритет — выше)
|
|
3221
3335
|
const sorted = [...matched].sort((a, b) => b.priority - a.priority);
|
|
3222
|
-
|
|
3336
|
+
// 3. Самая приоритетная политика — решающая
|
|
3337
|
+
const top = sorted[0];
|
|
3338
|
+
this._decisive = top;
|
|
3339
|
+
return top.effect;
|
|
3340
|
+
}
|
|
3341
|
+
decisivePolicy() {
|
|
3342
|
+
return this._decisive;
|
|
3223
3343
|
}
|
|
3224
3344
|
}
|
|
3225
3345
|
|