@authup/access 1.0.0-beta.68 → 1.0.0-beta.70

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 CHANGED
@@ -969,6 +969,11 @@ var DatePolicyEvaluator = class {
969
969
  };
970
970
  //#endregion
971
971
  //#region src/policy/built-in/identity/data/validator.ts
972
+ function isPermissionPolicyBinding(input) {
973
+ if (!input || typeof input !== "object") return false;
974
+ const { permission } = input;
975
+ return !!permission && typeof permission === "object" && typeof permission.name === "string";
976
+ }
972
977
  var PolicyIdentityDataValidator = class extends Container {
973
978
  initialize() {
974
979
  super.initialize();
@@ -976,6 +981,7 @@ var PolicyIdentityDataValidator = class extends Container {
976
981
  this.mount("id", createValidator(z.uuid()));
977
982
  this.mount("clientId", createValidator(z.uuid().or(z.null()).or(z.undefined()).optional()));
978
983
  this.mount("realmId", createValidator(z.uuid().or(z.null()).or(z.undefined()).optional()));
984
+ this.mount("grants", createValidator(z.array(z.custom((input) => isPermissionPolicyBinding(input))).optional()));
979
985
  this.mount("realmName", createValidator(z.string().min(3).max(128).or(z.null()).or(z.undefined()).optional()));
980
986
  }
981
987
  };
@@ -1205,12 +1211,10 @@ var IdentityPermissionBindingPolicyEvaluator = class {
1205
1211
  validator;
1206
1212
  identityEvaluator;
1207
1213
  realmMatchEvaluator;
1208
- identityPermissionProvider;
1209
- constructor(identityPermissionProvider) {
1214
+ constructor() {
1210
1215
  this.validator = new PermissionBindingPolicyValidator();
1211
1216
  this.identityEvaluator = new IdentityPolicyEvaluator();
1212
1217
  this.realmMatchEvaluator = new RealmMatchPolicyEvaluator();
1213
- this.identityPermissionProvider = identityPermissionProvider;
1214
1218
  }
1215
1219
  requires() {
1216
1220
  return ["permissionBinding"];
@@ -1223,6 +1227,18 @@ var IdentityPermissionBindingPolicyEvaluator = class {
1223
1227
  ctx.data.setValidated("permissionBinding");
1224
1228
  return data;
1225
1229
  }
1230
+ /**
1231
+ * The grants the identity holds. An identity carrying none is missing
1232
+ * data, which throws a PolicyError carrying the refusing issue.
1233
+ */
1234
+ accessGrants(ctx, identity) {
1235
+ if (!identity.grants) throw new PolicyError("The identity grants are missing", void 0, [definePolicyIssueItem({
1236
+ code: "DATA_MISSING",
1237
+ message: "The identity grants are missing",
1238
+ path: ctx.path
1239
+ })]);
1240
+ return identity.grants;
1241
+ }
1226
1242
  async evaluate(value, ctx) {
1227
1243
  const policy = await this.validator.run(value);
1228
1244
  const identity = await this.identityEvaluator.accessData(ctx);
@@ -1243,10 +1259,20 @@ var IdentityPermissionBindingPolicyEvaluator = class {
1243
1259
  path: ctx.path
1244
1260
  })]
1245
1261
  };
1246
- const identityBindings = await this.identityPermissionProvider.getFor(identity).then((bindings) => bindings.filter((item) => {
1262
+ let grants;
1263
+ try {
1264
+ grants = this.accessGrants(ctx, identity);
1265
+ } catch (e) {
1266
+ if (isPolicyError(e)) return {
1267
+ success: false,
1268
+ issues: [...e.issues]
1269
+ };
1270
+ throw e;
1271
+ }
1272
+ const identityBindings = grants.filter((item) => {
1247
1273
  if (item.permission.name !== binding.permission.name) return false;
1248
1274
  return (binding.permission.realmId ?? null) === (item.permission.realmId ?? null) && (binding.permission.clientId ?? null) === (item.permission.clientId ?? null);
1249
- }));
1275
+ });
1250
1276
  if (identityBindings.length === 0) return { success: maybeInvertPolicyOutcome(false, policy.invert) };
1251
1277
  const [aggr] = aggregatePermissionPolicyBindings(identityBindings);
1252
1278
  if (!aggr) return { success: maybeInvertPolicyOutcome(false, policy.invert) };
@@ -1689,6 +1715,105 @@ var PermissionEvaluator = class {
1689
1715
  }
1690
1716
  };
1691
1717
  //#endregion
1718
+ //#region src/permission/record/module.ts
1719
+ function describeName(name) {
1720
+ return Array.isArray(name) ? name.join(", ") : name;
1721
+ }
1722
+ /**
1723
+ * An IPolicyData that is not this package's PolicyData (another
1724
+ * implementation, or a second copy of the package). Spreading one as a record
1725
+ * would keep only its internal fields and drop every key it carries.
1726
+ */
1727
+ function isPolicyDataLike(input) {
1728
+ return isObject(input) && typeof input.has === "function" && typeof input.get === "function" && typeof input.clone === "function";
1729
+ }
1730
+ /**
1731
+ * A fresh policy data bag for one record: the caller's data is cloned, never
1732
+ * written to, so one bag can be reused across records.
1733
+ */
1734
+ function buildRecordData(name, target) {
1735
+ if (typeof target.realm === "undefined") throw new Error(`The record realm for permission ${describeName(name)} is undefined.`);
1736
+ let data;
1737
+ if (target.data instanceof PolicyData) data = target.data.clone();
1738
+ else if (isPolicyDataLike(target.data)) throw new Error(`The policy data for permission ${describeName(name)} must be a PolicyData instance of this package or a plain record.`);
1739
+ else data = new PolicyData({ ...target.data || {} });
1740
+ if (data.has("realmMatch")) throw new Error(`The policy data for permission ${describeName(name)} must not carry realmMatch.`);
1741
+ data.set("realmMatch", target.realm);
1742
+ return data;
1743
+ }
1744
+ function buildEvaluationContext(ctx) {
1745
+ return {
1746
+ name: ctx.name,
1747
+ data: buildRecordData(ctx.name, ctx),
1748
+ options: ctx.options
1749
+ };
1750
+ }
1751
+ /**
1752
+ * Throws a permission error unless `name` passes for the record: each grant's
1753
+ * `realm_scope` is matched against the record's realm and its policies are
1754
+ * evaluated against the record. An array of names must ALL pass, like
1755
+ * `evaluate()`.
1756
+ */
1757
+ async function evaluateRecord(evaluator, ctx) {
1758
+ await evaluator.evaluate(buildEvaluationContext(ctx));
1759
+ }
1760
+ /** {@link evaluateRecord} where ANY of the names suffices, like `evaluateOneOf()`. */
1761
+ async function evaluateRecordOneOf(evaluator, ctx) {
1762
+ await evaluator.evaluateOneOf(buildEvaluationContext(ctx));
1763
+ }
1764
+ async function asPredicate(fn) {
1765
+ try {
1766
+ await fn();
1767
+ return true;
1768
+ } catch (error) {
1769
+ if (isPermissionError(error)) return false;
1770
+ throw error;
1771
+ }
1772
+ }
1773
+ /**
1774
+ * {@link evaluateRecord} as a predicate. Only a permission error reads as
1775
+ * `false`; anything else propagates, so a failure on the authorization path
1776
+ * never turns into a quiet "not authorized".
1777
+ */
1778
+ async function reachesRecord(evaluator, ctx) {
1779
+ return asPredicate(() => evaluateRecord(evaluator, ctx));
1780
+ }
1781
+ /** {@link evaluateRecordOneOf} as a predicate, like {@link reachesRecord}. */
1782
+ async function reachesRecordOneOf(evaluator, ctx) {
1783
+ return asPredicate(() => evaluateRecordOneOf(evaluator, ctx));
1784
+ }
1785
+ /**
1786
+ * An update passes only when `name` passes for BOTH the stored row (the actor
1787
+ * may only change a row it can already reach) and the updated row (the row
1788
+ * must stay in reach afterwards). Evaluating the updated row alone lets an
1789
+ * update move a row INTO reach.
1790
+ */
1791
+ async function evaluateRecordUpdate(evaluator, ctx) {
1792
+ await evaluateRecord(evaluator, {
1793
+ realm: ctx.current.realm,
1794
+ data: ctx.current.data,
1795
+ name: ctx.name,
1796
+ options: ctx.options
1797
+ });
1798
+ await evaluateRecord(evaluator, {
1799
+ realm: ctx.next.realm,
1800
+ data: ctx.next.data,
1801
+ name: ctx.name,
1802
+ options: ctx.options
1803
+ });
1804
+ }
1805
+ /**
1806
+ * Whether a grant of `name` (any of, for an array) reaches every realm with
1807
+ * no narrowing policy, i.e. `realm_scope: any`. Reads the `compile()`
1808
+ * verdict, so it relies on the binding policy (`system.default`) being bound
1809
+ * to the permission: without it a grant's reach is never evaluated and any
1810
+ * grant compiles to `allow`. Fail-closed: a policy-bearing `any` grant
1811
+ * compiles to `conditional` and reads as `false`.
1812
+ */
1813
+ async function hasUnboundedReach(evaluator, name) {
1814
+ return (await evaluator.compile({ name })).verdict === "allow";
1815
+ }
1816
+ //#endregion
1692
1817
  //#region src/permission/authorization/error.ts
1693
1818
  const AUTHORIZATION_CATALOG_STALE_ERROR_INSTANCE = Symbol.for("@authup/access/AuthorizationCatalogStaleError");
1694
1819
  /**
@@ -2132,7 +2257,7 @@ async function createAuthorizationEvaluator(input, options = {}) {
2132
2257
  });
2133
2258
  }
2134
2259
  const engine = new PolicyEngine(options.evaluators ?? PolicyDefaultEvaluators);
2135
- engine.registerEvaluator("permissionBinding", new IdentityPermissionBindingPolicyEvaluator({ getFor: async () => bindings }));
2260
+ engine.registerEvaluator("permissionBinding", new IdentityPermissionBindingPolicyEvaluator());
2136
2261
  const evaluator = new PermissionEvaluator({
2137
2262
  provider: new PermissionMemoryProvider(definitions),
2138
2263
  policyEngine: engine
@@ -2148,7 +2273,8 @@ async function createAuthorizationEvaluator(input, options = {}) {
2148
2273
  type: identity.type,
2149
2274
  realmId: identity.realmId ?? void 0,
2150
2275
  realmName: identity.realmName ?? void 0,
2151
- clientId: identity.clientId ?? null
2276
+ clientId: identity.clientId ?? null,
2277
+ grants: bindings
2152
2278
  });
2153
2279
  return data;
2154
2280
  };
@@ -2267,6 +2393,6 @@ async function createAuthorizationCheckEvaluator(input) {
2267
2393
  };
2268
2394
  }
2269
2395
  //#endregion
2270
- export { AUTHORIZATION_CATALOG_STALE_ERROR_INSTANCE, AUTHORIZATION_POLICY_WITHHELD_TYPE, AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, AuthorizationCatalogStaleError, AuthorizationCheckEvaluatorInputValidator, AuthorizationEvaluatorInputValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, IdentityPermissionBindingPolicyEvaluator, IdentityPolicyEvaluator, IdentityPolicyValidator, PERMISSION_ERROR_INSTANCE, POLICY_ERROR_INSTANCE, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionError, PermissionEvaluator, PermissionMemoryProvider, PolicyData, PolicyDefaultEvaluators, PolicyDefaultValidators, PolicyEngine, PolicyError, PolicyIssueCode, PolicyIssueSeverity, REALM_SCOPE_ORDER, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, RealmScope, SystemPolicyName, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, aggregatePermissionPolicyBindings, authorizationCatalogSchema, authorizationCheckPermissionSchema, authorizationCheckPermissionsSchema, authorizationDefinitionSchema, authorizationGrantSchema, authorizationIdentitySchema, authorizationPolicySchema, buildPermissionKey, compareRealmScope, containsBindingCheck, createAuthorizationCheckEvaluator, createAuthorizationEvaluator, createPolicyTransitionCollector, defineAttributesPolicy, definePolicyData, definePolicyEvaluationContext, definePolicyIssueGroup, definePolicyIssueItem, definePolicyWithType, grantDominates, isAuthorizationCatalogStaleError, isCompositePolicy, isIntervalForDayOfMonth, isIntervalForDayOfWeek, isIntervalForDayOfYear, isPermissionError, isPermissionPolicyBindingEqual, isPolicyError, maxRealmScope, maybeInvertPolicyOutcome, minRealmScope, normalizeRealmScope, parseAuthorizationCheckEvaluatorInput, parseAuthorizationEvaluatorInput, projectAuthorizationPolicy, realmScopeMatches };
2396
+ export { AUTHORIZATION_CATALOG_STALE_ERROR_INSTANCE, AUTHORIZATION_POLICY_WITHHELD_TYPE, AttributeNamesPolicyEvaluator, AttributeNamesPolicyValidator, AttributesPolicyEvaluator, AttributesPolicyValidator, AuthorizationCatalogStaleError, AuthorizationCheckEvaluatorInputValidator, AuthorizationEvaluatorInputValidator, BuiltInPolicyType, CompositePolicyEvaluator, CompositePolicyValidator, DatePolicyEvaluator, DatePolicyValidator, IdentityPermissionBindingPolicyEvaluator, IdentityPolicyEvaluator, IdentityPolicyValidator, PERMISSION_ERROR_INSTANCE, POLICY_ERROR_INSTANCE, PermissionBindingPolicyEvaluator, PermissionBindingPolicyValidator, PermissionError, PermissionEvaluator, PermissionMemoryProvider, PolicyData, PolicyDefaultEvaluators, PolicyDefaultValidators, PolicyEngine, PolicyError, PolicyIssueCode, PolicyIssueSeverity, REALM_SCOPE_ORDER, RealmMatchPolicyEvaluator, RealmMatchPolicyValidator, RealmScope, SystemPolicyName, TimePolicyEvaluator, TimePolicyInterval, TimePolicyValidator, aggregatePermissionPolicyBindings, authorizationCatalogSchema, authorizationCheckPermissionSchema, authorizationCheckPermissionsSchema, authorizationDefinitionSchema, authorizationGrantSchema, authorizationIdentitySchema, authorizationPolicySchema, buildPermissionKey, compareRealmScope, containsBindingCheck, createAuthorizationCheckEvaluator, createAuthorizationEvaluator, createPolicyTransitionCollector, defineAttributesPolicy, definePolicyData, definePolicyEvaluationContext, definePolicyIssueGroup, definePolicyIssueItem, definePolicyWithType, evaluateRecord, evaluateRecordOneOf, evaluateRecordUpdate, grantDominates, hasUnboundedReach, isAuthorizationCatalogStaleError, isCompositePolicy, isIntervalForDayOfMonth, isIntervalForDayOfWeek, isIntervalForDayOfYear, isPermissionError, isPermissionPolicyBindingEqual, isPolicyError, maxRealmScope, maybeInvertPolicyOutcome, minRealmScope, normalizeRealmScope, parseAuthorizationCheckEvaluatorInput, parseAuthorizationEvaluatorInput, projectAuthorizationPolicy, reachesRecord, reachesRecordOneOf, realmScopeMatches };
2271
2397
 
2272
2398
  //# sourceMappingURL=index.mjs.map