@authup/access 1.0.0-beta.28 → 1.0.0-beta.29
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.mjs +693 -499
- package/dist/index.mjs.map +1 -1
- package/dist/permission/checker/index.d.ts +3 -0
- package/dist/permission/checker/index.d.ts.map +1 -0
- package/dist/permission/{module.d.ts → checker/module.d.ts} +8 -7
- package/dist/permission/checker/module.d.ts.map +1 -0
- package/dist/permission/checker/types.d.ts +26 -0
- package/dist/permission/checker/types.d.ts.map +1 -0
- package/dist/permission/error/index.d.ts +0 -1
- package/dist/permission/error/index.d.ts.map +1 -1
- package/dist/permission/error/module.d.ts +4 -5
- package/dist/permission/error/module.d.ts.map +1 -1
- package/dist/permission/index.d.ts +2 -2
- package/dist/permission/index.d.ts.map +1 -1
- package/dist/permission/repository/index.d.ts.map +1 -0
- package/dist/permission/repository/memory.d.ts +9 -0
- package/dist/permission/repository/memory.d.ts.map +1 -0
- package/dist/permission/{provider → repository}/types.d.ts +2 -2
- package/dist/permission/repository/types.d.ts.map +1 -0
- package/dist/permission/types.d.ts +1 -19
- package/dist/permission/types.d.ts.map +1 -1
- package/dist/policy/built-in/attribute-names/evaluator.d.ts +5 -8
- package/dist/policy/built-in/attribute-names/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/attributes/evaluator.d.ts +4 -8
- package/dist/policy/built-in/attributes/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/attributes/validator.d.ts.map +1 -1
- package/dist/policy/built-in/composite/evaluator.d.ts +3 -8
- package/dist/policy/built-in/composite/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/composite/validator.d.ts.map +1 -1
- package/dist/policy/built-in/date/evaluator.d.ts +3 -8
- package/dist/policy/built-in/date/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/identity/data/index.d.ts +2 -0
- package/dist/policy/built-in/identity/data/index.d.ts.map +1 -0
- package/dist/policy/built-in/identity/data/validator.d.ts +6 -0
- package/dist/policy/built-in/identity/data/validator.d.ts.map +1 -0
- package/dist/policy/built-in/identity/evaluator.d.ts +7 -8
- package/dist/policy/built-in/identity/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/identity/types.d.ts +22 -0
- package/dist/policy/built-in/identity/types.d.ts.map +1 -1
- package/dist/policy/built-in/permission-binding/module.d.ts +3 -8
- package/dist/policy/built-in/permission-binding/module.d.ts.map +1 -1
- package/dist/policy/built-in/realm-match/evaluator.d.ts +7 -8
- package/dist/policy/built-in/realm-match/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/time/evaluator.d.ts +3 -8
- package/dist/policy/built-in/time/evaluator.d.ts.map +1 -1
- package/dist/policy/constants.d.ts +12 -0
- package/dist/policy/constants.d.ts.map +1 -0
- package/dist/policy/data.d.ts +20 -0
- package/dist/policy/data.d.ts.map +1 -0
- package/dist/policy/engine/module.d.ts +9 -9
- package/dist/policy/engine/module.d.ts.map +1 -1
- package/dist/policy/engine/types.d.ts +6 -3
- package/dist/policy/engine/types.d.ts.map +1 -1
- package/dist/policy/error/module.d.ts +3 -0
- package/dist/policy/error/module.d.ts.map +1 -1
- package/dist/policy/evaluation/context/define.d.ts +3 -0
- package/dist/policy/evaluation/context/define.d.ts.map +1 -0
- package/dist/policy/evaluation/context/index.d.ts +3 -0
- package/dist/policy/evaluation/context/index.d.ts.map +1 -0
- package/dist/policy/evaluation/context/types.d.ts +11 -0
- package/dist/policy/evaluation/context/types.d.ts.map +1 -0
- package/dist/policy/evaluation/index.d.ts +3 -0
- package/dist/policy/evaluation/index.d.ts.map +1 -0
- package/dist/policy/evaluation/types.d.ts +14 -0
- package/dist/policy/evaluation/types.d.ts.map +1 -0
- package/dist/policy/index.d.ts +4 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/issue/constants.d.ts +21 -0
- package/dist/policy/issue/constants.d.ts.map +1 -0
- package/dist/policy/issue/define.d.ts +4 -0
- package/dist/policy/issue/define.d.ts.map +1 -0
- package/dist/policy/issue/index.d.ts +4 -0
- package/dist/policy/issue/index.d.ts.map +1 -0
- package/dist/policy/issue/types.d.ts +5 -0
- package/dist/policy/issue/types.d.ts.map +1 -0
- package/dist/policy/types.d.ts +3 -41
- package/dist/policy/types.d.ts.map +1 -1
- package/package.json +13 -12
- package/dist/permission/error/types.d.ts +0 -11
- package/dist/permission/error/types.d.ts.map +0 -1
- package/dist/permission/module.d.ts.map +0 -1
- package/dist/permission/provider/index.d.ts.map +0 -1
- package/dist/permission/provider/memory.d.ts +0 -9
- package/dist/permission/provider/memory.d.ts.map +0 -1
- package/dist/permission/provider/types.d.ts.map +0 -1
- package/dist/policy/evaluator/data/identity/index.d.ts +0 -2
- package/dist/policy/evaluator/data/identity/index.d.ts.map +0 -1
- package/dist/policy/evaluator/data/identity/validator.d.ts +0 -6
- package/dist/policy/evaluator/data/identity/validator.d.ts.map +0 -1
- package/dist/policy/evaluator/data/index.d.ts +0 -4
- package/dist/policy/evaluator/data/index.d.ts.map +0 -1
- package/dist/policy/evaluator/data/permission/index.d.ts +0 -2
- package/dist/policy/evaluator/data/permission/index.d.ts.map +0 -1
- package/dist/policy/evaluator/data/permission/validator.d.ts +0 -6
- package/dist/policy/evaluator/data/permission/validator.d.ts.map +0 -1
- package/dist/policy/evaluator/data/validator.d.ts +0 -6
- package/dist/policy/evaluator/data/validator.d.ts.map +0 -1
- package/dist/policy/evaluator/evaluate.d.ts +0 -4
- package/dist/policy/evaluator/evaluate.d.ts.map +0 -1
- package/dist/policy/evaluator/index.d.ts +0 -4
- package/dist/policy/evaluator/index.d.ts.map +0 -1
- package/dist/policy/evaluator/types.d.ts +0 -44
- package/dist/policy/evaluator/types.d.ts.map +0 -1
- /package/dist/permission/{provider → repository}/index.d.ts +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,119 @@
|
|
|
1
1
|
import { AuthupError, ErrorCode } from '@authup/errors';
|
|
2
|
+
import { defineIssueItem, defineIssueGroup, Container } from 'validup';
|
|
2
3
|
import { isObject, flattenObject, hasOwnProperty } from '@authup/kit';
|
|
3
4
|
import { createValidator } from '@validup/adapter-zod';
|
|
4
|
-
import { Container } from 'validup';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { guard } from '@ucast/mongo2js';
|
|
7
7
|
|
|
8
|
+
class PermissionError extends AuthupError {
|
|
9
|
+
constructor(options = {}){
|
|
10
|
+
super({
|
|
11
|
+
...options,
|
|
12
|
+
message: options.message || 'A permission error occurred.',
|
|
13
|
+
statusCode: options.statusCode || 403
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
addIssue(data) {
|
|
17
|
+
this.issues.push(data);
|
|
18
|
+
}
|
|
19
|
+
addIssues(data) {
|
|
20
|
+
this.issues.push(...data);
|
|
21
|
+
}
|
|
22
|
+
static notFound(name) {
|
|
23
|
+
return new PermissionError({
|
|
24
|
+
message: `The permission ${name} was not found.`,
|
|
25
|
+
code: ErrorCode.PERMISSION_NOT_FOUND
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
static denied(name) {
|
|
29
|
+
return new PermissionError({
|
|
30
|
+
message: `The permission ${name} has not been granted.`,
|
|
31
|
+
code: ErrorCode.PERMISSION_DENIED
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
static deniedAll(names) {
|
|
35
|
+
return new PermissionError({
|
|
36
|
+
message: `None of the permissions ${names.join(', ')} has been granted.`,
|
|
37
|
+
code: ErrorCode.PERMISSION_DENIED
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
static evaluationFailed(name) {
|
|
41
|
+
if (Array.isArray(name)) {
|
|
42
|
+
return new PermissionError({
|
|
43
|
+
message: `The evaluation of permissions ${name.join(', ')} failed`,
|
|
44
|
+
code: ErrorCode.PERMISSION_EVALUATION_FAILED
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return new PermissionError({
|
|
48
|
+
message: `The evaluation of permission ${name} failed`,
|
|
49
|
+
code: ErrorCode.PERMISSION_EVALUATION_FAILED
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/*
|
|
55
|
+
* Copyright (c) 2025.
|
|
56
|
+
* Author Peter Placzek (tada5hi)
|
|
57
|
+
* For the full copyright and license information,
|
|
58
|
+
* view the LICENSE file that was distributed with this source code.
|
|
59
|
+
*/ function buildPermissionItemKey(input) {
|
|
60
|
+
return `${input.realmId || '_'}/${input.clientId || '_'}/${input.name}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* Copyright (c) 2024-2024.
|
|
65
|
+
* Author Peter Placzek (tada5hi)
|
|
66
|
+
* For the full copyright and license information,
|
|
67
|
+
* view the LICENSE file that was distributed with this source code.
|
|
68
|
+
*/ function isPermissionItemEqual(a, b) {
|
|
69
|
+
if (a.name !== b.name) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
if (typeof a.realmId === 'string' || typeof b.realmId === 'string') {
|
|
73
|
+
return a.realmId === b.realmId;
|
|
74
|
+
}
|
|
75
|
+
if (typeof a.clientId === 'string' || typeof b.clientId === 'string') {
|
|
76
|
+
return a.clientId === b.clientId;
|
|
77
|
+
}
|
|
78
|
+
return !!a.realmId === !!b.realmId && !!a.clientId === !!b.clientId;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* Copyright (c) 2024-2024.
|
|
83
|
+
* Author Peter Placzek (tada5hi)
|
|
84
|
+
* For the full copyright and license information,
|
|
85
|
+
* view the LICENSE file that was distributed with this source code.
|
|
86
|
+
*/ var DecisionStrategy = /*#__PURE__*/ function(DecisionStrategy) {
|
|
87
|
+
/**
|
|
88
|
+
* One or more positive
|
|
89
|
+
*/ DecisionStrategy["AFFIRMATIVE"] = "affirmative";
|
|
90
|
+
/**
|
|
91
|
+
* All positive
|
|
92
|
+
*/ DecisionStrategy["UNANIMOUS"] = "unanimous";
|
|
93
|
+
/**
|
|
94
|
+
* More positive than negative
|
|
95
|
+
*/ DecisionStrategy["CONSENSUS"] = "consensus";
|
|
96
|
+
return DecisionStrategy;
|
|
97
|
+
}({});
|
|
98
|
+
|
|
99
|
+
/*
|
|
100
|
+
* Copyright (c) 2024-2024.
|
|
101
|
+
* Author Peter Placzek (tada5hi)
|
|
102
|
+
* For the full copyright and license information,
|
|
103
|
+
* view the LICENSE file that was distributed with this source code.
|
|
104
|
+
*/ function maybeInvertPolicyOutcome(outcome, invert) {
|
|
105
|
+
if (typeof invert === 'undefined') {
|
|
106
|
+
return outcome;
|
|
107
|
+
}
|
|
108
|
+
if (typeof invert === 'function') {
|
|
109
|
+
return invert(outcome);
|
|
110
|
+
}
|
|
111
|
+
if (!invert) {
|
|
112
|
+
return outcome;
|
|
113
|
+
}
|
|
114
|
+
return !outcome;
|
|
115
|
+
}
|
|
116
|
+
|
|
8
117
|
class PolicyError extends AuthupError {
|
|
9
118
|
constructor(message, code){
|
|
10
119
|
super({
|
|
@@ -12,6 +121,12 @@ class PolicyError extends AuthupError {
|
|
|
12
121
|
code
|
|
13
122
|
});
|
|
14
123
|
}
|
|
124
|
+
addIssue(data) {
|
|
125
|
+
this.issues.push(data);
|
|
126
|
+
}
|
|
127
|
+
addIssues(data) {
|
|
128
|
+
this.issues.push(...data);
|
|
129
|
+
}
|
|
15
130
|
static evaluatorNotFound(type) {
|
|
16
131
|
return new PolicyError(`No evaluator is registered to handle the policy: ${type}`, ErrorCode.POLICY_EVALUATOR_NOT_FOUND);
|
|
17
132
|
}
|
|
@@ -23,22 +138,200 @@ class PolicyError extends AuthupError {
|
|
|
23
138
|
}
|
|
24
139
|
}
|
|
25
140
|
|
|
141
|
+
/**
|
|
142
|
+
* The policy engine is a component that interprets defined policies and makes decisions
|
|
143
|
+
* on whether to allow or deny a particular access.
|
|
144
|
+
*/ class PolicyEngine {
|
|
145
|
+
evaluators;
|
|
146
|
+
constructor(evaluators = {}){
|
|
147
|
+
this.evaluators = evaluators;
|
|
148
|
+
}
|
|
149
|
+
registerEvaluator(type, evaluator) {
|
|
150
|
+
this.evaluators[type] = evaluator;
|
|
151
|
+
}
|
|
152
|
+
registerEvaluators(evaluators) {
|
|
153
|
+
const keys = Object.keys(evaluators);
|
|
154
|
+
for(let i = 0; i < keys.length; i++){
|
|
155
|
+
this.registerEvaluator(keys[i], evaluators[keys[i]]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* @param policy
|
|
160
|
+
* @param ctx
|
|
161
|
+
*/ async evaluate(policy, ctx) {
|
|
162
|
+
if (ctx.exclude && ctx.exclude.length > 0 && ctx.exclude.indexOf(policy.type) !== -1) {
|
|
163
|
+
return {
|
|
164
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (ctx.include && ctx.include.length > 0 && ctx.include.indexOf(policy.type) === -1) {
|
|
168
|
+
return {
|
|
169
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const issues = [];
|
|
173
|
+
const evaluator = this.evaluators[policy.type];
|
|
174
|
+
if (!evaluator) {
|
|
175
|
+
// todo: add issue here instead + return false ?
|
|
176
|
+
issues.push(defineIssueItem({
|
|
177
|
+
path: [
|
|
178
|
+
policy.type
|
|
179
|
+
],
|
|
180
|
+
message: `The policy ${policy.type} can not be handled by any evaluator.`,
|
|
181
|
+
code: ErrorCode.POLICY_EVALUATOR_NOT_FOUND
|
|
182
|
+
}));
|
|
183
|
+
return {
|
|
184
|
+
success: maybeInvertPolicyOutcome(false, policy.invert),
|
|
185
|
+
issues
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
return await evaluator.evaluate(policy, {
|
|
190
|
+
...ctx,
|
|
191
|
+
evaluators: {
|
|
192
|
+
...this.evaluators,
|
|
193
|
+
...ctx.evaluators || {}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
} catch (e) {
|
|
197
|
+
issues.push(defineIssueItem({
|
|
198
|
+
path: [
|
|
199
|
+
policy.type
|
|
200
|
+
],
|
|
201
|
+
message: `The ${policy.type} evaluator can not process the policy specification.`,
|
|
202
|
+
code: ErrorCode.POLICY_EVALUATOR_NOT_PROCESSABLE
|
|
203
|
+
}));
|
|
204
|
+
return {
|
|
205
|
+
success: maybeInvertPolicyOutcome(false, policy.invert),
|
|
206
|
+
issues
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
async evaluateOrFail(policy, ctx) {
|
|
211
|
+
const issues = [];
|
|
212
|
+
try {
|
|
213
|
+
const outcome = await this.evaluate(policy, ctx);
|
|
214
|
+
if (outcome.success) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
if (outcome.issues) {
|
|
218
|
+
issues.push(...outcome.issues);
|
|
219
|
+
}
|
|
220
|
+
} catch (e) {
|
|
221
|
+
if (e instanceof PolicyError) {
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
const error = new PolicyError(`The policy ${policy.type} evaluation failed.`);
|
|
226
|
+
error.addIssues(issues);
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
26
231
|
/*
|
|
27
|
-
* Copyright (c)
|
|
232
|
+
* Copyright (c) 2026.
|
|
28
233
|
* Author Peter Placzek (tada5hi)
|
|
29
234
|
* For the full copyright and license information,
|
|
30
235
|
* view the LICENSE file that was distributed with this source code.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
|
|
236
|
+
*/ class PolicyData {
|
|
237
|
+
data;
|
|
238
|
+
validated;
|
|
239
|
+
constructor(data = {}, validated = new Set()){
|
|
240
|
+
this.data = data;
|
|
241
|
+
this.validated = validated;
|
|
242
|
+
}
|
|
243
|
+
set(key, value) {
|
|
244
|
+
this.data[key] = value;
|
|
245
|
+
this.validated.delete(key);
|
|
246
|
+
}
|
|
247
|
+
get(key) {
|
|
248
|
+
if (key in this.data) {
|
|
249
|
+
return this.data[key];
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`Policy data item ${key} does not exist.`);
|
|
34
252
|
}
|
|
35
|
-
|
|
36
|
-
return
|
|
253
|
+
has(key) {
|
|
254
|
+
return key in this.data;
|
|
37
255
|
}
|
|
38
|
-
|
|
39
|
-
|
|
256
|
+
setValidated(key) {
|
|
257
|
+
this.validated.add(key);
|
|
258
|
+
}
|
|
259
|
+
isValidated(key) {
|
|
260
|
+
return this.validated.has(key);
|
|
261
|
+
}
|
|
262
|
+
clone() {
|
|
263
|
+
return new PolicyData({
|
|
264
|
+
...this.data
|
|
265
|
+
}, new Set(this.validated));
|
|
40
266
|
}
|
|
41
|
-
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function definePolicyEvaluationContext(input = {}) {
|
|
270
|
+
return {
|
|
271
|
+
evaluators: input.evaluators || {},
|
|
272
|
+
path: input.path || [],
|
|
273
|
+
data: input.data || new PolicyData(),
|
|
274
|
+
include: input.include || [],
|
|
275
|
+
exclude: input.exclude || []
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/*
|
|
280
|
+
* Copyright (c) 2026-2026.
|
|
281
|
+
* Author Peter Placzek (tada5hi)
|
|
282
|
+
* For the full copyright and license information,
|
|
283
|
+
* view the LICENSE file that was distributed with this source code.
|
|
284
|
+
*/ var PolicyIssueSeverity = /*#__PURE__*/ function(PolicyIssueSeverity) {
|
|
285
|
+
PolicyIssueSeverity["WARNING"] = "warning";
|
|
286
|
+
PolicyIssueSeverity["ERROR"] = "error";
|
|
287
|
+
return PolicyIssueSeverity;
|
|
288
|
+
}({});
|
|
289
|
+
var PolicyIssueCode = /*#__PURE__*/ function(PolicyIssueCode) {
|
|
290
|
+
// Data/Input
|
|
291
|
+
PolicyIssueCode["DATA_MISSING"] = "DATA_MISSING";
|
|
292
|
+
PolicyIssueCode["DATA_INVALID"] = "DATA_INVALID";
|
|
293
|
+
// Policy Spec / Config
|
|
294
|
+
PolicyIssueCode["INVALID"] = "INVALID";
|
|
295
|
+
PolicyIssueCode["TYPE_UNKNOWN"] = "TYPE_UNKNOWN";
|
|
296
|
+
PolicyIssueCode["FIELD_MISSING"] = "FIELD_MISSING";
|
|
297
|
+
PolicyIssueCode["FIELD_INVALID"] = "FIELD_INVALID";
|
|
298
|
+
// Composite / Tree evaluation
|
|
299
|
+
PolicyIssueCode["CHILD_INVALID"] = "CHILD_INVALID";
|
|
300
|
+
PolicyIssueCode["CHILD_EVALUATION_FAILED"] = "CHILD_EVALUATION_FAILED";
|
|
301
|
+
// Engine / Evaluator registry
|
|
302
|
+
PolicyIssueCode["EVALUATION_DENIED"] = "EVALUATION_DENIED";
|
|
303
|
+
PolicyIssueCode["EVALUATOR_NOT_FOUND"] = "EVALUATOR_NOT_FOUND";
|
|
304
|
+
PolicyIssueCode["EVALUATOR_ERROR"] = "EVALUATOR_ERROR";
|
|
305
|
+
// Execution control
|
|
306
|
+
PolicyIssueCode["SKIPPED_EXCLUDED"] = "SKIPPED_EXCLUDED";
|
|
307
|
+
PolicyIssueCode["SKIPPED_NOT_INCLUDED"] = "SKIPPED_NOT_INCLUDED";
|
|
308
|
+
// Internal / unexpected
|
|
309
|
+
PolicyIssueCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
|
|
310
|
+
return PolicyIssueCode;
|
|
311
|
+
}({});
|
|
312
|
+
|
|
313
|
+
function definePolicyIssueItem(input) {
|
|
314
|
+
return defineIssueItem(input);
|
|
315
|
+
}
|
|
316
|
+
function definePolicyIssueGroup(input) {
|
|
317
|
+
return defineIssueGroup(input);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
class AttributeNamesPolicyValidator extends Container {
|
|
321
|
+
initialize() {
|
|
322
|
+
super.initialize();
|
|
323
|
+
this.mount('names', createValidator(z.array(z.string().min(3).max(128))));
|
|
324
|
+
this.mount('invert', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/*
|
|
329
|
+
* Copyright (c) 2025.
|
|
330
|
+
* Author Peter Placzek (tada5hi)
|
|
331
|
+
* For the full copyright and license information,
|
|
332
|
+
* view the LICENSE file that was distributed with this source code.
|
|
333
|
+
*/ function defineAttributesPolicy(data) {
|
|
334
|
+
return data;
|
|
42
335
|
}
|
|
43
336
|
|
|
44
337
|
/*
|
|
@@ -74,60 +367,10 @@ class PolicyError extends AuthupError {
|
|
|
74
367
|
return BuiltInPolicyType;
|
|
75
368
|
}({});
|
|
76
369
|
|
|
77
|
-
class AttributeNamesPolicyValidator extends Container {
|
|
78
|
-
initialize() {
|
|
79
|
-
super.initialize();
|
|
80
|
-
this.mount('names', createValidator(z.array(z.string().min(3).max(128))));
|
|
81
|
-
this.mount('invert', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
class AttributeNamesPolicyEvaluator {
|
|
86
|
-
validator;
|
|
87
|
-
constructor(){
|
|
88
|
-
this.validator = new AttributeNamesPolicyValidator();
|
|
89
|
-
}
|
|
90
|
-
async can(ctx) {
|
|
91
|
-
return ctx.config.type === BuiltInPolicyType.ATTRIBUTE_NAMES;
|
|
92
|
-
}
|
|
93
|
-
async validateConfig(ctx) {
|
|
94
|
-
return this.validator.run(ctx.config);
|
|
95
|
-
}
|
|
96
|
-
async validateInput(ctx) {
|
|
97
|
-
if (!isObject(ctx.input.attributes)) {
|
|
98
|
-
throw PolicyError.evaluatorContextInvalid();
|
|
99
|
-
}
|
|
100
|
-
return ctx.input;
|
|
101
|
-
}
|
|
102
|
-
async evaluate(ctx) {
|
|
103
|
-
if (!ctx.input.attributes) {
|
|
104
|
-
throw PolicyError.evaluatorContextInvalid();
|
|
105
|
-
}
|
|
106
|
-
const attributes = flattenObject(ctx.input.attributes);
|
|
107
|
-
const keys = Object.keys(attributes);
|
|
108
|
-
for(let i = 0; i < keys.length; i++){
|
|
109
|
-
const index = ctx.config.names.indexOf(keys[i]);
|
|
110
|
-
if (index === -1) {
|
|
111
|
-
return maybeInvertPolicyOutcome(false, ctx.config.invert);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return maybeInvertPolicyOutcome(true, ctx.config.invert);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/*
|
|
119
|
-
* Copyright (c) 2025.
|
|
120
|
-
* Author Peter Placzek (tada5hi)
|
|
121
|
-
* For the full copyright and license information,
|
|
122
|
-
* view the LICENSE file that was distributed with this source code.
|
|
123
|
-
*/ function defineAttributesPolicy(data) {
|
|
124
|
-
return data;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
370
|
class AttributesPolicyValidator extends Container {
|
|
128
371
|
initialize() {
|
|
129
372
|
super.initialize();
|
|
130
|
-
this.mount('query', createValidator(z.
|
|
373
|
+
this.mount('query', createValidator(z.looseObject({})));
|
|
131
374
|
this.mount('invert', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
132
375
|
}
|
|
133
376
|
}
|
|
@@ -137,25 +380,40 @@ class AttributesPolicyEvaluator {
|
|
|
137
380
|
constructor(){
|
|
138
381
|
this.validator = new AttributesPolicyValidator();
|
|
139
382
|
}
|
|
140
|
-
async
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
async validateConfig(ctx) {
|
|
144
|
-
return this.validator.run(ctx.config);
|
|
145
|
-
}
|
|
146
|
-
async validateInput(ctx) {
|
|
147
|
-
if (!isObject(ctx.input.attributes)) {
|
|
148
|
-
throw PolicyError.evaluatorContextInvalid();
|
|
383
|
+
async accessData(ctx) {
|
|
384
|
+
if (!ctx.data.has(BuiltInPolicyType.ATTRIBUTES)) {
|
|
385
|
+
return null;
|
|
149
386
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
387
|
+
if (ctx.data.isValidated(BuiltInPolicyType.ATTRIBUTES)) {
|
|
388
|
+
return ctx.data.get(BuiltInPolicyType.ATTRIBUTES);
|
|
389
|
+
}
|
|
390
|
+
// todo: run validator on attributes (isObject ...)
|
|
391
|
+
const data = ctx.data.get(BuiltInPolicyType.ATTRIBUTES);
|
|
392
|
+
ctx.data.set(BuiltInPolicyType.ATTRIBUTES, data);
|
|
393
|
+
ctx.data.setValidated(BuiltInPolicyType.ATTRIBUTES);
|
|
394
|
+
return data;
|
|
395
|
+
}
|
|
396
|
+
async evaluate(value, ctx) {
|
|
397
|
+
// todo: catch errors + transform to issue(s)
|
|
398
|
+
const policy = await this.validator.run(value);
|
|
399
|
+
const data = await this.accessData(ctx);
|
|
400
|
+
if (!data) {
|
|
401
|
+
return {
|
|
402
|
+
success: false,
|
|
403
|
+
issues: [
|
|
404
|
+
definePolicyIssueItem({
|
|
405
|
+
code: PolicyIssueCode.DATA_MISSING,
|
|
406
|
+
message: 'The data property attributes is missing',
|
|
407
|
+
path: ctx.path
|
|
408
|
+
})
|
|
409
|
+
]
|
|
410
|
+
};
|
|
155
411
|
}
|
|
156
|
-
this.fixQuery(
|
|
157
|
-
const testIt = guard(
|
|
158
|
-
return
|
|
412
|
+
this.fixQuery(policy.query);
|
|
413
|
+
const testIt = guard(policy.query);
|
|
414
|
+
return {
|
|
415
|
+
success: maybeInvertPolicyOutcome(testIt(data), policy.invert)
|
|
416
|
+
};
|
|
159
417
|
}
|
|
160
418
|
fixQuery(query) {
|
|
161
419
|
if (Array.isArray(query)) {
|
|
@@ -184,122 +442,59 @@ class AttributesPolicyEvaluator {
|
|
|
184
442
|
}
|
|
185
443
|
}
|
|
186
444
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
this.mount('id', createValidator(z.string().uuid()));
|
|
210
|
-
this.mount('clientId', createValidator(z.string().uuid().or(z.null()).or(z.undefined()).optional()));
|
|
211
|
-
this.mount('realmId', createValidator(z.string().uuid().or(z.null()).or(z.undefined()).optional()));
|
|
212
|
-
this.mount('realmName', createValidator(z.string().min(3).max(128).or(z.null()).or(z.undefined()).optional()));
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
class PolicyDataPermissionValidator extends Container {
|
|
217
|
-
initialize() {
|
|
218
|
-
super.initialize();
|
|
219
|
-
this.mount('name', createValidator(z.string().min(3).max(256)));
|
|
220
|
-
this.mount('clientId', createValidator(z.string().uuid().or(z.null()).or(z.undefined()).optional()));
|
|
221
|
-
this.mount('realmId', createValidator(z.string().uuid().or(z.null()).or(z.undefined()).optional()));
|
|
222
|
-
this.mount('policy', createValidator(z.object({
|
|
223
|
-
type: z.string()
|
|
224
|
-
}).optional()));
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
class PolicyDataValidator extends Container {
|
|
229
|
-
initialize() {
|
|
230
|
-
super.initialize();
|
|
231
|
-
// todo: optionalValue should be null
|
|
232
|
-
const identity = new PolicyDataIdentityValidator();
|
|
233
|
-
this.mount('identity', {
|
|
234
|
-
optional: true,
|
|
235
|
-
optionalValue: 'undefined',
|
|
236
|
-
optionalInclude: true
|
|
237
|
-
}, identity);
|
|
238
|
-
// todo: optionalValue should be null
|
|
239
|
-
const permission = new PolicyDataPermissionValidator();
|
|
240
|
-
this.mount('permission', {
|
|
241
|
-
optional: true,
|
|
242
|
-
optionalValue: 'undefined',
|
|
243
|
-
optionalInclude: true
|
|
244
|
-
}, permission);
|
|
245
|
-
this.mount('attributes', createValidator(z.object({}).optional()));
|
|
246
|
-
this.mount('dateTime', createValidator(z.date().or(z.string()).or(z.number()).optional()));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
async function evaluatePolicy(ctx) {
|
|
251
|
-
if (ctx.options.exclude && ctx.options.exclude.length > 0 && ctx.options.exclude.indexOf(ctx.config.type) !== -1) {
|
|
252
|
-
if (typeof ctx.config.invert === 'boolean') {
|
|
253
|
-
return maybeInvertPolicyOutcome(true, ctx.config.invert);
|
|
254
|
-
}
|
|
255
|
-
return true;
|
|
256
|
-
}
|
|
257
|
-
if (ctx.options.include && ctx.options.include.length > 0 && ctx.options.include.indexOf(ctx.config.type) === -1) {
|
|
258
|
-
if (typeof ctx.config.invert === 'boolean') {
|
|
259
|
-
return maybeInvertPolicyOutcome(true, ctx.config.invert);
|
|
260
|
-
}
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
|
-
const evaluator = ctx.evaluators[ctx.config.type];
|
|
264
|
-
if (!evaluator) {
|
|
265
|
-
throw PolicyError.evaluatorNotFound(ctx.config.type);
|
|
266
|
-
}
|
|
267
|
-
try {
|
|
268
|
-
const canEvaluate = await evaluator.can(ctx);
|
|
269
|
-
if (canEvaluate) {
|
|
270
|
-
const config = await evaluator.validateConfig(ctx);
|
|
271
|
-
const input = await evaluator.validateInput(ctx);
|
|
272
|
-
return await evaluator.evaluate({
|
|
273
|
-
...ctx,
|
|
274
|
-
input,
|
|
275
|
-
config
|
|
276
|
-
});
|
|
277
|
-
}
|
|
278
|
-
} catch (e) {
|
|
279
|
-
if (e instanceof PolicyError) {
|
|
280
|
-
throw e;
|
|
445
|
+
class AttributeNamesPolicyEvaluator {
|
|
446
|
+
validator;
|
|
447
|
+
attributesEvaluator;
|
|
448
|
+
constructor(){
|
|
449
|
+
this.validator = new AttributeNamesPolicyValidator();
|
|
450
|
+
this.attributesEvaluator = new AttributesPolicyEvaluator();
|
|
451
|
+
}
|
|
452
|
+
async evaluate(value, ctx) {
|
|
453
|
+
// todo: catch errors + transform to issue(s)
|
|
454
|
+
const policy = await this.validator.run(value);
|
|
455
|
+
const data = await this.attributesEvaluator.accessData(ctx);
|
|
456
|
+
if (!data) {
|
|
457
|
+
return {
|
|
458
|
+
success: false,
|
|
459
|
+
issues: [
|
|
460
|
+
definePolicyIssueItem({
|
|
461
|
+
code: PolicyIssueCode.DATA_MISSING,
|
|
462
|
+
message: 'The data property attributes is missing',
|
|
463
|
+
path: ctx.path
|
|
464
|
+
})
|
|
465
|
+
]
|
|
466
|
+
};
|
|
281
467
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
468
|
+
const attributes = flattenObject(data);
|
|
469
|
+
const keys = Object.keys(attributes);
|
|
470
|
+
const issues = [];
|
|
471
|
+
for(let i = 0; i < keys.length; i++){
|
|
472
|
+
const index = policy.names.indexOf(keys[i]);
|
|
473
|
+
if (index === -1) {
|
|
474
|
+
issues.push(definePolicyIssueItem({
|
|
475
|
+
code: PolicyIssueCode.EVALUATION_DENIED,
|
|
476
|
+
message: `The attribute ${keys[i]} is not included`,
|
|
477
|
+
path: [
|
|
478
|
+
...ctx.path,
|
|
479
|
+
keys[i]
|
|
480
|
+
]
|
|
481
|
+
}));
|
|
482
|
+
}
|
|
286
483
|
}
|
|
287
|
-
|
|
484
|
+
return {
|
|
485
|
+
success: maybeInvertPolicyOutcome(issues.length === 0, policy.invert),
|
|
486
|
+
issues
|
|
487
|
+
};
|
|
288
488
|
}
|
|
289
|
-
throw PolicyError.evaluatorNotProcessable();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
function isCompositePolicy(input) {
|
|
293
|
-
return input.type === BuiltInPolicyType.COMPOSITE;
|
|
294
489
|
}
|
|
295
490
|
|
|
296
491
|
class CompositePolicyValidator extends Container {
|
|
297
492
|
initialize() {
|
|
298
493
|
super.initialize();
|
|
299
|
-
this.mount('decisionStrategy', createValidator(z.
|
|
300
|
-
this.mount('children', createValidator(z.array(z.
|
|
494
|
+
this.mount('decisionStrategy', createValidator(z.enum(DecisionStrategy).or(z.null()).or(z.undefined()).optional()));
|
|
495
|
+
this.mount('children', createValidator(z.array(z.looseObject({
|
|
301
496
|
type: z.string().min(3)
|
|
302
|
-
})
|
|
497
|
+
}))));
|
|
303
498
|
this.mount('invert', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
304
499
|
}
|
|
305
500
|
}
|
|
@@ -309,53 +504,81 @@ class CompositePolicyEvaluator {
|
|
|
309
504
|
constructor(){
|
|
310
505
|
this.validator = new CompositePolicyValidator();
|
|
311
506
|
}
|
|
312
|
-
async
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
async validateConfig(ctx) {
|
|
316
|
-
return this.validator.run(ctx.config);
|
|
317
|
-
}
|
|
318
|
-
async validateInput(ctx) {
|
|
319
|
-
return ctx.input;
|
|
320
|
-
}
|
|
321
|
-
async evaluate(ctx) {
|
|
507
|
+
async evaluate(value, ctx) {
|
|
508
|
+
// todo: catch errors + transform to issue(s)
|
|
509
|
+
const policy = await this.validator.run(value);
|
|
322
510
|
let count = 0;
|
|
323
|
-
const decisionStrategy =
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
if (outcome) {
|
|
511
|
+
const decisionStrategy = policy.decisionStrategy ?? DecisionStrategy.UNANIMOUS;
|
|
512
|
+
const engine = new PolicyEngine(ctx.evaluators);
|
|
513
|
+
const issues = [];
|
|
514
|
+
for(let i = 0; i < policy.children.length; i++){
|
|
515
|
+
const childPolicy = policy.children[i];
|
|
516
|
+
const path = [
|
|
517
|
+
...ctx.path || [],
|
|
518
|
+
childPolicy.type
|
|
519
|
+
];
|
|
520
|
+
const outcome = await engine.evaluate(childPolicy, {
|
|
521
|
+
...ctx,
|
|
522
|
+
path
|
|
523
|
+
});
|
|
524
|
+
if (outcome.success) {
|
|
339
525
|
if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
|
|
340
|
-
return
|
|
526
|
+
return {
|
|
527
|
+
success: maybeInvertPolicyOutcome(true, policy.invert),
|
|
528
|
+
issues: []
|
|
529
|
+
};
|
|
341
530
|
}
|
|
342
531
|
count++;
|
|
343
532
|
} else {
|
|
533
|
+
if (outcome.issues) {
|
|
534
|
+
issues.push(defineIssueGroup({
|
|
535
|
+
message: `The evaluation of child policy ${childPolicy.type} failed`,
|
|
536
|
+
issues: outcome.issues || [],
|
|
537
|
+
path
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
344
540
|
if (decisionStrategy === DecisionStrategy.UNANIMOUS) {
|
|
345
|
-
return
|
|
541
|
+
return {
|
|
542
|
+
success: maybeInvertPolicyOutcome(false, policy.invert),
|
|
543
|
+
issues: [
|
|
544
|
+
defineIssueGroup({
|
|
545
|
+
message: `The evaluation of composite policy failed (${DecisionStrategy.UNANIMOUS})`,
|
|
546
|
+
issues,
|
|
547
|
+
path: ctx.path
|
|
548
|
+
})
|
|
549
|
+
]
|
|
550
|
+
};
|
|
346
551
|
}
|
|
347
552
|
count--;
|
|
348
553
|
}
|
|
349
554
|
}
|
|
350
|
-
|
|
555
|
+
if (count > 0) {
|
|
556
|
+
return {
|
|
557
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
return {
|
|
561
|
+
success: maybeInvertPolicyOutcome(false, policy.invert),
|
|
562
|
+
issues: [
|
|
563
|
+
defineIssueGroup({
|
|
564
|
+
message: `The evaluation of composite policy failed (${DecisionStrategy.CONSENSUS})`,
|
|
565
|
+
issues: issues || [],
|
|
566
|
+
path: ctx.path
|
|
567
|
+
})
|
|
568
|
+
]
|
|
569
|
+
};
|
|
351
570
|
}
|
|
352
571
|
}
|
|
353
572
|
|
|
573
|
+
function isCompositePolicy(input) {
|
|
574
|
+
return input.type === BuiltInPolicyType.COMPOSITE;
|
|
575
|
+
}
|
|
576
|
+
|
|
354
577
|
class DatePolicyValidator extends Container {
|
|
355
578
|
initialize() {
|
|
356
579
|
super.initialize();
|
|
357
|
-
this.mount('start', createValidator(z.date().or(z.
|
|
358
|
-
this.mount('end', createValidator(z.date().or(z.
|
|
580
|
+
this.mount('start', createValidator(z.date().or(z.iso.date()).or(z.number()).or(z.null()).or(z.undefined()).optional()));
|
|
581
|
+
this.mount('end', createValidator(z.date().or(z.iso.date()).or(z.number()).or(z.null()).or(z.undefined()).optional()));
|
|
359
582
|
this.mount('invert', createValidator(z.boolean().or(z.undefined()).or(z.null()).optional()));
|
|
360
583
|
}
|
|
361
584
|
}
|
|
@@ -377,35 +600,52 @@ class DatePolicyEvaluator {
|
|
|
377
600
|
constructor(){
|
|
378
601
|
this.validator = new DatePolicyValidator();
|
|
379
602
|
}
|
|
380
|
-
async
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
async validateConfig(ctx) {
|
|
384
|
-
return this.validator.run(ctx.config);
|
|
385
|
-
}
|
|
386
|
-
async validateInput(ctx) {
|
|
387
|
-
return ctx.input;
|
|
388
|
-
}
|
|
389
|
-
async evaluate(ctx) {
|
|
603
|
+
async evaluate(value, ctx) {
|
|
604
|
+
// todo: catch errors + transform to issue(s)
|
|
605
|
+
const policy = await this.validator.run(value);
|
|
390
606
|
let now;
|
|
391
|
-
if (ctx.
|
|
392
|
-
|
|
607
|
+
if (ctx.data.has(BuiltInPolicyType.DATE)) {
|
|
608
|
+
if (ctx.data.isValidated(BuiltInPolicyType.DATE)) {
|
|
609
|
+
now = ctx.data.get(BuiltInPolicyType.DATE);
|
|
610
|
+
} else {
|
|
611
|
+
// todo: run validator on attributes (isObject ...)
|
|
612
|
+
now = normalizeDate$1(toDate$1(ctx.data.get(BuiltInPolicyType.DATE)));
|
|
613
|
+
ctx.data.set(BuiltInPolicyType.DATE, now);
|
|
614
|
+
ctx.data.setValidated(BuiltInPolicyType.DATE);
|
|
615
|
+
}
|
|
393
616
|
} else {
|
|
394
617
|
now = normalizeDate$1(new Date());
|
|
395
618
|
}
|
|
396
|
-
if (
|
|
397
|
-
const start = normalizeDate$1(toDate$1(
|
|
619
|
+
if (policy.start) {
|
|
620
|
+
const start = normalizeDate$1(toDate$1(policy.start));
|
|
398
621
|
if (now < start) {
|
|
399
|
-
return
|
|
622
|
+
return {
|
|
623
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
624
|
+
};
|
|
400
625
|
}
|
|
401
626
|
}
|
|
402
|
-
if (
|
|
403
|
-
const end = normalizeDate$1(toDate$1(
|
|
627
|
+
if (policy.end) {
|
|
628
|
+
const end = normalizeDate$1(toDate$1(policy.end));
|
|
404
629
|
if (now > end) {
|
|
405
|
-
return
|
|
630
|
+
return {
|
|
631
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
632
|
+
};
|
|
406
633
|
}
|
|
407
634
|
}
|
|
408
|
-
return
|
|
635
|
+
return {
|
|
636
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
class PolicyIdentityDataValidator extends Container {
|
|
642
|
+
initialize() {
|
|
643
|
+
super.initialize();
|
|
644
|
+
this.mount('type', createValidator(z.string().min(3).max(128)));
|
|
645
|
+
this.mount('id', createValidator(z.uuid()));
|
|
646
|
+
this.mount('clientId', createValidator(z.uuid().or(z.null()).or(z.undefined()).optional()));
|
|
647
|
+
this.mount('realmId', createValidator(z.uuid().or(z.null()).or(z.undefined()).optional()));
|
|
648
|
+
this.mount('realmName', createValidator(z.string().min(3).max(128).or(z.null()).or(z.undefined()).optional()));
|
|
409
649
|
}
|
|
410
650
|
}
|
|
411
651
|
|
|
@@ -418,28 +658,60 @@ class IdentityPolicyValidator extends Container {
|
|
|
418
658
|
|
|
419
659
|
class IdentityPolicyEvaluator {
|
|
420
660
|
validator;
|
|
661
|
+
dataValidator;
|
|
421
662
|
constructor(){
|
|
422
663
|
this.validator = new IdentityPolicyValidator();
|
|
664
|
+
this.dataValidator = new PolicyIdentityDataValidator();
|
|
423
665
|
}
|
|
424
|
-
async
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
async validateConfig(ctx) {
|
|
428
|
-
return this.validator.run(ctx.config);
|
|
429
|
-
}
|
|
430
|
-
async validateInput(ctx) {
|
|
431
|
-
return ctx.input;
|
|
432
|
-
}
|
|
433
|
-
async evaluate(ctx) {
|
|
434
|
-
if (!isObject(ctx.input.identity)) {
|
|
435
|
-
return maybeInvertPolicyOutcome(false, ctx.config.invert);
|
|
666
|
+
async accessData(ctx) {
|
|
667
|
+
if (!ctx.data.has('identity')) {
|
|
668
|
+
return null;
|
|
436
669
|
}
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
return maybeInvertPolicyOutcome(true, ctx.config.invert);
|
|
670
|
+
if (ctx.data.isValidated('identity')) {
|
|
671
|
+
return ctx.data.get('identity');
|
|
440
672
|
}
|
|
441
|
-
const
|
|
442
|
-
|
|
673
|
+
const data = await this.dataValidator.run(ctx.data.get('identity'));
|
|
674
|
+
ctx.data.set('identity', data);
|
|
675
|
+
ctx.data.setValidated('identity');
|
|
676
|
+
return data;
|
|
677
|
+
}
|
|
678
|
+
async evaluate(value, ctx) {
|
|
679
|
+
// todo: catch errors + transform to issue(s)
|
|
680
|
+
const policy = await this.validator.run(value);
|
|
681
|
+
const data = await this.accessData(ctx);
|
|
682
|
+
if (!data) {
|
|
683
|
+
return {
|
|
684
|
+
success: false,
|
|
685
|
+
issues: [
|
|
686
|
+
definePolicyIssueItem({
|
|
687
|
+
code: PolicyIssueCode.DATA_MISSING,
|
|
688
|
+
message: 'The data property identity is missing',
|
|
689
|
+
path: ctx.path
|
|
690
|
+
})
|
|
691
|
+
]
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
if (!policy.types || policy.types.length === 0) {
|
|
695
|
+
return {
|
|
696
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const typeAllowed = policy.types.indexOf(data.type) !== -1;
|
|
700
|
+
if (typeAllowed) {
|
|
701
|
+
return {
|
|
702
|
+
success: maybeInvertPolicyOutcome(typeAllowed, policy.invert)
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
return {
|
|
706
|
+
success: maybeInvertPolicyOutcome(typeAllowed, policy.invert),
|
|
707
|
+
issues: [
|
|
708
|
+
definePolicyIssueItem({
|
|
709
|
+
code: PolicyIssueCode.EVALUATION_DENIED,
|
|
710
|
+
message: `The type ${data.type} is not covered by the policy configuration`,
|
|
711
|
+
path: ctx.path
|
|
712
|
+
})
|
|
713
|
+
]
|
|
714
|
+
};
|
|
443
715
|
}
|
|
444
716
|
}
|
|
445
717
|
|
|
@@ -452,28 +724,19 @@ class PermissionBindingPolicyEvaluator {
|
|
|
452
724
|
constructor(){
|
|
453
725
|
this.validator = new PermissionBindingPolicyValidator();
|
|
454
726
|
}
|
|
455
|
-
async
|
|
456
|
-
|
|
457
|
-
}
|
|
458
|
-
async validateConfig(ctx) {
|
|
459
|
-
return this.validator.run(ctx.config);
|
|
460
|
-
}
|
|
461
|
-
async validateInput(ctx) {
|
|
462
|
-
if (!isObject(ctx.input.identity) && !isObject(ctx.input.permission)) {
|
|
463
|
-
throw PolicyError.evaluatorContextInvalid();
|
|
464
|
-
}
|
|
465
|
-
return ctx.input;
|
|
466
|
-
}
|
|
467
|
-
async evaluate(ctx) {
|
|
727
|
+
async evaluate(value) {
|
|
728
|
+
const policy = await this.validator.run(value);
|
|
468
729
|
// todo: this must be changed when the permission-checker not only checks owned permissions.
|
|
469
|
-
return
|
|
730
|
+
return {
|
|
731
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
732
|
+
};
|
|
470
733
|
}
|
|
471
734
|
}
|
|
472
735
|
|
|
473
736
|
class RealmMatchPolicyValidator extends Container {
|
|
474
737
|
initialize() {
|
|
475
738
|
super.initialize();
|
|
476
|
-
this.mount('decisionStrategy', createValidator(z.
|
|
739
|
+
this.mount('decisionStrategy', createValidator(z.enum(DecisionStrategy).or(z.null()).or(z.undefined()).optional()));
|
|
477
740
|
this.mount('attributeName', createValidator(z.string().min(3).or(z.array(z.string().min(3))).or(z.null()).or(z.undefined()).optional()));
|
|
478
741
|
this.mount('attributeNameStrict', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
479
742
|
this.mount('attributeNullMatchAll', createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
@@ -484,36 +747,55 @@ class RealmMatchPolicyValidator extends Container {
|
|
|
484
747
|
|
|
485
748
|
class RealmMatchPolicyEvaluator {
|
|
486
749
|
validator;
|
|
750
|
+
identityEvaluator;
|
|
751
|
+
attributesEvaluator;
|
|
487
752
|
constructor(){
|
|
488
753
|
this.validator = new RealmMatchPolicyValidator();
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
754
|
+
this.identityEvaluator = new IdentityPolicyEvaluator();
|
|
755
|
+
this.attributesEvaluator = new AttributesPolicyEvaluator();
|
|
756
|
+
}
|
|
757
|
+
async evaluate(value, ctx) {
|
|
758
|
+
// todo: catch errors + transform to issue(s)
|
|
759
|
+
const policy = await this.validator.run(value);
|
|
760
|
+
const identity = await this.identityEvaluator.accessData(ctx);
|
|
761
|
+
if (!identity) {
|
|
762
|
+
return {
|
|
763
|
+
success: false,
|
|
764
|
+
issues: [
|
|
765
|
+
definePolicyIssueItem({
|
|
766
|
+
code: PolicyIssueCode.DATA_MISSING,
|
|
767
|
+
message: 'The data property identity is missing',
|
|
768
|
+
path: ctx.path
|
|
769
|
+
})
|
|
770
|
+
]
|
|
771
|
+
};
|
|
499
772
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
773
|
+
const attributes = await this.attributesEvaluator.accessData(ctx);
|
|
774
|
+
if (!attributes) {
|
|
775
|
+
return {
|
|
776
|
+
success: false,
|
|
777
|
+
issues: [
|
|
778
|
+
definePolicyIssueItem({
|
|
779
|
+
code: PolicyIssueCode.DATA_MISSING,
|
|
780
|
+
message: 'The data property attributes is missing',
|
|
781
|
+
path: ctx.path
|
|
782
|
+
})
|
|
783
|
+
]
|
|
784
|
+
};
|
|
505
785
|
}
|
|
506
|
-
const identityMasterMatchAll =
|
|
507
|
-
if (identityMasterMatchAll &&
|
|
508
|
-
return
|
|
786
|
+
const identityMasterMatchAll = policy.identityMasterMatchAll ?? true;
|
|
787
|
+
if (identityMasterMatchAll && identity.realmName && identity.realmName === 'master') {
|
|
788
|
+
return {
|
|
789
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
790
|
+
};
|
|
509
791
|
}
|
|
510
792
|
let keys;
|
|
511
|
-
if (
|
|
512
|
-
if (Array.isArray(
|
|
513
|
-
keys =
|
|
793
|
+
if (policy.attributeName) {
|
|
794
|
+
if (Array.isArray(policy.attributeName)) {
|
|
795
|
+
keys = policy.attributeName;
|
|
514
796
|
} else {
|
|
515
797
|
keys = [
|
|
516
|
-
|
|
798
|
+
policy.attributeName
|
|
517
799
|
];
|
|
518
800
|
}
|
|
519
801
|
} else {
|
|
@@ -523,11 +805,11 @@ class RealmMatchPolicyEvaluator {
|
|
|
523
805
|
'realmId',
|
|
524
806
|
'realmName'
|
|
525
807
|
];
|
|
526
|
-
|
|
808
|
+
policy.decisionStrategy = DecisionStrategy.CONSENSUS;
|
|
527
809
|
}
|
|
528
|
-
const attributeNameStrict =
|
|
810
|
+
const attributeNameStrict = policy.attributeNameStrict ?? true;
|
|
529
811
|
if (!attributeNameStrict) {
|
|
530
|
-
const resourceKeys = Object.keys(
|
|
812
|
+
const resourceKeys = Object.keys(attributes);
|
|
531
813
|
const keysToAdd = [];
|
|
532
814
|
for(let i = 0; i < resourceKeys.length; i++){
|
|
533
815
|
let contains = false;
|
|
@@ -547,29 +829,35 @@ class RealmMatchPolicyEvaluator {
|
|
|
547
829
|
}
|
|
548
830
|
let count = 0;
|
|
549
831
|
for(let i = 0; i < keys.length; i++){
|
|
550
|
-
if (!hasOwnProperty(
|
|
832
|
+
if (!hasOwnProperty(attributes, keys[i])) {
|
|
551
833
|
continue;
|
|
552
834
|
}
|
|
553
835
|
let outcome = false;
|
|
554
|
-
const attributeValue =
|
|
555
|
-
if (attributeValue === null &&
|
|
836
|
+
const attributeValue = attributes[keys[i]];
|
|
837
|
+
if (attributeValue === null && policy.attributeNullMatchAll) {
|
|
556
838
|
outcome = true;
|
|
557
|
-
} else if (attributeValue ===
|
|
839
|
+
} else if (attributeValue === identity.realmId || attributeValue === identity.realmName) {
|
|
558
840
|
outcome = true;
|
|
559
841
|
}
|
|
560
842
|
if (outcome) {
|
|
561
|
-
if (
|
|
562
|
-
return
|
|
843
|
+
if (policy.decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
|
|
844
|
+
return {
|
|
845
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
846
|
+
};
|
|
563
847
|
}
|
|
564
848
|
count++;
|
|
565
849
|
} else {
|
|
566
|
-
if (
|
|
567
|
-
return
|
|
850
|
+
if (policy.decisionStrategy === DecisionStrategy.UNANIMOUS) {
|
|
851
|
+
return {
|
|
852
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
853
|
+
};
|
|
568
854
|
}
|
|
569
855
|
count--;
|
|
570
856
|
}
|
|
571
857
|
}
|
|
572
|
-
return
|
|
858
|
+
return {
|
|
859
|
+
success: maybeInvertPolicyOutcome(count > 0, policy.invert)
|
|
860
|
+
};
|
|
573
861
|
}
|
|
574
862
|
}
|
|
575
863
|
|
|
@@ -602,9 +890,9 @@ class TimePolicyValidator extends Container {
|
|
|
602
890
|
this.mount('dayOfWeek', createValidator(z.number().min(0).max(6).or(z.null()).or(z.undefined()).optional()));
|
|
603
891
|
this.mount('dayOfMonth', createValidator(z.number().min(1).max(31).or(z.null()).or(z.undefined()).optional()));
|
|
604
892
|
this.mount('dayOfYear', createValidator(z.number().min(1).max(365).or(z.null()).or(z.undefined()).optional()));
|
|
605
|
-
this.mount('start', createValidator(z.date().or(z.
|
|
606
|
-
this.mount('end', createValidator(z.date().or(z.
|
|
607
|
-
this.mount('interval', createValidator(z.
|
|
893
|
+
this.mount('start', createValidator(z.date().or(z.iso.datetime()).or(z.iso.time()).or(z.number()).or(z.null()).or(z.undefined()).optional()));
|
|
894
|
+
this.mount('end', createValidator(z.date().or(z.iso.datetime()).or(z.iso.time()).or(z.number()).or(z.null()).or(z.undefined()).optional()));
|
|
895
|
+
this.mount('interval', createValidator(z.enum(TimePolicyInterval).or(z.null()).or(z.undefined()).optional()));
|
|
608
896
|
this.mount('invert', createValidator(z.boolean().or(z.undefined()).or(z.null()).optional()));
|
|
609
897
|
}
|
|
610
898
|
}
|
|
@@ -635,56 +923,68 @@ class TimePolicyEvaluator {
|
|
|
635
923
|
constructor(){
|
|
636
924
|
this.validator = new TimePolicyValidator();
|
|
637
925
|
}
|
|
638
|
-
async
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
async validateConfig(ctx) {
|
|
642
|
-
return this.validator.run(ctx.config);
|
|
643
|
-
}
|
|
644
|
-
async validateInput(ctx) {
|
|
645
|
-
return ctx.input;
|
|
646
|
-
}
|
|
647
|
-
async evaluate(ctx) {
|
|
926
|
+
async evaluate(value, ctx) {
|
|
927
|
+
// todo: catch errors + transform to issue(s)
|
|
928
|
+
const policy = await this.validator.run(value);
|
|
648
929
|
let now;
|
|
649
|
-
if (ctx.
|
|
650
|
-
|
|
930
|
+
if (ctx.data.has(BuiltInPolicyType.TIME)) {
|
|
931
|
+
if (ctx.data.isValidated(BuiltInPolicyType.TIME)) {
|
|
932
|
+
now = ctx.data.get(BuiltInPolicyType.TIME);
|
|
933
|
+
} else {
|
|
934
|
+
// todo: run validator on attributes (isObject ...)
|
|
935
|
+
now = toDate(ctx.data.get(BuiltInPolicyType.TIME));
|
|
936
|
+
ctx.data.set(BuiltInPolicyType.TIME, now);
|
|
937
|
+
ctx.data.setValidated(BuiltInPolicyType.TIME);
|
|
938
|
+
}
|
|
651
939
|
} else {
|
|
652
940
|
now = new Date();
|
|
653
941
|
}
|
|
654
|
-
if (
|
|
655
|
-
const start = normalizeDate(toDate(
|
|
942
|
+
if (policy.start) {
|
|
943
|
+
const start = normalizeDate(toDate(policy.start, now), now);
|
|
656
944
|
if (now < start) {
|
|
657
|
-
return
|
|
945
|
+
return {
|
|
946
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
947
|
+
};
|
|
658
948
|
}
|
|
659
949
|
}
|
|
660
|
-
if (
|
|
661
|
-
const end = normalizeDate(toDate(
|
|
950
|
+
if (policy.end) {
|
|
951
|
+
const end = normalizeDate(toDate(policy.end, now), now);
|
|
662
952
|
if (now > end) {
|
|
663
|
-
return
|
|
953
|
+
return {
|
|
954
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
955
|
+
};
|
|
664
956
|
}
|
|
665
957
|
}
|
|
666
|
-
if (
|
|
667
|
-
if (isIntervalForDayOfWeek(
|
|
668
|
-
if (now.getDay() !==
|
|
669
|
-
return
|
|
958
|
+
if (policy.interval) {
|
|
959
|
+
if (isIntervalForDayOfWeek(policy.interval) && policy.dayOfWeek) {
|
|
960
|
+
if (now.getDay() !== policy.dayOfWeek) {
|
|
961
|
+
return {
|
|
962
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
963
|
+
};
|
|
670
964
|
}
|
|
671
965
|
}
|
|
672
|
-
if (isIntervalForDayOfMonth(
|
|
673
|
-
if (now.getDate() !==
|
|
674
|
-
return
|
|
966
|
+
if (isIntervalForDayOfMonth(policy.interval) && policy.dayOfMonth) {
|
|
967
|
+
if (now.getDate() !== policy.dayOfMonth) {
|
|
968
|
+
return {
|
|
969
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
970
|
+
};
|
|
675
971
|
}
|
|
676
972
|
}
|
|
677
|
-
if (isIntervalForDayOfYear(
|
|
973
|
+
if (isIntervalForDayOfYear(policy.interval) && policy.dayOfYear) {
|
|
678
974
|
const start = new Date(now.getFullYear(), 0, 0);
|
|
679
975
|
const diff = now.getTime() - start.getTime() + (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
|
|
680
976
|
const oneDay = 1000 * 60 * 60 * 24;
|
|
681
977
|
const dayOfYear = Math.floor(diff / oneDay);
|
|
682
|
-
if (dayOfYear !==
|
|
683
|
-
return
|
|
978
|
+
if (dayOfYear !== policy.dayOfYear) {
|
|
979
|
+
return {
|
|
980
|
+
success: maybeInvertPolicyOutcome(false, policy.invert)
|
|
981
|
+
};
|
|
684
982
|
}
|
|
685
983
|
}
|
|
686
984
|
}
|
|
687
|
-
return
|
|
985
|
+
return {
|
|
986
|
+
success: maybeInvertPolicyOutcome(true, policy.invert)
|
|
987
|
+
};
|
|
688
988
|
}
|
|
689
989
|
}
|
|
690
990
|
|
|
@@ -700,120 +1000,16 @@ class TimePolicyEvaluator {
|
|
|
700
1000
|
};
|
|
701
1001
|
}
|
|
702
1002
|
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
registerEvaluator(type, evaluator) {
|
|
714
|
-
this.evaluators[type] = evaluator;
|
|
715
|
-
}
|
|
716
|
-
registerEvaluators(evaluators) {
|
|
717
|
-
const keys = Object.keys(evaluators);
|
|
718
|
-
for(let i = 0; i < keys.length; i++){
|
|
719
|
-
this.registerEvaluator(keys[i], evaluators[keys[i]]);
|
|
720
|
-
}
|
|
721
|
-
}
|
|
722
|
-
registerDefaultEvaluators() {
|
|
723
|
-
this.registerEvaluator(BuiltInPolicyType.COMPOSITE, new CompositePolicyEvaluator());
|
|
724
|
-
this.registerEvaluator(BuiltInPolicyType.ATTRIBUTES, new AttributesPolicyEvaluator());
|
|
725
|
-
this.registerEvaluator(BuiltInPolicyType.ATTRIBUTE_NAMES, new AttributeNamesPolicyEvaluator());
|
|
726
|
-
this.registerEvaluator(BuiltInPolicyType.DATE, new DatePolicyEvaluator());
|
|
727
|
-
this.registerEvaluator(BuiltInPolicyType.IDENTITY, new IdentityPolicyEvaluator());
|
|
728
|
-
this.registerEvaluator(BuiltInPolicyType.PERMISSION_BINDING, new PermissionBindingPolicyEvaluator());
|
|
729
|
-
this.registerEvaluator(BuiltInPolicyType.REALM_MATCH, new RealmMatchPolicyEvaluator());
|
|
730
|
-
this.registerEvaluator(BuiltInPolicyType.TIME, new TimePolicyEvaluator());
|
|
731
|
-
}
|
|
732
|
-
/**
|
|
733
|
-
* @throws PolicyError
|
|
734
|
-
*
|
|
735
|
-
* @param ctx
|
|
736
|
-
*/ async evaluate(ctx) {
|
|
737
|
-
return evaluatePolicy({
|
|
738
|
-
...ctx,
|
|
739
|
-
options: ctx.options || {},
|
|
740
|
-
evaluators: this.evaluators
|
|
741
|
-
});
|
|
742
|
-
}
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
class PermissionError extends AuthupError {
|
|
746
|
-
policy;
|
|
747
|
-
policyError;
|
|
748
|
-
constructor(options = {}){
|
|
749
|
-
super({
|
|
750
|
-
...options,
|
|
751
|
-
message: options.message || 'A permission error occurred.',
|
|
752
|
-
statusCode: options.statusCode || 403
|
|
753
|
-
});
|
|
754
|
-
}
|
|
755
|
-
static notFound(name) {
|
|
756
|
-
return new PermissionError({
|
|
757
|
-
message: `The permission ${name} was not found.`,
|
|
758
|
-
code: ErrorCode.PERMISSION_NOT_FOUND
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
static denied(name) {
|
|
762
|
-
return new PermissionError({
|
|
763
|
-
message: `The permission ${name} has not been granted.`,
|
|
764
|
-
code: ErrorCode.PERMISSION_DENIED
|
|
765
|
-
});
|
|
766
|
-
}
|
|
767
|
-
static deniedAll(names) {
|
|
768
|
-
return new PermissionError({
|
|
769
|
-
message: `None of the permissions ${names.join(', ')} has been granted.`,
|
|
770
|
-
code: ErrorCode.PERMISSION_DENIED
|
|
771
|
-
});
|
|
772
|
-
}
|
|
773
|
-
static evaluationFailed(options) {
|
|
774
|
-
const error = new PermissionError({
|
|
775
|
-
message: `The evaluation of permission ${options.name} failed.`,
|
|
776
|
-
code: ErrorCode.PERMISSION_EVALUATION_FAILED
|
|
777
|
-
});
|
|
778
|
-
if (options.policy) {
|
|
779
|
-
error.policy = options.policy;
|
|
780
|
-
if (options.policy.type === BuiltInPolicyType.IDENTITY) {
|
|
781
|
-
error.statusCode = 401;
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
if (options.policyError) {
|
|
785
|
-
error.policyError = options.policyError;
|
|
786
|
-
}
|
|
787
|
-
return error;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
/*
|
|
792
|
-
* Copyright (c) 2025.
|
|
793
|
-
* Author Peter Placzek (tada5hi)
|
|
794
|
-
* For the full copyright and license information,
|
|
795
|
-
* view the LICENSE file that was distributed with this source code.
|
|
796
|
-
*/ function buildPermissionItemKey(input) {
|
|
797
|
-
return `${input.realmId || '_'}/${input.clientId || '_'}/${input.name}`;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
/*
|
|
801
|
-
* Copyright (c) 2024-2024.
|
|
802
|
-
* Author Peter Placzek (tada5hi)
|
|
803
|
-
* For the full copyright and license information,
|
|
804
|
-
* view the LICENSE file that was distributed with this source code.
|
|
805
|
-
*/ function isPermissionItemEqual(a, b) {
|
|
806
|
-
if (a.name !== b.name) {
|
|
807
|
-
return false;
|
|
808
|
-
}
|
|
809
|
-
if (typeof a.realmId === 'string' || typeof b.realmId === 'string') {
|
|
810
|
-
return a.realmId === b.realmId;
|
|
811
|
-
}
|
|
812
|
-
if (typeof a.clientId === 'string' || typeof b.clientId === 'string') {
|
|
813
|
-
return a.clientId === b.clientId;
|
|
814
|
-
}
|
|
815
|
-
return !!a.realmId === !!b.realmId && !!a.clientId === !!b.clientId;
|
|
816
|
-
}
|
|
1003
|
+
const PolicyDefaultEvaluators = {
|
|
1004
|
+
[BuiltInPolicyType.COMPOSITE]: new CompositePolicyEvaluator(),
|
|
1005
|
+
[BuiltInPolicyType.ATTRIBUTES]: new AttributesPolicyEvaluator(),
|
|
1006
|
+
[BuiltInPolicyType.ATTRIBUTE_NAMES]: new AttributeNamesPolicyEvaluator(),
|
|
1007
|
+
[BuiltInPolicyType.DATE]: new DatePolicyEvaluator(),
|
|
1008
|
+
[BuiltInPolicyType.IDENTITY]: new IdentityPolicyEvaluator(),
|
|
1009
|
+
[BuiltInPolicyType.PERMISSION_BINDING]: new PermissionBindingPolicyEvaluator(),
|
|
1010
|
+
[BuiltInPolicyType.REALM_MATCH]: new RealmMatchPolicyEvaluator(),
|
|
1011
|
+
[BuiltInPolicyType.TIME]: new TimePolicyEvaluator()
|
|
1012
|
+
};
|
|
817
1013
|
|
|
818
1014
|
function mergePermissionItems(input) {
|
|
819
1015
|
const grouped = input.reduce((previous, current)=>{
|
|
@@ -846,13 +1042,17 @@ function mergePermissionItems(input) {
|
|
|
846
1042
|
return output;
|
|
847
1043
|
}
|
|
848
1044
|
|
|
849
|
-
class
|
|
1045
|
+
class PermissionMemoryRepository {
|
|
850
1046
|
items = {};
|
|
851
1047
|
constructor(items = []){
|
|
852
1048
|
this.setMany(items);
|
|
853
1049
|
}
|
|
854
|
-
async
|
|
855
|
-
|
|
1050
|
+
async findOne(options) {
|
|
1051
|
+
const entry = this.items[buildPermissionItemKey(options)];
|
|
1052
|
+
if (entry) {
|
|
1053
|
+
return entry;
|
|
1054
|
+
}
|
|
1055
|
+
return null;
|
|
856
1056
|
}
|
|
857
1057
|
setMany(input) {
|
|
858
1058
|
this.items = mergePermissionItems(input).reduce((prev, current)=>{
|
|
@@ -869,10 +1069,10 @@ class PermissionChecker {
|
|
|
869
1069
|
realmId;
|
|
870
1070
|
// ----------------------------------------------
|
|
871
1071
|
constructor(options = {}){
|
|
872
|
-
if (options.
|
|
873
|
-
this.provider = options.
|
|
1072
|
+
if (options.repository) {
|
|
1073
|
+
this.provider = options.repository;
|
|
874
1074
|
} else {
|
|
875
|
-
this.provider = new
|
|
1075
|
+
this.provider = new PermissionMemoryRepository();
|
|
876
1076
|
}
|
|
877
1077
|
if (options.clientId) {
|
|
878
1078
|
this.clientId = options.clientId;
|
|
@@ -883,7 +1083,7 @@ class PermissionChecker {
|
|
|
883
1083
|
if (options.policyEngine) {
|
|
884
1084
|
this.policyEngine = options.policyEngine;
|
|
885
1085
|
} else {
|
|
886
|
-
this.policyEngine = new PolicyEngine();
|
|
1086
|
+
this.policyEngine = new PolicyEngine(PolicyDefaultEvaluators);
|
|
887
1087
|
}
|
|
888
1088
|
}
|
|
889
1089
|
// ----------------------------------------------
|
|
@@ -891,7 +1091,7 @@ class PermissionChecker {
|
|
|
891
1091
|
* Get a permission.
|
|
892
1092
|
*
|
|
893
1093
|
* @param input
|
|
894
|
-
*/ async
|
|
1094
|
+
*/ async findOne(input) {
|
|
895
1095
|
const options = {
|
|
896
1096
|
name: input
|
|
897
1097
|
};
|
|
@@ -901,7 +1101,7 @@ class PermissionChecker {
|
|
|
901
1101
|
if (this.realmId) {
|
|
902
1102
|
options.realmId = this.realmId;
|
|
903
1103
|
}
|
|
904
|
-
return this.provider.
|
|
1104
|
+
return this.provider.findOne(options);
|
|
905
1105
|
}
|
|
906
1106
|
// ----------------------------------------------
|
|
907
1107
|
/**
|
|
@@ -922,14 +1122,23 @@ class PermissionChecker {
|
|
|
922
1122
|
}
|
|
923
1123
|
const { options = {} } = ctx;
|
|
924
1124
|
const decisionStrategy = options.decisionStrategy ?? DecisionStrategy.UNANIMOUS;
|
|
925
|
-
|
|
1125
|
+
const issues = [];
|
|
926
1126
|
let count = 0;
|
|
1127
|
+
const dataBase = ctx.input || new PolicyData();
|
|
927
1128
|
for(let i = 0; i < ctx.name.length; i++){
|
|
928
|
-
const entity = await this.
|
|
1129
|
+
const entity = await this.findOne(ctx.name[i]);
|
|
929
1130
|
if (!entity) {
|
|
930
|
-
|
|
1131
|
+
issues.push(defineIssueItem({
|
|
1132
|
+
code: ErrorCode.PERMISSION_NOT_FOUND,
|
|
1133
|
+
message: `The ${ctx.name[i]} permission could not be resolved`,
|
|
1134
|
+
path: [
|
|
1135
|
+
ctx.name[i]
|
|
1136
|
+
]
|
|
1137
|
+
}));
|
|
931
1138
|
if (decisionStrategy === DecisionStrategy.UNANIMOUS) {
|
|
932
|
-
|
|
1139
|
+
const error = PermissionError.evaluationFailed(ctx.name);
|
|
1140
|
+
error.addIssues(issues);
|
|
1141
|
+
throw error;
|
|
933
1142
|
}
|
|
934
1143
|
continue;
|
|
935
1144
|
}
|
|
@@ -940,48 +1149,31 @@ class PermissionChecker {
|
|
|
940
1149
|
count++;
|
|
941
1150
|
continue;
|
|
942
1151
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
options: {
|
|
952
|
-
include: options.policiesIncluded,
|
|
953
|
-
exclude: options.policiesExcluded
|
|
954
|
-
}
|
|
955
|
-
});
|
|
956
|
-
} catch (e) {
|
|
957
|
-
if (e instanceof PolicyError) {
|
|
958
|
-
lastError = PermissionError.evaluationFailed({
|
|
959
|
-
name: entity.name,
|
|
960
|
-
policy: entity.policy,
|
|
961
|
-
policyError: e
|
|
962
|
-
});
|
|
963
|
-
} else {
|
|
964
|
-
lastError = PermissionError.evaluationFailed({
|
|
965
|
-
name: entity.name,
|
|
966
|
-
policy: entity.policy
|
|
967
|
-
});
|
|
968
|
-
}
|
|
969
|
-
outcome = false;
|
|
970
|
-
}
|
|
971
|
-
if (outcome) {
|
|
1152
|
+
const data = dataBase.clone();
|
|
1153
|
+
data.set(BuiltInPolicyType.PERMISSION_BINDING, entity);
|
|
1154
|
+
const evaluationResult = await this.policyEngine.evaluate(entity.policy, definePolicyEvaluationContext({
|
|
1155
|
+
include: options.policiesIncluded,
|
|
1156
|
+
exclude: options.policiesExcluded,
|
|
1157
|
+
data
|
|
1158
|
+
}));
|
|
1159
|
+
if (evaluationResult.success) {
|
|
972
1160
|
if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
|
|
973
1161
|
return;
|
|
974
1162
|
}
|
|
975
1163
|
count++;
|
|
976
1164
|
} else {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
1165
|
+
issues.push(definePolicyIssueGroup({
|
|
1166
|
+
code: ErrorCode.PERMISSION_EVALUATION_FAILED,
|
|
1167
|
+
issues: evaluationResult.issues || [],
|
|
1168
|
+
message: `The ${entity.name} permissions policy evaluation failed`,
|
|
1169
|
+
path: [
|
|
1170
|
+
entity.name
|
|
1171
|
+
]
|
|
1172
|
+
}));
|
|
983
1173
|
if (decisionStrategy === DecisionStrategy.UNANIMOUS) {
|
|
984
|
-
|
|
1174
|
+
const error = PermissionError.evaluationFailed(entity.name);
|
|
1175
|
+
error.addIssues(issues);
|
|
1176
|
+
throw error;
|
|
985
1177
|
}
|
|
986
1178
|
count--;
|
|
987
1179
|
}
|
|
@@ -989,10 +1181,12 @@ class PermissionChecker {
|
|
|
989
1181
|
if (count > 0) {
|
|
990
1182
|
return;
|
|
991
1183
|
}
|
|
992
|
-
if (
|
|
1184
|
+
if (issues.length === 0) {
|
|
993
1185
|
throw PermissionError.deniedAll(ctx.name);
|
|
994
1186
|
} else {
|
|
995
|
-
|
|
1187
|
+
const error = PermissionError.evaluationFailed(ctx.name);
|
|
1188
|
+
error.addIssues(issues);
|
|
1189
|
+
throw error;
|
|
996
1190
|
}
|
|
997
1191
|
}
|
|
998
1192
|
/**
|
|
@@ -1035,5 +1229,5 @@ class PermissionChecker {
|
|
|
1035
1229
|
}
|
|
1036
1230
|
}
|
|
1037
1231
|
|
|
1038
|
-
export { AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, DecisionStrategy, IdentityPolicyEvaluator, IdentityPolicyValidator, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionChecker, PermissionError,
|
|
1232
|
+
export { AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, DecisionStrategy, IdentityPolicyEvaluator, IdentityPolicyValidator, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionChecker, PermissionError, PermissionMemoryRepository, PolicyData, PolicyDefaultEvaluators, PolicyEngine, PolicyError, PolicyIssueCode, PolicyIssueSeverity, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, buildPermissionItemKey, defineAttributesPolicy, definePolicyEvaluationContext, definePolicyIssueGroup, definePolicyIssueItem, definePolicyWithType, isCompositePolicy, isIntervalForDayOfMonth, isIntervalForDayOfWeek, isIntervalForDayOfYear, isPermissionItemEqual, maybeInvertPolicyOutcome, mergePermissionItems };
|
|
1039
1233
|
//# sourceMappingURL=index.mjs.map
|