@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/README.md +230 -0
- package/dist/index.d.mts +140 -55
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +134 -8
- package/dist/index.mjs.map +1 -1
- package/dist/permission/authorization/module.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/record/index.d.ts +3 -0
- package/dist/permission/record/index.d.ts.map +1 -0
- package/dist/permission/record/module.d.ts +36 -0
- package/dist/permission/record/module.d.ts.map +1 -0
- package/dist/permission/record/types.d.ts +41 -0
- package/dist/permission/record/types.d.ts.map +1 -0
- package/dist/policy/built-in/identity/data/validator.d.ts.map +1 -1
- package/dist/policy/built-in/identity/types.d.ts +9 -0
- package/dist/policy/built-in/identity/types.d.ts.map +1 -1
- package/dist/policy/built-in/permission-binding/identity-evaluator.d.ts +7 -7
- package/dist/policy/built-in/permission-binding/identity-evaluator.d.ts.map +1 -1
- package/package.json +7 -7
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|