@authup/access 1.0.0-beta.50 → 1.0.0-beta.52
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +141 -18
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +252 -47
- package/dist/index.mjs.map +1 -1
- package/dist/permission/evaluator/module.d.ts +2 -2
- package/dist/permission/evaluator/module.d.ts.map +1 -1
- package/dist/permission/evaluator/types.d.ts +1 -1
- package/dist/permission/evaluator/types.d.ts.map +1 -1
- package/dist/permission/helpers/aggregate.d.ts +10 -0
- package/dist/permission/helpers/aggregate.d.ts.map +1 -0
- package/dist/permission/helpers/equal.d.ts +6 -2
- package/dist/permission/helpers/equal.d.ts.map +1 -1
- package/dist/permission/helpers/grant.d.ts +9 -0
- package/dist/permission/helpers/grant.d.ts.map +1 -0
- package/dist/permission/helpers/index.d.ts +2 -1
- package/dist/permission/helpers/index.d.ts.map +1 -1
- package/dist/permission/index.d.ts +1 -0
- package/dist/permission/index.d.ts.map +1 -1
- package/dist/permission/provider/memory.d.ts +3 -3
- package/dist/permission/provider/memory.d.ts.map +1 -1
- package/dist/permission/provider/types.d.ts +2 -2
- package/dist/permission/provider/types.d.ts.map +1 -1
- package/dist/permission/realm-scope/constants.d.ts +20 -0
- package/dist/permission/realm-scope/constants.d.ts.map +1 -0
- package/dist/permission/realm-scope/helpers.d.ts +23 -0
- package/dist/permission/realm-scope/helpers.d.ts.map +1 -0
- package/dist/permission/realm-scope/index.d.ts +3 -0
- package/dist/permission/realm-scope/index.d.ts.map +1 -0
- package/dist/permission/types.d.ts +37 -0
- package/dist/permission/types.d.ts.map +1 -1
- package/dist/policy/built-in/constants.d.ts +0 -2
- package/dist/policy/built-in/constants.d.ts.map +1 -1
- package/dist/policy/built-in/realm-match/evaluator.d.ts.map +1 -1
- package/dist/policy/built-in/realm-match/types.d.ts +6 -3
- package/dist/policy/built-in/realm-match/types.d.ts.map +1 -1
- package/dist/policy/built-in/realm-match/validator.d.ts.map +1 -1
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/input.d.ts +26 -0
- package/dist/policy/input.d.ts.map +1 -0
- package/package.json +6 -10
- package/dist/permission/helpers/merge.d.ts +0 -3
- package/dist/permission/helpers/merge.d.ts.map +0 -1
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuthupError, ErrorCode, hasInstanceof, isAuthupError, markInstanceof } from "@authup/errors";
|
|
2
2
|
import { DecisionStrategy, flattenObject, hasOwnProperty, isObject } from "@authup/kit";
|
|
3
|
+
import { isEqual } from "smob";
|
|
3
4
|
import { Container, defineIssueGroup, defineIssueItem } from "validup";
|
|
4
5
|
import { createValidator } from "@validup/zod";
|
|
5
6
|
import { z } from "zod";
|
|
@@ -58,12 +59,102 @@ function isPermissionError(input) {
|
|
|
58
59
|
return input.code === ErrorCode.PERMISSION_NOT_FOUND || input.code === ErrorCode.PERMISSION_DENIED || input.code === ErrorCode.PERMISSION_EVALUATION_FAILED;
|
|
59
60
|
}
|
|
60
61
|
//#endregion
|
|
61
|
-
//#region src/permission/
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
//#region src/permission/realm-scope/constants.ts
|
|
63
|
+
/**
|
|
64
|
+
* Actor-relative realm reach of a permission grant.
|
|
65
|
+
*
|
|
66
|
+
* Total order (least -> most permissive): none < own < ownOrNull < any.
|
|
67
|
+
* A missing/unknown column coerces to `own` (fail-closed); `none` is only ever an
|
|
68
|
+
* explicit value (the grant reaches no realm at all).
|
|
69
|
+
*/
|
|
70
|
+
let RealmScope = /* @__PURE__ */ function(RealmScope) {
|
|
71
|
+
/** No reach — the grant matches no resource realm. */
|
|
72
|
+
RealmScope["NONE"] = "none";
|
|
73
|
+
/** Own realm only — resource realm must equal the actor's realm. */
|
|
74
|
+
RealmScope["OWN"] = "own";
|
|
75
|
+
/** Own realm or null/global resources (realm_id === null). */
|
|
76
|
+
RealmScope["OWN_OR_NULL"] = "ownOrNull";
|
|
77
|
+
/** Any realm, including null/global. */
|
|
78
|
+
RealmScope["ANY"] = "any";
|
|
79
|
+
return RealmScope;
|
|
80
|
+
}({});
|
|
81
|
+
/** Numeric restrictiveness order — internal to the fold/compare helpers. */
|
|
82
|
+
const REALM_SCOPE_ORDER = {
|
|
83
|
+
["none"]: 0,
|
|
84
|
+
["own"]: 1,
|
|
85
|
+
["ownOrNull"]: 2,
|
|
86
|
+
["any"]: 3
|
|
87
|
+
};
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/permission/realm-scope/helpers.ts
|
|
90
|
+
/**
|
|
91
|
+
* Coerce an arbitrary value to a RealmScope, defaulting to `own` for missing /
|
|
92
|
+
* null / unknown input (fail-closed). `none` is honoured only when explicit.
|
|
93
|
+
*/
|
|
94
|
+
function normalizeRealmScope(value) {
|
|
95
|
+
return value === "any" || value === "ownOrNull" || value === "none" ? value : "own";
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Numeric comparison of two realm scopes by restrictiveness.
|
|
99
|
+
* Negative when `a` is more restrictive than `b`, positive when less.
|
|
100
|
+
*/
|
|
101
|
+
function compareRealmScope(a, b) {
|
|
102
|
+
return REALM_SCOPE_ORDER[normalizeRealmScope(a)] - REALM_SCOPE_ORDER[normalizeRealmScope(b)];
|
|
103
|
+
}
|
|
104
|
+
/** Fold scopes to the MOST permissive (ordered max). Empty folds to `own`. */
|
|
105
|
+
function maxRealmScope(values) {
|
|
106
|
+
let max = "none";
|
|
107
|
+
let seen = false;
|
|
108
|
+
for (const value of values) {
|
|
109
|
+
seen = true;
|
|
110
|
+
const scope = normalizeRealmScope(value);
|
|
111
|
+
if (REALM_SCOPE_ORDER[scope] > REALM_SCOPE_ORDER[max]) max = scope;
|
|
112
|
+
}
|
|
113
|
+
return seen ? max : "own";
|
|
114
|
+
}
|
|
115
|
+
/** Fold scopes to the MORE restrictive (ordered min) — used to CAP a grant. Empty folds to `own`. */
|
|
116
|
+
function minRealmScope(values) {
|
|
117
|
+
let min = "any";
|
|
118
|
+
let seen = false;
|
|
119
|
+
for (const value of values) {
|
|
120
|
+
seen = true;
|
|
121
|
+
const scope = normalizeRealmScope(value);
|
|
122
|
+
if (REALM_SCOPE_ORDER[scope] < REALM_SCOPE_ORDER[min]) min = scope;
|
|
123
|
+
}
|
|
124
|
+
return seen ? min : "own";
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Whether `scope` reaches a single resource realm `resourceRealmId` for an actor whose
|
|
128
|
+
* realm is `identityRealmId`/`identityRealmName`.
|
|
129
|
+
*
|
|
130
|
+
* - `any` -> always.
|
|
131
|
+
* - `none` -> never.
|
|
132
|
+
* - realm-less actor (no realmId and no realmName) -> never `own`/`ownOrNull`.
|
|
133
|
+
* - resource realm `null` (global) -> only `ownOrNull`.
|
|
134
|
+
* - concrete resource realm -> must equal the actor's own realm (by id or name).
|
|
135
|
+
*/
|
|
136
|
+
function matchesSingle(normalized, resourceRealmId, identityRealmId, identityRealmName) {
|
|
137
|
+
if (resourceRealmId === null) return normalized === "ownOrNull";
|
|
138
|
+
return resourceRealmId === identityRealmId || resourceRealmId === identityRealmName;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Whether `scope` reaches the resource. The resource realm may be presented as a single
|
|
142
|
+
* id, `null` (global), or an array of realm ids — the array form (a resource that spans
|
|
143
|
+
* multiple realms) requires the scope to reach EVERY listed realm (unanimous, fail-closed),
|
|
144
|
+
* so any app/entity can express its realm via one or many keys under the canonical contract.
|
|
145
|
+
*/
|
|
146
|
+
function realmScopeMatches(scope, resourceRealmId, identityRealmId, identityRealmName) {
|
|
147
|
+
const normalized = normalizeRealmScope(scope);
|
|
148
|
+
if (normalized === "any") return true;
|
|
149
|
+
if (normalized === "none") return false;
|
|
150
|
+
const realmId = identityRealmId ?? null;
|
|
151
|
+
const realmName = identityRealmName ?? null;
|
|
152
|
+
if (realmId === null && realmName === null) return false;
|
|
153
|
+
if (Array.isArray(resourceRealmId)) {
|
|
154
|
+
if (resourceRealmId.length === 0) return false;
|
|
155
|
+
return resourceRealmId.every((id) => matchesSingle(normalized, id ?? null, realmId, realmName));
|
|
156
|
+
}
|
|
157
|
+
return matchesSingle(normalized, resourceRealmId ?? null, realmId, realmName);
|
|
67
158
|
}
|
|
68
159
|
//#endregion
|
|
69
160
|
//#region src/permission/helpers/key.ts
|
|
@@ -74,8 +165,34 @@ function buildPermissionKey(input) {
|
|
|
74
165
|
return `${formatKeySegment(input.realm_id)}/${formatKeySegment(input.client_id)}/${input.name}`;
|
|
75
166
|
}
|
|
76
167
|
//#endregion
|
|
77
|
-
//#region src/permission/helpers/
|
|
78
|
-
|
|
168
|
+
//#region src/permission/helpers/aggregate.ts
|
|
169
|
+
/**
|
|
170
|
+
* One disjunction term per raw binding: the grant's realm reach (fail-closed `own` default)
|
|
171
|
+
* paired with its policy — the raw single policy (its `id` preserved for junction propagation),
|
|
172
|
+
* a composite over multiple policies (Layer-1 permission policies, combined under the
|
|
173
|
+
* permission's decision_strategy), or `undefined` when the grant is unrestricted.
|
|
174
|
+
*/
|
|
175
|
+
function buildGrant(binding) {
|
|
176
|
+
let policy;
|
|
177
|
+
if (binding.policies && binding.policies.length === 1) policy = binding.policies[0];
|
|
178
|
+
else if (binding.policies && binding.policies.length > 1) policy = {
|
|
179
|
+
type: "composite",
|
|
180
|
+
decision_strategy: binding.permission.decision_strategy || DecisionStrategy.UNANIMOUS,
|
|
181
|
+
children: binding.policies
|
|
182
|
+
};
|
|
183
|
+
return {
|
|
184
|
+
realm_scope: normalizeRealmScope(binding.realm_scope),
|
|
185
|
+
policy
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Group raw permission-policy bindings by permission key into the actor's disjunction of
|
|
190
|
+
* `(realm_scope, policy)` grants per permission — with NO lossy collapse. Every consumer
|
|
191
|
+
* (the binding evaluator, isSuperset, junction propagation, the memory provider) evaluates the
|
|
192
|
+
* grant disjunction directly, so each grant's realm reach stays paired with the policy that
|
|
193
|
+
* gates it.
|
|
194
|
+
*/
|
|
195
|
+
function aggregatePermissionPolicyBindings(input) {
|
|
79
196
|
const grouped = input.reduce((previous, current) => {
|
|
80
197
|
const key = buildPermissionKey(current.permission);
|
|
81
198
|
if (!previous[key]) previous[key] = [];
|
|
@@ -83,41 +200,115 @@ function mergePermissionPolicyBindings(input) {
|
|
|
83
200
|
return previous;
|
|
84
201
|
}, {});
|
|
85
202
|
const output = [];
|
|
86
|
-
const
|
|
87
|
-
for (const key of keys) {
|
|
203
|
+
for (const key of Object.keys(grouped)) {
|
|
88
204
|
const group = grouped[key];
|
|
89
|
-
const first = group[0];
|
|
90
|
-
if (group.length === 1) {
|
|
91
|
-
output.push(first);
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
const children = [];
|
|
95
|
-
for (const element of group) {
|
|
96
|
-
if (!element.policies || element.policies.length === 0) continue;
|
|
97
|
-
const policy = {
|
|
98
|
-
type: "composite",
|
|
99
|
-
decision_strategy: element.permission.decision_strategy || DecisionStrategy.UNANIMOUS,
|
|
100
|
-
children: element.policies
|
|
101
|
-
};
|
|
102
|
-
children.push(policy);
|
|
103
|
-
}
|
|
104
|
-
let mergedPolicies;
|
|
105
|
-
if (children.length > 0 && children.length === group.length) mergedPolicies = [{
|
|
106
|
-
type: "composite",
|
|
107
|
-
decision_strategy: DecisionStrategy.AFFIRMATIVE,
|
|
108
|
-
children
|
|
109
|
-
}];
|
|
110
205
|
output.push({
|
|
111
|
-
permission:
|
|
112
|
-
|
|
113
|
-
decision_strategy: DecisionStrategy.AFFIRMATIVE
|
|
114
|
-
},
|
|
115
|
-
policies: mergedPolicies
|
|
206
|
+
permission: group[0].permission,
|
|
207
|
+
grants: group.map(buildGrant)
|
|
116
208
|
});
|
|
117
209
|
}
|
|
118
210
|
return output;
|
|
119
211
|
}
|
|
120
212
|
//#endregion
|
|
213
|
+
//#region src/permission/helpers/equal.ts
|
|
214
|
+
function isPermissionPolicyBindingEqual(a, b) {
|
|
215
|
+
if (a.permission.name !== b.permission.name) return false;
|
|
216
|
+
if ((a.permission.realm_id ?? null) !== (b.permission.realm_id ?? null)) return false;
|
|
217
|
+
if ((a.permission.client_id ?? null) !== (b.permission.client_id ?? null)) return false;
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
//#endregion
|
|
221
|
+
//#region src/permission/helpers/grant.ts
|
|
222
|
+
/**
|
|
223
|
+
* Policy fields that do NOT affect evaluation — persisted identity, audit and label columns plus
|
|
224
|
+
* the loaded `parent` / `realm` relation objects. They are stripped before the structural compare
|
|
225
|
+
* in isPolicyEquivalent so two DISTINCT rows with identical configuration still match. Verified
|
|
226
|
+
* against every built-in evaluator, which read only `type`, `invert`, `decision_strategy`,
|
|
227
|
+
* `children` and the type-specific config (query / names / types / start / end / interval /
|
|
228
|
+
* day_of_* / attribute_name* / scope). `parent` is additionally a circular back-reference and
|
|
229
|
+
* `realm` a nested object with its own id, so leaving either in would break the deep compare.
|
|
230
|
+
*
|
|
231
|
+
* SECURITY INVARIANT: every key here MUST stay non-evaluation-affecting. Adding an
|
|
232
|
+
* evaluation-relevant field would silently widen equivalence into an over-permit. New *config*
|
|
233
|
+
* fields need NOT be added — they are compared by default, which is the fail-closed direction.
|
|
234
|
+
*/
|
|
235
|
+
const NON_SEMANTIC_POLICY_KEYS = /* @__PURE__ */ new Set([
|
|
236
|
+
"id",
|
|
237
|
+
"built_in",
|
|
238
|
+
"name",
|
|
239
|
+
"display_name",
|
|
240
|
+
"description",
|
|
241
|
+
"parent_id",
|
|
242
|
+
"parent",
|
|
243
|
+
"realm_id",
|
|
244
|
+
"realm",
|
|
245
|
+
"created_at",
|
|
246
|
+
"updated_at"
|
|
247
|
+
]);
|
|
248
|
+
/**
|
|
249
|
+
* Project a policy node onto its evaluation-relevant configuration: drop the non-semantic keys
|
|
250
|
+
* and recurse through `children` (the only field that carries nested policy nodes). Config values
|
|
251
|
+
* (e.g. an `attributes` `query`) are left untouched, so a semantic `id` / `name` key *inside* a
|
|
252
|
+
* query is never mistaken for the policy's own identity column.
|
|
253
|
+
*/
|
|
254
|
+
function normalizePolicyForEquality(policy) {
|
|
255
|
+
const result = {};
|
|
256
|
+
for (const key of Object.keys(policy)) {
|
|
257
|
+
if (NON_SEMANTIC_POLICY_KEYS.has(key)) continue;
|
|
258
|
+
const value = policy[key];
|
|
259
|
+
if (key === "children" && Array.isArray(value)) {
|
|
260
|
+
result[key] = value.map((child) => normalizePolicyForEquality(child));
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
result[key] = value;
|
|
264
|
+
}
|
|
265
|
+
return result;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Whether two restriction policies are provably the SAME policy for domination purposes — by
|
|
269
|
+
* persisted identity OR by identical configuration, never by evaluated effect. Fast path: equal
|
|
270
|
+
* primary-key `id` means the same persisted row (the `typeof … === 'string'` guards keep an
|
|
271
|
+
* id-less / missing-id policy out of it — never `undefined === undefined`). Otherwise fall back to
|
|
272
|
+
* a structural value-compare of the configuration (`normalizePolicyForEquality` strips the
|
|
273
|
+
* non-semantic identity / audit / relation columns, `isEqual` deep-compares the rest), so two
|
|
274
|
+
* distinct rows with identical config — the same predicate — still dominate. Comparing by
|
|
275
|
+
* *effect* is undecidable (a policy is a predicate over PolicyData), so we accept ONLY provable
|
|
276
|
+
* identity or provable structural equality; a genuinely different configuration is treated as
|
|
277
|
+
* distinct (fail-closed) — isSuperset must never assume one restriction covers a different one.
|
|
278
|
+
*/
|
|
279
|
+
function isPolicyEquivalent(parent, child) {
|
|
280
|
+
const parentId = parent.id;
|
|
281
|
+
const childId = child.id;
|
|
282
|
+
if (typeof parentId === "string" && typeof childId === "string" && parentId === childId) return true;
|
|
283
|
+
return isEqual(normalizePolicyForEquality(parent), normalizePolicyForEquality(child));
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Whether the actor's `parent` policy provably covers the target's `child` policy on a shared
|
|
287
|
+
* permission. Conservative / fail-closed:
|
|
288
|
+
* - an unrestricted parent (no policy) covers any child;
|
|
289
|
+
* - a restricted parent NEVER covers an unrestricted child — it cannot confer the wider,
|
|
290
|
+
* policy-free reach it does not itself hold;
|
|
291
|
+
* - two restricted grants cover one another only when their policies are provably equivalent.
|
|
292
|
+
* A DIFFERENT policy is NOT assumed to be a subset (#3159: the predecessor treated any two
|
|
293
|
+
* policy-bound grants as mutually dominating, a latent over-permit across disjoint policy
|
|
294
|
+
* scopes — e.g. an actor restricted to `department=X` conferring a `department=Y` grant).
|
|
295
|
+
*/
|
|
296
|
+
function policyDominates(parent, child) {
|
|
297
|
+
if (!parent) return true;
|
|
298
|
+
if (!child) return false;
|
|
299
|
+
return isPolicyEquivalent(parent, child);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Whether the actor's `parent` grant covers the target's `child` grant: it reaches at least as
|
|
303
|
+
* far (ordered none < own < ownOrNull < any) AND its policy provably covers the child's
|
|
304
|
+
* (see `policyDominates`). Used per grant by `isSuperset` over the aggregated disjunction —
|
|
305
|
+
* every child grant must be dominated by SOME parent grant.
|
|
306
|
+
*/
|
|
307
|
+
function grantDominates(parent, child) {
|
|
308
|
+
if (compareRealmScope(parent.realm_scope, child.realm_scope) < 0) return false;
|
|
309
|
+
return policyDominates(parent.policy, child.policy);
|
|
310
|
+
}
|
|
311
|
+
//#endregion
|
|
121
312
|
//#region src/permission/provider/memory.ts
|
|
122
313
|
var PermissionMemoryProvider = class {
|
|
123
314
|
items = {};
|
|
@@ -135,7 +326,7 @@ var PermissionMemoryProvider = class {
|
|
|
135
326
|
return null;
|
|
136
327
|
}
|
|
137
328
|
setMany(input) {
|
|
138
|
-
this.items =
|
|
329
|
+
this.items = aggregatePermissionPolicyBindings(input).reduce((prev, current) => {
|
|
139
330
|
const key = buildPermissionKey(current.permission);
|
|
140
331
|
prev[key] = current;
|
|
141
332
|
return prev;
|
|
@@ -402,8 +593,6 @@ const SystemPolicyName = {
|
|
|
402
593
|
IDENTITY: "system.identity",
|
|
403
594
|
PERMISSION_BINDING: "system.permission-binding",
|
|
404
595
|
REALM_MATCH: "system.realm-match",
|
|
405
|
-
REALM_BOUND: "system.realm-bound",
|
|
406
|
-
REALM_OR_GLOBAL: "system.realm-or-global",
|
|
407
596
|
CLIENT_NAMES_SELF_MANAGE: "system.client-names-self-manage",
|
|
408
597
|
ROBOT_NAMES_SELF_MANAGE: "system.robot-names-self-manage",
|
|
409
598
|
USER_NAMES_SELF_MANAGE: "system.user-names-self-manage"
|
|
@@ -708,7 +897,7 @@ var RealmMatchPolicyValidator = class extends Container {
|
|
|
708
897
|
this.mount("attribute_name", createValidator(z.string().min(3).or(z.array(z.string().min(3))).or(z.null()).or(z.undefined()).optional()));
|
|
709
898
|
this.mount("attribute_name_strict", createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
710
899
|
this.mount("attribute_null_match_all", createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
711
|
-
this.mount("
|
|
900
|
+
this.mount("scope", createValidator(z.enum(RealmScope).or(z.null()).or(z.undefined()).optional()));
|
|
712
901
|
this.mount("invert", createValidator(z.boolean().or(z.null()).or(z.undefined()).optional()));
|
|
713
902
|
}
|
|
714
903
|
};
|
|
@@ -734,6 +923,11 @@ var RealmMatchPolicyEvaluator = class {
|
|
|
734
923
|
path: ctx.path
|
|
735
924
|
})]
|
|
736
925
|
};
|
|
926
|
+
if (policy.scope) {
|
|
927
|
+
if (!ctx.data || !ctx.data.has("realmMatch")) return { success: true };
|
|
928
|
+
const resourceRealm = ctx.data.get("realmMatch");
|
|
929
|
+
return { success: maybeInvertPolicyOutcome(realmScopeMatches(policy.scope, resourceRealm ?? null, identity.realmId, identity.realmName), policy.invert) };
|
|
930
|
+
}
|
|
737
931
|
const attributes = await this.attributesEvaluator.accessData(ctx);
|
|
738
932
|
if (!attributes) return {
|
|
739
933
|
success: false,
|
|
@@ -743,7 +937,6 @@ var RealmMatchPolicyEvaluator = class {
|
|
|
743
937
|
path: ctx.path
|
|
744
938
|
})]
|
|
745
939
|
};
|
|
746
|
-
if ((policy.identity_master_match_all ?? true) && identity.realmName && identity.realmName === "master") return { success: true };
|
|
747
940
|
let keys;
|
|
748
941
|
if (policy.attribute_name) if (Array.isArray(policy.attribute_name)) keys = policy.attribute_name;
|
|
749
942
|
else keys = [policy.attribute_name];
|
|
@@ -910,6 +1103,18 @@ const PolicyDefaultEvaluators = {
|
|
|
910
1103
|
["time"]: new TimePolicyEvaluator()
|
|
911
1104
|
};
|
|
912
1105
|
//#endregion
|
|
1106
|
+
//#region src/policy/input.ts
|
|
1107
|
+
/**
|
|
1108
|
+
* Build a {@link PolicyData} bag from a typed {@link PolicyInput} seed. Well-known keys
|
|
1109
|
+
* (identity / attributes / realmMatch) are type-checked; unknown keys are accepted as-is.
|
|
1110
|
+
*
|
|
1111
|
+
* Prefer this over `new PolicyData({ ... })` at construction sites so the key vocabulary and
|
|
1112
|
+
* per-key value types live in one place.
|
|
1113
|
+
*/
|
|
1114
|
+
function definePolicyData(input = {}) {
|
|
1115
|
+
return new PolicyData(input);
|
|
1116
|
+
}
|
|
1117
|
+
//#endregion
|
|
913
1118
|
//#region src/permission/evaluator/module.ts
|
|
914
1119
|
var PermissionEvaluator = class {
|
|
915
1120
|
provider;
|
|
@@ -942,7 +1147,7 @@ var PermissionEvaluator = class {
|
|
|
942
1147
|
const decisionStrategy = options.decisionStrategy ?? DecisionStrategy.UNANIMOUS;
|
|
943
1148
|
const issues = [];
|
|
944
1149
|
let count = 0;
|
|
945
|
-
const dataBase = ctx.
|
|
1150
|
+
const dataBase = ctx.data || new PolicyData();
|
|
946
1151
|
for (const name of ctx.name) {
|
|
947
1152
|
const binding = await this.findOne(name, {
|
|
948
1153
|
realmId: ctx.realmId,
|
|
@@ -961,8 +1166,8 @@ var PermissionEvaluator = class {
|
|
|
961
1166
|
}
|
|
962
1167
|
continue;
|
|
963
1168
|
}
|
|
964
|
-
const
|
|
965
|
-
if (
|
|
1169
|
+
const grantPolicies = binding.grants.map((grant) => grant.policy).filter((grantPolicy) => !!grantPolicy);
|
|
1170
|
+
if (grantPolicies.length < binding.grants.length) {
|
|
966
1171
|
if (decisionStrategy === DecisionStrategy.AFFIRMATIVE) return;
|
|
967
1172
|
count++;
|
|
968
1173
|
continue;
|
|
@@ -971,8 +1176,8 @@ var PermissionEvaluator = class {
|
|
|
971
1176
|
data.set("permissionBinding", binding);
|
|
972
1177
|
const compositePolicy = {
|
|
973
1178
|
type: "composite",
|
|
974
|
-
decision_strategy:
|
|
975
|
-
children:
|
|
1179
|
+
decision_strategy: DecisionStrategy.AFFIRMATIVE,
|
|
1180
|
+
children: grantPolicies
|
|
976
1181
|
};
|
|
977
1182
|
const evaluationResult = await this.policyEngine.evaluate(compositePolicy, definePolicyEvaluationContext({
|
|
978
1183
|
include: options.policiesIncluded,
|
|
@@ -1038,6 +1243,6 @@ var PermissionEvaluator = class {
|
|
|
1038
1243
|
}
|
|
1039
1244
|
};
|
|
1040
1245
|
//#endregion
|
|
1041
|
-
export { AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, IdentityPolicyEvaluator, IdentityPolicyValidator, PERMISSION_ERROR_INSTANCE, POLICY_ERROR_INSTANCE, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionError, PermissionEvaluator, PermissionMemoryProvider, PolicyData, PolicyDefaultEvaluators, PolicyEngine, PolicyError, PolicyIssueCode, PolicyIssueSeverity, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, SystemPolicyName, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, buildPermissionKey, defineAttributesPolicy, definePolicyEvaluationContext, definePolicyIssueGroup, definePolicyIssueItem, definePolicyWithType, isCompositePolicy, isIntervalForDayOfMonth, isIntervalForDayOfWeek, isIntervalForDayOfYear, isPermissionError, isPermissionPolicyBindingEqual, isPolicyError, maybeInvertPolicyOutcome,
|
|
1246
|
+
export { AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, IdentityPolicyEvaluator, IdentityPolicyValidator, PERMISSION_ERROR_INSTANCE, POLICY_ERROR_INSTANCE, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionError, PermissionEvaluator, PermissionMemoryProvider, PolicyData, PolicyDefaultEvaluators, PolicyEngine, PolicyError, PolicyIssueCode, PolicyIssueSeverity, REALM_SCOPE_ORDER, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, RealmScope, SystemPolicyName, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, aggregatePermissionPolicyBindings, buildPermissionKey, compareRealmScope, defineAttributesPolicy, definePolicyData, definePolicyEvaluationContext, definePolicyIssueGroup, definePolicyIssueItem, definePolicyWithType, grantDominates, isCompositePolicy, isIntervalForDayOfMonth, isIntervalForDayOfWeek, isIntervalForDayOfYear, isPermissionError, isPermissionPolicyBindingEqual, isPolicyError, maxRealmScope, maybeInvertPolicyOutcome, minRealmScope, normalizeRealmScope, realmScopeMatches };
|
|
1042
1247
|
|
|
1043
1248
|
//# sourceMappingURL=index.mjs.map
|