@authup/access 1.0.0-beta.24

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.
Files changed (144) hide show
  1. package/LICENSE +201 -0
  2. package/dist/constants.d.ts +15 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/index.cjs +1007 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.mjs +974 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/permission/error/index.d.ts +3 -0
  11. package/dist/permission/error/index.d.ts.map +1 -0
  12. package/dist/permission/error/module.d.ts +13 -0
  13. package/dist/permission/error/module.d.ts.map +1 -0
  14. package/dist/permission/error/types.d.ts +11 -0
  15. package/dist/permission/error/types.d.ts.map +1 -0
  16. package/dist/permission/helpers/equal.d.ts +3 -0
  17. package/dist/permission/helpers/equal.d.ts.map +1 -0
  18. package/dist/permission/helpers/index.d.ts +3 -0
  19. package/dist/permission/helpers/index.d.ts.map +1 -0
  20. package/dist/permission/helpers/merge.d.ts +3 -0
  21. package/dist/permission/helpers/merge.d.ts.map +1 -0
  22. package/dist/permission/index.d.ts +6 -0
  23. package/dist/permission/index.d.ts.map +1 -0
  24. package/dist/permission/module.d.ts +34 -0
  25. package/dist/permission/module.d.ts.map +1 -0
  26. package/dist/permission/provider/index.d.ts +3 -0
  27. package/dist/permission/provider/index.d.ts.map +1 -0
  28. package/dist/permission/provider/memory.d.ts +9 -0
  29. package/dist/permission/provider/memory.d.ts.map +1 -0
  30. package/dist/permission/provider/types.d.ts +9 -0
  31. package/dist/permission/provider/types.d.ts.map +1 -0
  32. package/dist/permission/types.d.ts +24 -0
  33. package/dist/permission/types.d.ts.map +1 -0
  34. package/dist/policy/built-in/attribute-names/evaluator.d.ts +13 -0
  35. package/dist/policy/built-in/attribute-names/evaluator.d.ts.map +1 -0
  36. package/dist/policy/built-in/attribute-names/index.d.ts +4 -0
  37. package/dist/policy/built-in/attribute-names/index.d.ts.map +1 -0
  38. package/dist/policy/built-in/attribute-names/types.d.ts +5 -0
  39. package/dist/policy/built-in/attribute-names/types.d.ts.map +1 -0
  40. package/dist/policy/built-in/attribute-names/validator.d.ts +6 -0
  41. package/dist/policy/built-in/attribute-names/validator.d.ts.map +1 -0
  42. package/dist/policy/built-in/attributes/evaluator.d.ts +14 -0
  43. package/dist/policy/built-in/attributes/evaluator.d.ts.map +1 -0
  44. package/dist/policy/built-in/attributes/index.d.ts +4 -0
  45. package/dist/policy/built-in/attributes/index.d.ts.map +1 -0
  46. package/dist/policy/built-in/attributes/types.d.ts +7 -0
  47. package/dist/policy/built-in/attributes/types.d.ts.map +1 -0
  48. package/dist/policy/built-in/attributes/validator.d.ts +7 -0
  49. package/dist/policy/built-in/attributes/validator.d.ts.map +1 -0
  50. package/dist/policy/built-in/composite/evaluator.d.ts +13 -0
  51. package/dist/policy/built-in/composite/evaluator.d.ts.map +1 -0
  52. package/dist/policy/built-in/composite/helper.d.ts +4 -0
  53. package/dist/policy/built-in/composite/helper.d.ts.map +1 -0
  54. package/dist/policy/built-in/composite/index.d.ts +5 -0
  55. package/dist/policy/built-in/composite/index.d.ts.map +1 -0
  56. package/dist/policy/built-in/composite/types.d.ts +13 -0
  57. package/dist/policy/built-in/composite/types.d.ts.map +1 -0
  58. package/dist/policy/built-in/composite/validator.d.ts +7 -0
  59. package/dist/policy/built-in/composite/validator.d.ts.map +1 -0
  60. package/dist/policy/built-in/constants.d.ts +35 -0
  61. package/dist/policy/built-in/constants.d.ts.map +1 -0
  62. package/dist/policy/built-in/date/evaluator.d.ts +13 -0
  63. package/dist/policy/built-in/date/evaluator.d.ts.map +1 -0
  64. package/dist/policy/built-in/date/index.d.ts +4 -0
  65. package/dist/policy/built-in/date/index.d.ts.map +1 -0
  66. package/dist/policy/built-in/date/types.d.ts +6 -0
  67. package/dist/policy/built-in/date/types.d.ts.map +1 -0
  68. package/dist/policy/built-in/date/validator.d.ts +7 -0
  69. package/dist/policy/built-in/date/validator.d.ts.map +1 -0
  70. package/dist/policy/built-in/identity/evaluator.d.ts +13 -0
  71. package/dist/policy/built-in/identity/evaluator.d.ts.map +1 -0
  72. package/dist/policy/built-in/identity/index.d.ts +4 -0
  73. package/dist/policy/built-in/identity/index.d.ts.map +1 -0
  74. package/dist/policy/built-in/identity/types.d.ts +8 -0
  75. package/dist/policy/built-in/identity/types.d.ts.map +1 -0
  76. package/dist/policy/built-in/identity/validator.d.ts +6 -0
  77. package/dist/policy/built-in/identity/validator.d.ts.map +1 -0
  78. package/dist/policy/built-in/index.d.ts +11 -0
  79. package/dist/policy/built-in/index.d.ts.map +1 -0
  80. package/dist/policy/built-in/permission-binding/index.d.ts +4 -0
  81. package/dist/policy/built-in/permission-binding/index.d.ts.map +1 -0
  82. package/dist/policy/built-in/permission-binding/module.d.ts +13 -0
  83. package/dist/policy/built-in/permission-binding/module.d.ts.map +1 -0
  84. package/dist/policy/built-in/permission-binding/types.d.ts +4 -0
  85. package/dist/policy/built-in/permission-binding/types.d.ts.map +1 -0
  86. package/dist/policy/built-in/permission-binding/validator.d.ts +6 -0
  87. package/dist/policy/built-in/permission-binding/validator.d.ts.map +1 -0
  88. package/dist/policy/built-in/realm-match/evaluator.d.ts +13 -0
  89. package/dist/policy/built-in/realm-match/evaluator.d.ts.map +1 -0
  90. package/dist/policy/built-in/realm-match/index.d.ts +4 -0
  91. package/dist/policy/built-in/realm-match/index.d.ts.map +1 -0
  92. package/dist/policy/built-in/realm-match/types.d.ts +28 -0
  93. package/dist/policy/built-in/realm-match/types.d.ts.map +1 -0
  94. package/dist/policy/built-in/realm-match/validator.d.ts +7 -0
  95. package/dist/policy/built-in/realm-match/validator.d.ts.map +1 -0
  96. package/dist/policy/built-in/time/constants.d.ts +7 -0
  97. package/dist/policy/built-in/time/constants.d.ts.map +1 -0
  98. package/dist/policy/built-in/time/evaluator.d.ts +13 -0
  99. package/dist/policy/built-in/time/evaluator.d.ts.map +1 -0
  100. package/dist/policy/built-in/time/index.d.ts +5 -0
  101. package/dist/policy/built-in/time/index.d.ts.map +1 -0
  102. package/dist/policy/built-in/time/types.d.ts +29 -0
  103. package/dist/policy/built-in/time/types.d.ts.map +1 -0
  104. package/dist/policy/built-in/time/validator.d.ts +7 -0
  105. package/dist/policy/built-in/time/validator.d.ts.map +1 -0
  106. package/dist/policy/built-in/types.d.ts +7 -0
  107. package/dist/policy/built-in/types.d.ts.map +1 -0
  108. package/dist/policy/engine/index.d.ts +3 -0
  109. package/dist/policy/engine/index.d.ts.map +1 -0
  110. package/dist/policy/engine/module.d.ts +20 -0
  111. package/dist/policy/engine/module.d.ts.map +1 -0
  112. package/dist/policy/engine/types.d.ts +4 -0
  113. package/dist/policy/engine/types.d.ts.map +1 -0
  114. package/dist/policy/error/index.d.ts +2 -0
  115. package/dist/policy/error/index.d.ts.map +1 -0
  116. package/dist/policy/error/module.d.ts +8 -0
  117. package/dist/policy/error/module.d.ts.map +1 -0
  118. package/dist/policy/evaluator/data/identity/index.d.ts +2 -0
  119. package/dist/policy/evaluator/data/identity/index.d.ts.map +1 -0
  120. package/dist/policy/evaluator/data/identity/validator.d.ts +8 -0
  121. package/dist/policy/evaluator/data/identity/validator.d.ts.map +1 -0
  122. package/dist/policy/evaluator/data/index.d.ts +4 -0
  123. package/dist/policy/evaluator/data/index.d.ts.map +1 -0
  124. package/dist/policy/evaluator/data/permission/index.d.ts +2 -0
  125. package/dist/policy/evaluator/data/permission/index.d.ts.map +1 -0
  126. package/dist/policy/evaluator/data/permission/validator.d.ts +8 -0
  127. package/dist/policy/evaluator/data/permission/validator.d.ts.map +1 -0
  128. package/dist/policy/evaluator/data/validator.d.ts +8 -0
  129. package/dist/policy/evaluator/data/validator.d.ts.map +1 -0
  130. package/dist/policy/evaluator/evaluate.d.ts +4 -0
  131. package/dist/policy/evaluator/evaluate.d.ts.map +1 -0
  132. package/dist/policy/evaluator/index.d.ts +4 -0
  133. package/dist/policy/evaluator/index.d.ts.map +1 -0
  134. package/dist/policy/evaluator/types.d.ts +44 -0
  135. package/dist/policy/evaluator/types.d.ts.map +1 -0
  136. package/dist/policy/helpers/index.d.ts +2 -0
  137. package/dist/policy/helpers/index.d.ts.map +1 -0
  138. package/dist/policy/helpers/invert.d.ts +4 -0
  139. package/dist/policy/helpers/invert.d.ts.map +1 -0
  140. package/dist/policy/index.d.ts +7 -0
  141. package/dist/policy/index.d.ts.map +1 -0
  142. package/dist/policy/types.d.ts +36 -0
  143. package/dist/policy/types.d.ts.map +1 -0
  144. package/package.json +59 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,974 @@
1
+ import { ErrorCode } from '@authup/errors';
2
+ import { isObject, flattenObject, hasOwnProperty } from '@authup/kit';
3
+ import { createValidator } from '@validup/adapter-zod';
4
+ import { Container } from 'validup';
5
+ import { z } from 'zod';
6
+ import { guard } from '@ucast/mongo2js';
7
+
8
+ class PermissionError extends Error {
9
+ static notFound(name) {
10
+ return new PermissionError({
11
+ message: `The permission ${name} was not found.`,
12
+ code: ErrorCode.PERMISSION_NOT_FOUND
13
+ });
14
+ }
15
+ static denied(name) {
16
+ return new PermissionError({
17
+ message: `The permission ${name} has not been granted.`,
18
+ code: ErrorCode.PERMISSION_DENIED
19
+ });
20
+ }
21
+ static deniedAll(names) {
22
+ return new PermissionError({
23
+ message: `None of the permissions ${names.join(', ')} has been granted.`,
24
+ code: ErrorCode.PERMISSION_DENIED
25
+ });
26
+ }
27
+ static evaluationFailed(options) {
28
+ const error = new PermissionError({
29
+ message: `The evaluation of permission ${options.name} failed.`,
30
+ code: ErrorCode.PERMISSION_EVALUATION_FAILED
31
+ });
32
+ if (options.policy) {
33
+ error.policy = options.policy;
34
+ }
35
+ if (options.policyError) {
36
+ error.policyError = options.policyError;
37
+ }
38
+ return error;
39
+ }
40
+ constructor(options = {}){
41
+ super(options.message || 'A permission error occurred.');
42
+ this.code = options.code ?? null;
43
+ }
44
+ }
45
+
46
+ /*
47
+ * Copyright (c) 2024-2024.
48
+ * Author Peter Placzek (tada5hi)
49
+ * For the full copyright and license information,
50
+ * view the LICENSE file that was distributed with this source code.
51
+ */ function isPermissionItemEqual(a, b) {
52
+ if (a.name !== b.name) {
53
+ return false;
54
+ }
55
+ if (typeof a.realm_id === 'string' || typeof b.realm_id === 'string') {
56
+ return a.realm_id === b.realm_id;
57
+ }
58
+ return !!a.realm_id === !!b.realm_id;
59
+ }
60
+
61
+ /*
62
+ * Copyright (c) 2024-2024.
63
+ * Author Peter Placzek (tada5hi)
64
+ * For the full copyright and license information,
65
+ * view the LICENSE file that was distributed with this source code.
66
+ */ var DecisionStrategy = /*#__PURE__*/ function(DecisionStrategy) {
67
+ /**
68
+ * One or more positive
69
+ */ DecisionStrategy["AFFIRMATIVE"] = "affirmative";
70
+ /**
71
+ * All positive
72
+ */ DecisionStrategy["UNANIMOUS"] = "unanimous";
73
+ /**
74
+ * More positive than negative
75
+ */ DecisionStrategy["CONSENSUS"] = "consensus";
76
+ return DecisionStrategy;
77
+ }({});
78
+
79
+ class PolicyError extends Error {
80
+ static evaluatorNotFound(type) {
81
+ return new PolicyError(`No evaluator is registered to handle the policy: ${type}`, ErrorCode.POLICY_EVALUATOR_NOT_FOUND);
82
+ }
83
+ static evaluatorNotProcessable() {
84
+ throw new PolicyError('The evaluator can not process the policy.', ErrorCode.POLICY_EVALUATOR_NOT_PROCESSABLE);
85
+ }
86
+ static evaluatorContextInvalid() {
87
+ throw new PolicyError('The evaluator context is not valid.', ErrorCode.POLICY_EVALUATOR_CONTEXT_INVALID);
88
+ }
89
+ constructor(message, code){
90
+ super(message);
91
+ this.code = code ?? null;
92
+ }
93
+ }
94
+
95
+ /*
96
+ * Copyright (c) 2024-2024.
97
+ * Author Peter Placzek (tada5hi)
98
+ * For the full copyright and license information,
99
+ * view the LICENSE file that was distributed with this source code.
100
+ */ function maybeInvertPolicyOutcome(outcome, invert) {
101
+ if (typeof invert === 'undefined') {
102
+ return outcome;
103
+ }
104
+ if (typeof invert === 'function') {
105
+ return invert(outcome);
106
+ }
107
+ if (!invert) {
108
+ return outcome;
109
+ }
110
+ return !outcome;
111
+ }
112
+
113
+ /*
114
+ * Copyright (c) 2024-2024.
115
+ * Author Peter Placzek (tada5hi)
116
+ * For the full copyright and license information,
117
+ * view the LICENSE file that was distributed with this source code.
118
+ */ var BuiltInPolicyType = /*#__PURE__*/ function(BuiltInPolicyType) {
119
+ /**
120
+ * Bundles different policies.
121
+ */ BuiltInPolicyType["COMPOSITE"] = "composite";
122
+ /**
123
+ * Date Based Access Control (DBAC)
124
+ */ BuiltInPolicyType["DATE"] = "date";
125
+ /**
126
+ * Time Based Access Control (TBAC)
127
+ */ BuiltInPolicyType["TIME"] = "time";
128
+ /**
129
+ * Attribute Name Based Access Control (ANBAC)
130
+ */ BuiltInPolicyType["ATTRIBUTE_NAMES"] = "attributeNames";
131
+ /**
132
+ * Attribute Based Access Control (ABAC)
133
+ */ BuiltInPolicyType["ATTRIBUTES"] = "attributes";
134
+ /**
135
+ * Realm of Identity is for attribute realm.
136
+ */ BuiltInPolicyType["REALM_MATCH"] = "realmMatch";
137
+ /**
138
+ * Restriction based on identity (type, id, ...)
139
+ */ BuiltInPolicyType["IDENTITY"] = "identity";
140
+ /**
141
+ * Restriction based on a binding between identity <-> permission
142
+ */ BuiltInPolicyType["PERMISSION_BINDING"] = "permissionBinding";
143
+ return BuiltInPolicyType;
144
+ }({});
145
+
146
+ class AttributeNamesPolicyValidator extends Container {
147
+ initialize() {
148
+ this.mount('names', createValidator(z.array(z.string().min(3).max(128))));
149
+ this.mount('invert', createValidator(z.boolean().optional()));
150
+ }
151
+ }
152
+
153
+ class AttributeNamesPolicyEvaluator {
154
+ async can(ctx) {
155
+ return ctx.spec.type === BuiltInPolicyType.ATTRIBUTE_NAMES;
156
+ }
157
+ async validateSpecification(ctx) {
158
+ return this.validator.run(ctx.spec);
159
+ }
160
+ async validateData(ctx) {
161
+ if (!isObject(ctx.data.attributes)) {
162
+ throw PolicyError.evaluatorContextInvalid();
163
+ }
164
+ return ctx.data;
165
+ }
166
+ async evaluate(ctx) {
167
+ if (!ctx.data.attributes) {
168
+ throw PolicyError.evaluatorContextInvalid();
169
+ }
170
+ const attributes = flattenObject(ctx.data.attributes);
171
+ const keys = Object.keys(attributes);
172
+ for(let i = 0; i < keys.length; i++){
173
+ const index = ctx.spec.names.indexOf(keys[i]);
174
+ if (index === -1) {
175
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
176
+ }
177
+ }
178
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
179
+ }
180
+ constructor(){
181
+ this.validator = new AttributeNamesPolicyValidator();
182
+ }
183
+ }
184
+
185
+ class AttributesPolicyValidator extends Container {
186
+ mountAll() {
187
+ this.mount('query', createValidator(z.object({}).passthrough()));
188
+ this.mount('invert', createValidator(z.boolean().optional()));
189
+ }
190
+ constructor(options = {}){
191
+ super(options);
192
+ this.mountAll();
193
+ }
194
+ }
195
+
196
+ class AttributesPolicyEvaluator {
197
+ async can(ctx) {
198
+ return ctx.spec.type === BuiltInPolicyType.ATTRIBUTES;
199
+ }
200
+ async validateSpecification(ctx) {
201
+ return this.validator.run(ctx.spec);
202
+ }
203
+ async validateData(ctx) {
204
+ if (!isObject(ctx.data.attributes)) {
205
+ throw PolicyError.evaluatorContextInvalid();
206
+ }
207
+ return ctx.data;
208
+ }
209
+ async evaluate(ctx) {
210
+ if (!ctx.data.attributes) {
211
+ throw PolicyError.evaluatorContextInvalid();
212
+ }
213
+ this.fixQuery(ctx.spec.query);
214
+ const testIt = guard(ctx.spec.query);
215
+ return maybeInvertPolicyOutcome(testIt(ctx.data.attributes), ctx.spec.invert);
216
+ }
217
+ fixQuery(query) {
218
+ const keys = Object.keys(query);
219
+ for(let i = 0; i < keys.length; i++){
220
+ const key = keys[i];
221
+ const value = query[key];
222
+ if (isObject(value) || Array.isArray(value)) {
223
+ this.fixQuery(value);
224
+ continue;
225
+ }
226
+ if (key === '$regex' && typeof value === 'string') {
227
+ const fragments = value.match(/\/(.*?)\/([a-z]*)?$/i);
228
+ if (fragments) {
229
+ query[key] = new RegExp(fragments[1], fragments[2]);
230
+ }
231
+ }
232
+ }
233
+ }
234
+ constructor(){
235
+ this.validator = new AttributesPolicyValidator();
236
+ }
237
+ }
238
+
239
+ class PolicyDataIdentityValidator extends Container {
240
+ mountAll() {
241
+ this.mount('type', createValidator(z.string().min(3).max(128)));
242
+ this.mount('id', createValidator(z.string().uuid()));
243
+ this.mount('realmId', createValidator(z.string().uuid().optional()));
244
+ this.mount('realmName', createValidator(z.string().min(3).max(128).optional()));
245
+ }
246
+ constructor(options = {}){
247
+ super(options);
248
+ this.mountAll();
249
+ }
250
+ }
251
+
252
+ class PolicyDataPermissionValidator extends Container {
253
+ mountAll() {
254
+ this.mount('name', createValidator(z.string().min(3).max(256)));
255
+ this.mount('realm_id', createValidator(z.string().uuid().optional()));
256
+ this.mount('policy', createValidator(z.object({
257
+ type: z.string()
258
+ }).optional()));
259
+ }
260
+ constructor(options = {}){
261
+ super(options);
262
+ this.mountAll();
263
+ }
264
+ }
265
+
266
+ class PolicyDataValidator extends Container {
267
+ mountAll() {
268
+ // todo: optionalValue should be null
269
+ const identity = new PolicyDataIdentityValidator();
270
+ this.mount('identity', {
271
+ optional: true,
272
+ optionalValue: 'undefined',
273
+ optionalInclude: true
274
+ }, identity);
275
+ // todo: optionalValue should be null
276
+ const permission = new PolicyDataPermissionValidator();
277
+ this.mount('permission', {
278
+ optional: true,
279
+ optionalValue: 'undefined',
280
+ optionalInclude: true
281
+ }, permission);
282
+ this.mount('attributes', createValidator(z.object({}).optional()));
283
+ this.mount('dateTime', createValidator(z.date().or(z.string()).or(z.number()).optional()));
284
+ }
285
+ constructor(options = {}){
286
+ super(options);
287
+ this.mountAll();
288
+ }
289
+ }
290
+
291
+ async function evaluatePolicy(ctx) {
292
+ if (ctx.options.exclude && ctx.options.exclude.length > 0 && ctx.options.exclude.indexOf(ctx.spec.type) !== -1) {
293
+ if (typeof ctx.spec.invert === 'boolean') {
294
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
295
+ }
296
+ return true;
297
+ }
298
+ if (ctx.options.include && ctx.options.include.length > 0 && ctx.options.include.indexOf(ctx.spec.type) === -1) {
299
+ if (typeof ctx.spec.invert === 'boolean') {
300
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
301
+ }
302
+ return true;
303
+ }
304
+ const evaluator = ctx.evaluators[ctx.spec.type];
305
+ if (!evaluator) {
306
+ throw PolicyError.evaluatorNotFound(ctx.spec.type);
307
+ }
308
+ try {
309
+ const canEvaluate = await evaluator.can(ctx);
310
+ if (canEvaluate) {
311
+ const spec = await evaluator.validateSpecification(ctx);
312
+ const data = await evaluator.validateData(ctx);
313
+ return await evaluator.evaluate({
314
+ ...ctx,
315
+ data,
316
+ spec
317
+ });
318
+ }
319
+ } catch (e) {
320
+ if (e instanceof PolicyError) {
321
+ throw e;
322
+ }
323
+ if (e instanceof Error) {
324
+ const error = new PolicyError(e.message);
325
+ error.stack = e.stack;
326
+ throw error;
327
+ }
328
+ throw new PolicyError();
329
+ }
330
+ throw PolicyError.evaluatorNotProcessable();
331
+ }
332
+
333
+ function isCompositePolicy(input) {
334
+ return input.type === BuiltInPolicyType.COMPOSITE;
335
+ }
336
+
337
+ class CompositePolicyValidator extends Container {
338
+ mountAll() {
339
+ this.mount('decisionStrategy', createValidator(z.nativeEnum(DecisionStrategy).optional()));
340
+ this.mount('children', createValidator(z.array(z.object({
341
+ type: z.string().min(3)
342
+ }).passthrough())));
343
+ this.mount('invert', createValidator(z.boolean().optional()));
344
+ }
345
+ constructor(options = {}){
346
+ super(options);
347
+ this.mountAll();
348
+ }
349
+ }
350
+
351
+ class CompositePolicyEvaluator {
352
+ async can(ctx) {
353
+ return isCompositePolicy(ctx.spec);
354
+ }
355
+ async validateSpecification(ctx) {
356
+ return this.validator.run(ctx.spec);
357
+ }
358
+ async validateData(ctx) {
359
+ return ctx.data;
360
+ }
361
+ async evaluate(ctx) {
362
+ let count = 0;
363
+ const decisionStrategy = ctx.spec.decisionStrategy ?? DecisionStrategy.UNANIMOUS;
364
+ for(let i = 0; i < ctx.spec.children.length; i++){
365
+ const childPolicy = ctx.spec.children[i];
366
+ let outcome;
367
+ if (isCompositePolicy(childPolicy)) {
368
+ outcome = await this.evaluate({
369
+ ...ctx,
370
+ spec: childPolicy
371
+ });
372
+ } else {
373
+ outcome = await evaluatePolicy({
374
+ ...ctx,
375
+ spec: childPolicy
376
+ });
377
+ }
378
+ if (outcome) {
379
+ if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
380
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
381
+ }
382
+ count++;
383
+ } else {
384
+ if (decisionStrategy === DecisionStrategy.UNANIMOUS) {
385
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
386
+ }
387
+ count--;
388
+ }
389
+ }
390
+ return maybeInvertPolicyOutcome(count > 0, ctx.spec.invert);
391
+ }
392
+ constructor(){
393
+ this.validator = new CompositePolicyValidator();
394
+ }
395
+ }
396
+
397
+ class DatePolicyValidator extends Container {
398
+ mountAll() {
399
+ this.mount('start', createValidator(z.date().or(z.string()).or(z.number()).optional()));
400
+ this.mount('end', createValidator(z.date().or(z.string()).or(z.number()).optional()));
401
+ this.mount('invert', createValidator(z.boolean().optional()));
402
+ }
403
+ constructor(options = {}){
404
+ super(options);
405
+ this.mountAll();
406
+ }
407
+ }
408
+
409
+ function normalizeDate$1(input) {
410
+ return new Date(input.getFullYear(), input.getMonth(), input.getDate());
411
+ }
412
+ function toDate$1(input) {
413
+ if (typeof input === 'string') {
414
+ return new Date(input);
415
+ }
416
+ if (typeof input === 'number') {
417
+ return new Date(input);
418
+ }
419
+ return input;
420
+ }
421
+ class DatePolicyEvaluator {
422
+ async can(ctx) {
423
+ return ctx.spec.type === BuiltInPolicyType.DATE;
424
+ }
425
+ async validateSpecification(ctx) {
426
+ return this.validator.run(ctx.spec);
427
+ }
428
+ async validateData(ctx) {
429
+ return ctx.data;
430
+ }
431
+ async evaluate(ctx) {
432
+ let now;
433
+ if (ctx.data.dateTime) {
434
+ now = normalizeDate$1(toDate$1(ctx.data.dateTime));
435
+ } else {
436
+ now = normalizeDate$1(new Date());
437
+ }
438
+ if (ctx.spec.start) {
439
+ const start = normalizeDate$1(toDate$1(ctx.spec.start));
440
+ if (now < start) {
441
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
442
+ }
443
+ }
444
+ if (ctx.spec.end) {
445
+ const end = normalizeDate$1(toDate$1(ctx.spec.end));
446
+ if (now > end) {
447
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
448
+ }
449
+ }
450
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
451
+ }
452
+ constructor(){
453
+ this.validator = new DatePolicyValidator();
454
+ }
455
+ }
456
+
457
+ class IdentityPolicyValidator extends Container {
458
+ initialize() {
459
+ this.mount('types', createValidator(z.array(z.string()).optional()));
460
+ this.mount('invert', createValidator(z.boolean().optional()));
461
+ }
462
+ }
463
+
464
+ class IdentityPolicyEvaluator {
465
+ async can(ctx) {
466
+ return ctx.spec.type === BuiltInPolicyType.IDENTITY;
467
+ }
468
+ async validateSpecification(ctx) {
469
+ return this.validator.run(ctx.spec);
470
+ }
471
+ async validateData(ctx) {
472
+ return ctx.data;
473
+ }
474
+ async evaluate(ctx) {
475
+ if (!isObject(ctx.data.identity)) {
476
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
477
+ }
478
+ const types = ctx.spec.types || [];
479
+ if (types.length === 0) {
480
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
481
+ }
482
+ const typeAllowed = types.indexOf(ctx.data.identity.type) !== -1;
483
+ return maybeInvertPolicyOutcome(typeAllowed, ctx.spec.invert);
484
+ }
485
+ constructor(){
486
+ this.validator = new IdentityPolicyValidator();
487
+ }
488
+ }
489
+
490
+ class PermissionBindingPolicyValidator extends Container {
491
+ initialize() {}
492
+ }
493
+
494
+ class PermissionBindingPolicyEvaluator {
495
+ async can(ctx) {
496
+ return ctx.spec.type === BuiltInPolicyType.PERMISSION_BINDING;
497
+ }
498
+ async validateSpecification(ctx) {
499
+ return this.validator.run(ctx.spec);
500
+ }
501
+ async validateData(ctx) {
502
+ if (!isObject(ctx.data.identity) && !isObject(ctx.data.permission)) {
503
+ throw PolicyError.evaluatorContextInvalid();
504
+ }
505
+ return ctx.data;
506
+ }
507
+ async evaluate(ctx) {
508
+ // todo: this must be changed when the permission-checker not only checks owned permissions.
509
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
510
+ }
511
+ constructor(){
512
+ this.validator = new PermissionBindingPolicyValidator();
513
+ }
514
+ }
515
+
516
+ class RealmMatchPolicyValidator extends Container {
517
+ mountAll() {
518
+ this.mount('decisionStrategy', createValidator(z.nativeEnum(DecisionStrategy).optional()));
519
+ this.mount('attributeName', createValidator(z.string().min(3).or(z.array(z.string().min(3))).optional()));
520
+ this.mount('attributeNameStrict', createValidator(z.boolean().optional()));
521
+ this.mount('attributeNullMatchAll', createValidator(z.boolean().optional()));
522
+ this.mount('identityMasterMatchAll', createValidator(z.boolean().optional()));
523
+ this.mount('invert', createValidator(z.boolean().optional()));
524
+ }
525
+ constructor(options = {}){
526
+ super(options);
527
+ this.mountAll();
528
+ }
529
+ }
530
+
531
+ class RealmMatchPolicyEvaluator {
532
+ async can(ctx) {
533
+ return ctx.spec.type === BuiltInPolicyType.REALM_MATCH;
534
+ }
535
+ async validateSpecification(ctx) {
536
+ return this.validator.run(ctx.spec);
537
+ }
538
+ async validateData(ctx) {
539
+ if (!isObject(ctx.data.attributes) || !isObject(ctx.data.identity)) {
540
+ throw PolicyError.evaluatorContextInvalid();
541
+ }
542
+ return ctx.data;
543
+ }
544
+ async evaluate(ctx) {
545
+ if (!ctx.data.attributes || !ctx.data.identity) {
546
+ throw PolicyError.evaluatorContextInvalid();
547
+ }
548
+ const identityMasterMatchAll = ctx.spec.identityMasterMatchAll ?? true;
549
+ if (identityMasterMatchAll && ctx.data.identity.realmName && ctx.data.identity.realmName === 'master') {
550
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
551
+ }
552
+ let keys;
553
+ if (ctx.spec.attributeName) {
554
+ if (Array.isArray(ctx.spec.attributeName)) {
555
+ keys = ctx.spec.attributeName;
556
+ } else {
557
+ keys = [
558
+ ctx.spec.attributeName
559
+ ];
560
+ }
561
+ } else {
562
+ keys = [
563
+ 'realm_id',
564
+ 'realm_name',
565
+ 'realmId',
566
+ 'realmName'
567
+ ];
568
+ ctx.spec.decisionStrategy = DecisionStrategy.CONSENSUS;
569
+ }
570
+ const attributeNameStrict = ctx.spec.attributeNameStrict ?? true;
571
+ if (!attributeNameStrict) {
572
+ const resourceKeys = Object.keys(ctx.data.attributes);
573
+ const keysToAdd = [];
574
+ for(let i = 0; i < resourceKeys.length; i++){
575
+ let contains = false;
576
+ for(let j = 0; j < keys.length; j++){
577
+ if (resourceKeys[i] !== keys[j] && resourceKeys[i].includes(keys[j])) {
578
+ contains = true;
579
+ break;
580
+ }
581
+ }
582
+ if (contains) {
583
+ keysToAdd.push(resourceKeys[i]);
584
+ }
585
+ }
586
+ if (keysToAdd.length > 0) {
587
+ keys.push(...keysToAdd);
588
+ }
589
+ }
590
+ let count = 0;
591
+ for(let i = 0; i < keys.length; i++){
592
+ if (!hasOwnProperty(ctx.data.attributes, keys[i])) {
593
+ continue;
594
+ }
595
+ let outcome = false;
596
+ const attributeValue = ctx.data.attributes[keys[i]];
597
+ if (attributeValue === null && ctx.spec.attributeNullMatchAll) {
598
+ outcome = true;
599
+ } else if (attributeValue === ctx.data.identity.realmId || attributeValue === ctx.data.identity.realmName) {
600
+ outcome = true;
601
+ }
602
+ if (outcome) {
603
+ if (ctx.spec.decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
604
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
605
+ }
606
+ count++;
607
+ } else {
608
+ if (ctx.spec.decisionStrategy === DecisionStrategy.UNANIMOUS) {
609
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
610
+ }
611
+ count--;
612
+ }
613
+ }
614
+ return maybeInvertPolicyOutcome(count > 0, ctx.spec.invert);
615
+ }
616
+ constructor(){
617
+ this.validator = new RealmMatchPolicyValidator();
618
+ }
619
+ }
620
+
621
+ /*
622
+ * Copyright (c) 2024-2024.
623
+ * Author Peter Placzek (tada5hi)
624
+ * For the full copyright and license information,
625
+ * view the LICENSE file that was distributed with this source code.
626
+ */ var TimePolicyInterval = /*#__PURE__*/ function(TimePolicyInterval) {
627
+ TimePolicyInterval["DAILY"] = "daily";
628
+ TimePolicyInterval["WEEKLY"] = "weekly";
629
+ TimePolicyInterval["MONTHLY"] = "monthly";
630
+ TimePolicyInterval["YEARLY"] = "yearly";
631
+ return TimePolicyInterval;
632
+ }({});
633
+
634
+ class TimePolicyValidator extends Container {
635
+ mountAll() {
636
+ this.mount('dayOfWeek', createValidator(z.number().min(0).max(6).optional()));
637
+ this.mount('dayOfMonth', createValidator(z.number().min(1).max(31).optional()));
638
+ this.mount('dayOfYear', createValidator(z.number().min(1).max(366).optional()));
639
+ this.mount('start', createValidator(z.date().or(z.string()).or(z.number()).optional()));
640
+ this.mount('end', createValidator(z.date().or(z.string()).or(z.number()).optional()));
641
+ this.mount('interval', createValidator(z.nativeEnum(TimePolicyInterval).optional()));
642
+ this.mount('invert', createValidator(z.boolean().optional()));
643
+ }
644
+ constructor(options = {}){
645
+ super(options);
646
+ this.mountAll();
647
+ }
648
+ }
649
+
650
+ const timeRegex = /^(?:[01]\d|2[0-3]):[0-5]\d$/;
651
+ function normalizeDate(input, dateRef) {
652
+ const date = dateRef ? new Date(dateRef) : new Date();
653
+ date.setHours(input.getHours(), input.getMinutes());
654
+ return date;
655
+ }
656
+ function toDate(input, dateRef) {
657
+ if (typeof input === 'string') {
658
+ if (timeRegex.test(input)) {
659
+ const [startHours, startMinutes] = input.split(':').map(Number);
660
+ const date = dateRef ? new Date(dateRef) : new Date();
661
+ date.setHours(startHours, startMinutes);
662
+ return date;
663
+ }
664
+ return new Date(input);
665
+ }
666
+ if (typeof input === 'number') {
667
+ return new Date(input);
668
+ }
669
+ return input;
670
+ }
671
+ class TimePolicyEvaluator {
672
+ async can(ctx) {
673
+ return ctx.spec.type === BuiltInPolicyType.TIME;
674
+ }
675
+ async validateSpecification(ctx) {
676
+ return this.validator.run(ctx.spec);
677
+ }
678
+ async validateData(ctx) {
679
+ return ctx.data;
680
+ }
681
+ async evaluate(ctx) {
682
+ let now;
683
+ if (ctx.data.dateTime) {
684
+ now = toDate(ctx.data.dateTime);
685
+ } else {
686
+ now = new Date();
687
+ }
688
+ if (ctx.spec.start) {
689
+ const start = normalizeDate(toDate(ctx.spec.start, now), now);
690
+ if (now < start) {
691
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
692
+ }
693
+ }
694
+ if (ctx.spec.end) {
695
+ const end = normalizeDate(toDate(ctx.spec.end, now), now);
696
+ if (now > end) {
697
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
698
+ }
699
+ }
700
+ if (ctx.spec.dayOfWeek) {
701
+ if (now.getDay() !== ctx.spec.dayOfWeek) {
702
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
703
+ }
704
+ }
705
+ if (ctx.spec.dayOfMonth) {
706
+ if (now.getDate() !== ctx.spec.dayOfMonth) {
707
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
708
+ }
709
+ }
710
+ if (ctx.spec.dayOfYear) {
711
+ const start = new Date(now.getFullYear(), 0, 0);
712
+ const diff = now.getTime() - start.getTime() + (start.getTimezoneOffset() - now.getTimezoneOffset()) * 60 * 1000;
713
+ const oneDay = 1000 * 60 * 60 * 24;
714
+ const dayOfYear = Math.floor(diff / oneDay);
715
+ if (dayOfYear !== ctx.spec.dayOfYear) {
716
+ return maybeInvertPolicyOutcome(false, ctx.spec.invert);
717
+ }
718
+ }
719
+ return maybeInvertPolicyOutcome(true, ctx.spec.invert);
720
+ }
721
+ constructor(){
722
+ this.validator = new TimePolicyValidator();
723
+ }
724
+ }
725
+
726
+ /**
727
+ * The policy engine is a component that interprets defined policies and makes decisions
728
+ * on whether to allow or deny a particular access.
729
+ */ class PolicyEngine {
730
+ registerEvaluator(type, evaluator) {
731
+ this.evaluators[type] = evaluator;
732
+ }
733
+ registerEvaluators(evaluators) {
734
+ const keys = Object.keys(evaluators);
735
+ for(let i = 0; i < keys.length; i++){
736
+ this.registerEvaluator(keys[i], evaluators[keys[i]]);
737
+ }
738
+ }
739
+ registerDefaultEvaluators() {
740
+ this.registerEvaluator(BuiltInPolicyType.COMPOSITE, new CompositePolicyEvaluator());
741
+ this.registerEvaluator(BuiltInPolicyType.ATTRIBUTES, new AttributesPolicyEvaluator());
742
+ this.registerEvaluator(BuiltInPolicyType.ATTRIBUTE_NAMES, new AttributeNamesPolicyEvaluator());
743
+ this.registerEvaluator(BuiltInPolicyType.DATE, new DatePolicyEvaluator());
744
+ this.registerEvaluator(BuiltInPolicyType.IDENTITY, new IdentityPolicyEvaluator());
745
+ this.registerEvaluator(BuiltInPolicyType.PERMISSION_BINDING, new PermissionBindingPolicyEvaluator());
746
+ this.registerEvaluator(BuiltInPolicyType.REALM_MATCH, new RealmMatchPolicyEvaluator());
747
+ this.registerEvaluator(BuiltInPolicyType.TIME, new TimePolicyEvaluator());
748
+ }
749
+ /**
750
+ * @throws PolicyError
751
+ *
752
+ * @param ctx
753
+ */ async evaluate(ctx) {
754
+ return evaluatePolicy({
755
+ ...ctx,
756
+ options: ctx.options || {},
757
+ evaluators: this.evaluators
758
+ });
759
+ }
760
+ constructor(evaluators = {}){
761
+ this.evaluators = {};
762
+ this.registerDefaultEvaluators();
763
+ this.registerEvaluators(evaluators);
764
+ }
765
+ }
766
+
767
+ function mergePermissionItems(input) {
768
+ const grouped = input.reduce((previous, current)=>{
769
+ const key = current.realm_id || `/${current.name}`;
770
+ if (!previous[key]) {
771
+ previous[key] = [];
772
+ }
773
+ previous[key].push(current);
774
+ return previous;
775
+ }, {});
776
+ const output = [];
777
+ const keys = Object.keys(grouped);
778
+ for(let i = 0; i < keys.length; i++){
779
+ const [permission, ...permissions] = grouped[keys[i]];
780
+ if (permissions.length > 0) {
781
+ const policy = {
782
+ type: BuiltInPolicyType.COMPOSITE,
783
+ decisionStrategy: DecisionStrategy.AFFIRMATIVE,
784
+ children: permissions.map((el)=>el.policy).filter((el)=>typeof el !== 'undefined')
785
+ };
786
+ if (permission.policy) {
787
+ policy.children.push(permission.policy);
788
+ }
789
+ if (policy.children.length > 0) {
790
+ permission.policy = policy;
791
+ }
792
+ }
793
+ output.push(permission);
794
+ }
795
+ return output;
796
+ }
797
+
798
+ class PermissionMemoryProvider {
799
+ async get(options) {
800
+ return this.items[`${options.realmId || '/'}:${options.name}`];
801
+ }
802
+ setMany(input) {
803
+ this.items = mergePermissionItems(input).reduce((prev, current)=>{
804
+ prev[`${current.realm_id || '/'}:${current.name}`] = current;
805
+ return prev;
806
+ }, {});
807
+ }
808
+ constructor(items = []){
809
+ this.items = {};
810
+ this.setMany(items);
811
+ }
812
+ }
813
+
814
+ class PermissionChecker {
815
+ // ----------------------------------------------
816
+ /**
817
+ * Get a permission.
818
+ *
819
+ * @param input
820
+ */ async get(input) {
821
+ const options = {
822
+ name: input
823
+ };
824
+ if (typeof this.realmId !== 'undefined') {
825
+ options.realmId = this.realmId;
826
+ }
827
+ return this.provider.get(options);
828
+ }
829
+ // ----------------------------------------------
830
+ /**
831
+ * Verify if one or more possible owned permissions satisfy their conditions.
832
+ *
833
+ * @throws PermissionError
834
+ *
835
+ * @param ctx
836
+ */ async check(ctx) {
837
+ if (!Array.isArray(ctx.name)) {
838
+ await this.check({
839
+ ...ctx,
840
+ name: [
841
+ ctx.name
842
+ ]
843
+ });
844
+ return;
845
+ }
846
+ const { options = {} } = ctx;
847
+ const decisionStrategy = options.decisionStrategy ?? DecisionStrategy.UNANIMOUS;
848
+ let lastError;
849
+ let count = 0;
850
+ for(let i = 0; i < ctx.name.length; i++){
851
+ const entity = await this.get(ctx.name[i]);
852
+ if (!entity) {
853
+ throw PermissionError.notFound(ctx.name[i]);
854
+ }
855
+ if (!entity.policy) {
856
+ if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
857
+ return;
858
+ }
859
+ count++;
860
+ continue;
861
+ }
862
+ let outcome;
863
+ try {
864
+ outcome = await this.policyEngine.evaluate({
865
+ data: {
866
+ ...ctx.data || {},
867
+ permission: entity
868
+ },
869
+ spec: entity.policy,
870
+ options: {
871
+ include: options.policiesIncluded,
872
+ exclude: options.policiesExcluded
873
+ }
874
+ });
875
+ } catch (e) {
876
+ if (e instanceof PolicyError) {
877
+ lastError = PermissionError.evaluationFailed({
878
+ name: entity.name,
879
+ policy: entity.policy,
880
+ policyError: e
881
+ });
882
+ } else {
883
+ lastError = PermissionError.evaluationFailed({
884
+ name: entity.name,
885
+ policy: entity.policy
886
+ });
887
+ }
888
+ outcome = false;
889
+ }
890
+ if (outcome) {
891
+ if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) {
892
+ return;
893
+ }
894
+ count++;
895
+ } else {
896
+ if (!lastError) {
897
+ lastError = PermissionError.evaluationFailed({
898
+ name: entity.name,
899
+ policy: entity.policy
900
+ });
901
+ }
902
+ if (decisionStrategy === DecisionStrategy.UNANIMOUS) {
903
+ throw lastError;
904
+ }
905
+ count--;
906
+ }
907
+ }
908
+ if (count > 0) {
909
+ return;
910
+ }
911
+ if (count > 1 || !lastError) {
912
+ throw PermissionError.deniedAll(ctx.name);
913
+ } else {
914
+ throw lastError;
915
+ }
916
+ }
917
+ /**
918
+ * Verify if one of the permissions evaluates to true.
919
+ *
920
+ * @throws PermissionError
921
+ *
922
+ * @param ctx
923
+ */ async checkOneOf(ctx) {
924
+ return this.check({
925
+ ...ctx,
926
+ options: {
927
+ ...ctx.options || {},
928
+ decisionStrategy: DecisionStrategy.AFFIRMATIVE
929
+ }
930
+ });
931
+ }
932
+ // ----------------------------------------------
933
+ async preCheck(ctx) {
934
+ return this.check({
935
+ ...ctx,
936
+ options: {
937
+ ...ctx.options || {},
938
+ policiesExcluded: [
939
+ BuiltInPolicyType.ATTRIBUTES,
940
+ BuiltInPolicyType.ATTRIBUTE_NAMES,
941
+ BuiltInPolicyType.REALM_MATCH
942
+ ]
943
+ }
944
+ });
945
+ }
946
+ async preCheckOneOf(ctx) {
947
+ return this.preCheck({
948
+ ...ctx,
949
+ options: {
950
+ ...ctx.options || {},
951
+ decisionStrategy: DecisionStrategy.AFFIRMATIVE
952
+ }
953
+ });
954
+ }
955
+ // ----------------------------------------------
956
+ constructor(options = {}){
957
+ if (options.provider) {
958
+ this.provider = options.provider;
959
+ } else {
960
+ this.provider = new PermissionMemoryProvider();
961
+ }
962
+ if (options.realmId) {
963
+ this.realmId = options.realmId;
964
+ }
965
+ if (options.policyEngine) {
966
+ this.policyEngine = options.policyEngine;
967
+ } else {
968
+ this.policyEngine = new PolicyEngine();
969
+ }
970
+ }
971
+ }
972
+
973
+ export { AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, DecisionStrategy, IdentityPolicyEvaluator, IdentityPolicyValidator, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionChecker, PermissionError, PermissionMemoryProvider, PolicyDataIdentityValidator, PolicyDataPermissionValidator, PolicyDataValidator, PolicyEngine, PolicyError, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, evaluatePolicy, isCompositePolicy, isPermissionItemEqual, maybeInvertPolicyOutcome, mergePermissionItems };
974
+ //# sourceMappingURL=index.mjs.map