@aventara/access 0.0.0-stage → 0.1.0-rc.0
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/CHANGELOG.md +831 -0
- package/LICENSE +91 -0
- package/LICENSE-ADDITIONAL-PERMISSION.md +9 -0
- package/README.md +2439 -2
- package/dist/access/access-options.d.ts +66 -0
- package/dist/access/access-options.js +5 -0
- package/dist/access/access.error.d.ts +35 -0
- package/dist/access/access.error.js +11 -0
- package/dist/access/access.factory.d.ts +273 -0
- package/dist/access/access.factory.js +440 -0
- package/dist/access/better-auth.hardening.d.ts +120 -0
- package/dist/access/better-auth.hardening.js +420 -0
- package/dist/access/better-auth.loader.d.ts +67 -0
- package/dist/access/better-auth.loader.js +65 -0
- package/dist/access/better-auth.range.d.ts +9 -0
- package/dist/access/better-auth.range.js +28 -0
- package/dist/access/better-auth.require.d.ts +90 -0
- package/dist/access/better-auth.require.js +6 -0
- package/dist/access/better-auth.require.node.d.ts +7 -0
- package/dist/access/better-auth.require.node.js +52 -0
- package/dist/access/claims.mapper.d.ts +79 -0
- package/dist/access/claims.mapper.js +152 -0
- package/dist/access/key-set.cache.d.ts +10 -0
- package/dist/access/key-set.cache.js +63 -0
- package/dist/access/policies-block.checker.d.ts +64 -0
- package/dist/access/policies-block.checker.js +138 -0
- package/dist/access/principal.subject.d.ts +12 -0
- package/dist/access/principal.subject.js +60 -0
- package/dist/access/profile.policy.d.ts +74 -0
- package/dist/access/profile.policy.js +110 -0
- package/dist/access/request.state.d.ts +89 -0
- package/dist/access/request.state.js +196 -0
- package/dist/access/session.hooks.d.ts +114 -0
- package/dist/access/session.hooks.js +258 -0
- package/dist/access/token.verifier.d.ts +35 -0
- package/dist/access/token.verifier.js +69 -0
- package/dist/access-schema.d.ts +48 -0
- package/dist/access-schema.js +5 -0
- package/dist/access-warning.emitter.d.ts +2 -0
- package/dist/access-warning.emitter.js +20 -0
- package/dist/access.builder.d.ts +299 -0
- package/dist/access.builder.js +66 -0
- package/dist/access.engine.d.ts +142 -0
- package/dist/access.engine.js +470 -0
- package/dist/audit/audit-event.d.ts +135 -0
- package/dist/audit/audit-event.js +8 -0
- package/dist/audit/audit.log.d.ts +122 -0
- package/dist/audit/audit.log.js +345 -0
- package/dist/audit/audit.schema.d.ts +143 -0
- package/dist/audit/audit.schema.js +50 -0
- package/dist/auth/index.d.ts +12 -0
- package/dist/auth/index.js +2 -0
- package/dist/aventara-where.translator.d.ts +19 -0
- package/dist/aventara-where.translator.js +87 -0
- package/dist/beacon/beacon.check.d.ts +59 -0
- package/dist/beacon/beacon.check.js +128 -0
- package/dist/beacon/beacon.schema.d.ts +41 -0
- package/dist/beacon/beacon.schema.js +48 -0
- package/dist/beacon/beacon.store.d.ts +29 -0
- package/dist/beacon/beacon.store.js +78 -0
- package/dist/better-auth/access.client-plugin.d.ts +75 -0
- package/dist/better-auth/access.client-plugin.js +133 -0
- package/dist/better-auth/access.plugin.d.ts +164 -0
- package/dist/better-auth/access.plugin.js +348 -0
- package/dist/better-auth/access.provider.d.ts +78 -0
- package/dist/better-auth/access.provider.js +100 -0
- package/dist/better-auth/index.d.ts +7 -0
- package/dist/better-auth/index.js +4 -0
- package/dist/better-auth/organization.bridge.d.ts +66 -0
- package/dist/better-auth/organization.bridge.js +144 -0
- package/dist/canonical-json.writer.d.ts +12 -0
- package/dist/canonical-json.writer.js +30 -0
- package/dist/cli/avaccess.bin.d.ts +2 -0
- package/dist/cli/avaccess.bin.js +26 -0
- package/dist/cli/avaccess.program.d.ts +15 -0
- package/dist/cli/avaccess.program.js +166 -0
- package/dist/cli/beacon-pins.file.d.ts +14 -0
- package/dist/cli/beacon-pins.file.js +62 -0
- package/dist/cli/beacon.loader.d.ts +20 -0
- package/dist/cli/beacon.loader.js +71 -0
- package/dist/cli/cli.context.d.ts +49 -0
- package/dist/cli/cli.context.js +66 -0
- package/dist/cli/diff.command.d.ts +2 -0
- package/dist/cli/diff.command.js +66 -0
- package/dist/cli/generate.command.d.ts +2 -0
- package/dist/cli/generate.command.js +82 -0
- package/dist/cli/keys.command.d.ts +2 -0
- package/dist/cli/keys.command.js +96 -0
- package/dist/cli/lock.file.d.ts +31 -0
- package/dist/cli/lock.file.js +181 -0
- package/dist/cli/module.loader.d.ts +13 -0
- package/dist/cli/module.loader.js +63 -0
- package/dist/cli/policies.diff.d.ts +16 -0
- package/dist/cli/policies.diff.js +83 -0
- package/dist/cli/pull.command.d.ts +2 -0
- package/dist/cli/pull.command.js +234 -0
- package/dist/cli/push.command.d.ts +2 -0
- package/dist/cli/push.command.js +59 -0
- package/dist/cli/registry.client.d.ts +63 -0
- package/dist/cli/registry.client.js +287 -0
- package/dist/cli/schema.command.d.ts +2 -0
- package/dist/cli/schema.command.js +60 -0
- package/dist/cli/serve.command.d.ts +6 -0
- package/dist/cli/serve.command.js +200 -0
- package/dist/cli/status.command.d.ts +2 -0
- package/dist/cli/status.command.js +31 -0
- package/dist/cli/translator.flag.d.ts +6 -0
- package/dist/cli/translator.flag.js +24 -0
- package/dist/cli/verify.command.d.ts +2 -0
- package/dist/cli/verify.command.js +40 -0
- package/dist/client/access-client.factory.d.ts +92 -0
- package/dist/client/access-client.factory.js +269 -0
- package/dist/client/access-verbs.schema.d.ts +66 -0
- package/dist/client/access-verbs.schema.js +159 -0
- package/dist/client/verb-rate.limiter.d.ts +116 -0
- package/dist/client/verb-rate.limiter.js +358 -0
- package/dist/foreign/foreign-facts.d.ts +30 -0
- package/dist/foreign/foreign-facts.js +75 -0
- package/dist/foreign/foreign.merger.d.ts +10 -0
- package/dist/foreign/foreign.merger.js +52 -0
- package/dist/foreign-references.check.d.ts +12 -0
- package/dist/foreign-references.check.js +134 -0
- package/dist/generator/client.emitter.d.ts +28 -0
- package/dist/generator/client.emitter.js +411 -0
- package/dist/generator/copied-modules.reader.d.ts +14 -0
- package/dist/generator/copied-modules.reader.js +80 -0
- package/dist/generator/namespace.facts.d.ts +28 -0
- package/dist/generator/namespace.facts.js +32 -0
- package/dist/generator/orm.translator.d.ts +48 -0
- package/dist/generator/orm.translator.js +29 -0
- package/dist/generator/prisma7/prisma-schema.reader.d.ts +9 -0
- package/dist/generator/prisma7/prisma-schema.reader.js +394 -0
- package/dist/generator/prisma7/prisma-where.translator.d.ts +72 -0
- package/dist/generator/prisma7/prisma-where.translator.js +136 -0
- package/dist/generator/prisma7/prisma7.translator.d.ts +2 -0
- package/dist/generator/prisma7/prisma7.translator.js +87 -0
- package/dist/generator/schema.emitter.d.ts +14 -0
- package/dist/generator/schema.emitter.js +23 -0
- package/dist/generator/tsconfig.reader.d.ts +44 -0
- package/dist/generator/tsconfig.reader.js +110 -0
- package/dist/generator/typescript-literal.writer.d.ts +5 -0
- package/dist/generator/typescript-literal.writer.js +42 -0
- package/dist/guardrails/guardrails-contract.d.ts +45 -0
- package/dist/guardrails/guardrails-contract.js +12 -0
- package/dist/guardrails/guardrails.builder.d.ts +48 -0
- package/dist/guardrails/guardrails.builder.js +32 -0
- package/dist/guardrails/guardrails.compiler.d.ts +10 -0
- package/dist/guardrails/guardrails.compiler.js +254 -0
- package/dist/guardrails/guardrails.composer.d.ts +7 -0
- package/dist/guardrails/guardrails.composer.js +95 -0
- package/dist/guardrails/guardrails.hash.d.ts +10 -0
- package/dist/guardrails/guardrails.hash.js +38 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +6 -0
- package/dist/kiosk/kiosk-verbs.schema.d.ts +19 -0
- package/dist/kiosk/kiosk-verbs.schema.js +61 -0
- package/dist/kiosk/kiosk.plugin.d.ts +217 -0
- package/dist/kiosk/kiosk.plugin.js +877 -0
- package/dist/kiosk/kiosk.schema.d.ts +176 -0
- package/dist/kiosk/kiosk.schema.js +101 -0
- package/dist/kiosk/kiosk.store.d.ts +171 -0
- package/dist/kiosk/kiosk.store.js +478 -0
- package/dist/kiosk/lan-range.policy.d.ts +31 -0
- package/dist/kiosk/lan-range.policy.js +113 -0
- package/dist/kiosk/pin.policy.d.ts +9 -0
- package/dist/kiosk/pin.policy.js +143 -0
- package/dist/kiosk/session-use.marker.d.ts +4 -0
- package/dist/kiosk/session-use.marker.js +16 -0
- package/dist/lock/lock-contract.d.ts +38 -0
- package/dist/lock/lock-contract.js +67 -0
- package/dist/lock/lock.verifier.d.ts +27 -0
- package/dist/lock/lock.verifier.js +62 -0
- package/dist/mcp/agent-scope.gate.d.ts +11 -0
- package/dist/mcp/agent-scope.gate.js +75 -0
- package/dist/mcp/agent.grant.d.ts +36 -0
- package/dist/mcp/agent.grant.js +92 -0
- package/dist/mcp/mcp-options.d.ts +60 -0
- package/dist/mcp/mcp-options.js +72 -0
- package/dist/oauth/access-token.verifier.d.ts +66 -0
- package/dist/oauth/access-token.verifier.js +201 -0
- package/dist/oauth/oauth-provider.hardening.d.ts +62 -0
- package/dist/oauth/oauth-provider.hardening.js +261 -0
- package/dist/passwords/argon2id.hasher.d.ts +97 -0
- package/dist/passwords/argon2id.hasher.js +256 -0
- package/dist/passwords/node-rs-argon2.adapter.d.ts +25 -0
- package/dist/passwords/node-rs-argon2.adapter.js +42 -0
- package/dist/passwords/passwords.options.d.ts +79 -0
- package/dist/passwords/passwords.options.js +212 -0
- package/dist/policies-contract.d.ts +192 -0
- package/dist/policies-contract.js +8 -0
- package/dist/policies.compiler.d.ts +36 -0
- package/dist/policies.compiler.js +1358 -0
- package/dist/policies.decider.d.ts +103 -0
- package/dist/policies.decider.js +382 -0
- package/dist/policies.hash.d.ts +10 -0
- package/dist/policies.hash.js +53 -0
- package/dist/predicate.evaluator.d.ts +62 -0
- package/dist/predicate.evaluator.js +378 -0
- package/dist/random-secret.generator.d.ts +2 -0
- package/dist/random-secret.generator.js +15 -0
- package/dist/reference-reads.check.d.ts +12 -0
- package/dist/reference-reads.check.js +148 -0
- package/dist/registry/access.registry.d.ts +173 -0
- package/dist/registry/access.registry.js +865 -0
- package/dist/registry/beacon.ledger.d.ts +30 -0
- package/dist/registry/beacon.ledger.js +93 -0
- package/dist/registry/ownership.check.d.ts +54 -0
- package/dist/registry/ownership.check.js +215 -0
- package/dist/registry/registry.endpoints.d.ts +50 -0
- package/dist/registry/registry.endpoints.js +193 -0
- package/dist/registry/registry.key.d.ts +7 -0
- package/dist/registry/registry.key.js +13 -0
- package/dist/registry/registry.options.d.ts +13 -0
- package/dist/registry/registry.options.js +28 -0
- package/dist/registry/registry.refusal.d.ts +6 -0
- package/dist/registry/registry.refusal.js +11 -0
- package/dist/registry/registry.schema.d.ts +238 -0
- package/dist/registry/registry.schema.js +127 -0
- package/dist/resources/auth-tables.catalog.d.ts +91 -0
- package/dist/resources/auth-tables.catalog.js +102 -0
- package/dist/resources/auth-tables.guard.d.ts +95 -0
- package/dist/resources/auth-tables.guard.js +405 -0
- package/dist/scope.resolver.d.ts +138 -0
- package/dist/scope.resolver.js +512 -0
- package/dist/sha256.digest.d.ts +2 -0
- package/dist/sha256.digest.js +81 -0
- package/dist/standalone/access.server.d.ts +41 -0
- package/dist/standalone/access.server.js +301 -0
- package/dist/standalone/index.d.ts +1 -0
- package/dist/standalone/index.js +1 -0
- package/package.json +101 -3
- package/src/aventara-where.translator.ts +116 -0
- package/src/beacon/beacon.check.ts +204 -0
- package/src/canonical-json.writer.ts +51 -0
- package/src/generator/prisma7/prisma-where.translator.ts +241 -0
- package/src/policies-contract.ts +184 -0
- package/src/policies.decider.ts +650 -0
- package/src/predicate.evaluator.ts +574 -0
- package/src/scope.resolver.ts +791 -0
- package/src/sha256.digest.ts +85 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
// The principal fold: substitute the principal into every rule, fold what
|
|
2
|
+
// the principal alone decides (claims, origin, `signedIn`), expand roles, and keep only
|
|
3
|
+
// row conditions. Grants combine with OR, denies with AND NOT. What leaves this module
|
|
4
|
+
// holds literals only.
|
|
5
|
+
export const TRUE = { k: "true" };
|
|
6
|
+
export const FALSE = { k: "false" };
|
|
7
|
+
const junction = (k, args) => {
|
|
8
|
+
const kept = [];
|
|
9
|
+
for (const arg of args) {
|
|
10
|
+
if (arg.k === "false") {
|
|
11
|
+
return FALSE;
|
|
12
|
+
}
|
|
13
|
+
if (arg.k !== "true") {
|
|
14
|
+
kept.push(arg);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (kept.length === 0) {
|
|
18
|
+
return TRUE;
|
|
19
|
+
}
|
|
20
|
+
return kept.length === 1 ? kept[0] : { k, args: kept };
|
|
21
|
+
};
|
|
22
|
+
/** A conjunction the author wrote as one object. */
|
|
23
|
+
export const andOf = (args) => junction("and", args);
|
|
24
|
+
/** A conjunction the engine builds. */
|
|
25
|
+
export const allOf = (args) => junction("all", args);
|
|
26
|
+
export const orOf = (args) => {
|
|
27
|
+
const kept = [];
|
|
28
|
+
for (const arg of args) {
|
|
29
|
+
if (arg.k === "true") {
|
|
30
|
+
return TRUE;
|
|
31
|
+
}
|
|
32
|
+
if (arg.k !== "false") {
|
|
33
|
+
kept.push(arg);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (kept.length === 0) {
|
|
37
|
+
return FALSE;
|
|
38
|
+
}
|
|
39
|
+
return kept.length === 1 ? kept[0] : { k: "or", args: kept };
|
|
40
|
+
};
|
|
41
|
+
export const notOf = (arg) => arg.k === "true" ? FALSE : arg.k === "false" ? TRUE : { k: "not", arg };
|
|
42
|
+
export function ownOf(record, key) {
|
|
43
|
+
return record !== undefined && Object.hasOwn(record, key)
|
|
44
|
+
? record[key]
|
|
45
|
+
: undefined;
|
|
46
|
+
}
|
|
47
|
+
/** A plain object: made by `{}` or JSON, not an array, a `Date`, a `Map` or a class instance. */
|
|
48
|
+
export const isPlainObject = (value) => {
|
|
49
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const prototype = Object.getPrototypeOf(value);
|
|
53
|
+
return prototype === Object.prototype || prototype === null;
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Reads the principal off a question: any object, an instance of a class included
|
|
57
|
+
* (an entity), is a principal; an array, a function or a primitive is anonymous.
|
|
58
|
+
*/
|
|
59
|
+
export const principalFactsOf = (principal, origin) => ({
|
|
60
|
+
principal: typeof principal === "object" &&
|
|
61
|
+
principal !== null &&
|
|
62
|
+
!Array.isArray(principal)
|
|
63
|
+
? principal
|
|
64
|
+
: null,
|
|
65
|
+
origin,
|
|
66
|
+
});
|
|
67
|
+
/** The value at `path`, through own members only; `undefined` where one is missing. */
|
|
68
|
+
const readPath = (from, path) => {
|
|
69
|
+
let value = from;
|
|
70
|
+
for (const key of path) {
|
|
71
|
+
if (typeof value !== "object" ||
|
|
72
|
+
value === null ||
|
|
73
|
+
!Object.hasOwn(value, key)) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
value = value[key];
|
|
77
|
+
}
|
|
78
|
+
return value;
|
|
79
|
+
};
|
|
80
|
+
/**
|
|
81
|
+
* A `me` value the principal does not have: anonymous, a missing or null property,
|
|
82
|
+
* an empty string (no identity is `""`), a number JSON cannot carry, or anything but
|
|
83
|
+
* a string, number or boolean.
|
|
84
|
+
*/
|
|
85
|
+
class MissingPrincipalValue extends Error {
|
|
86
|
+
}
|
|
87
|
+
const literalOf = (operand, facts) => {
|
|
88
|
+
if (operand === null || typeof operand !== "object") {
|
|
89
|
+
return operand;
|
|
90
|
+
}
|
|
91
|
+
const value = facts.principal === null
|
|
92
|
+
? undefined
|
|
93
|
+
: readPath(facts.principal, operand.principal);
|
|
94
|
+
if (value === "" ||
|
|
95
|
+
(typeof value === "number" && !Number.isFinite(value)) ||
|
|
96
|
+
(typeof value !== "string" &&
|
|
97
|
+
typeof value !== "number" &&
|
|
98
|
+
typeof value !== "boolean")) {
|
|
99
|
+
throw new MissingPrincipalValue();
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
};
|
|
103
|
+
/** A condition on the principal, three-valued: a claim it lacks is `"unknown"`. */
|
|
104
|
+
const principalTruth = (condition, facts) => {
|
|
105
|
+
switch (condition.op) {
|
|
106
|
+
case "and": {
|
|
107
|
+
let unknown = false;
|
|
108
|
+
for (const arg of condition.args) {
|
|
109
|
+
const truth = principalTruth(arg, facts);
|
|
110
|
+
if (truth === false) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
unknown ||= truth === "unknown";
|
|
114
|
+
}
|
|
115
|
+
return unknown ? "unknown" : true;
|
|
116
|
+
}
|
|
117
|
+
case "origin":
|
|
118
|
+
return facts.origin === condition.value;
|
|
119
|
+
case "cmp": {
|
|
120
|
+
if (facts.principal === null) {
|
|
121
|
+
// The anonymous caller has no token, so none can be stale: no flag is set on
|
|
122
|
+
// it (a ban does not close an `anyone` grant), and any other claim is unknown.
|
|
123
|
+
return condition.value === true ? false : "unknown";
|
|
124
|
+
}
|
|
125
|
+
const value = readPath(facts.principal, ["claims", ...condition.claim]);
|
|
126
|
+
if (value === undefined || value === null) {
|
|
127
|
+
return "unknown";
|
|
128
|
+
}
|
|
129
|
+
return typeof value === typeof condition.value
|
|
130
|
+
? value === condition.value
|
|
131
|
+
: "unknown";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Folds a condition on the principal alone (claims, origin) to a boolean. A claim
|
|
137
|
+
* the principal lacks, or holds as another type, fails closed: a grant's global role
|
|
138
|
+
* is not held, and a deny's `if` holds, whatever value it compares with. Access mints
|
|
139
|
+
* every declared boolean claim, `false` when unset, so a token it issued lacks none;
|
|
140
|
+
* a foreign or stale token without `banned` is denied by `banned: true`. The
|
|
141
|
+
* anonymous caller is the one exception: a comparison with `true` never holds for it.
|
|
142
|
+
*/
|
|
143
|
+
export const foldPrincipalCondition = (condition, facts, rule) => {
|
|
144
|
+
const truth = principalTruth(condition, facts);
|
|
145
|
+
return truth === "unknown" ? rule === "deny" : truth;
|
|
146
|
+
};
|
|
147
|
+
const resolveIn = (policies, predicate, facts, mode) => {
|
|
148
|
+
switch (predicate.op) {
|
|
149
|
+
case "true":
|
|
150
|
+
return TRUE;
|
|
151
|
+
case "false":
|
|
152
|
+
return FALSE;
|
|
153
|
+
case "and":
|
|
154
|
+
return andOf(predicate.args.map((arg) => resolveIn(policies, arg, facts, mode)));
|
|
155
|
+
case "or":
|
|
156
|
+
return orOf(predicate.args.map((arg) => resolveIn(policies, arg, facts, mode)));
|
|
157
|
+
case "not":
|
|
158
|
+
return notOf(resolveIn(policies, predicate.arg, facts, mode));
|
|
159
|
+
case "cmp": {
|
|
160
|
+
const value = Array.isArray(predicate.value)
|
|
161
|
+
? predicate.value.map((item) => literalOf(item, facts))
|
|
162
|
+
: literalOf(predicate.value, facts);
|
|
163
|
+
return { k: "cmp", field: predicate.field, cmp: predicate.cmp, value };
|
|
164
|
+
}
|
|
165
|
+
case "rel": {
|
|
166
|
+
const where = resolveIn(policies, predicate.where, facts, mode);
|
|
167
|
+
return where.k === "false"
|
|
168
|
+
? FALSE
|
|
169
|
+
: {
|
|
170
|
+
k: "rel",
|
|
171
|
+
relation: predicate.relation,
|
|
172
|
+
target: predicate.target,
|
|
173
|
+
where,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
case "some":
|
|
177
|
+
case "every":
|
|
178
|
+
case "none": {
|
|
179
|
+
const where = resolveIn(policies, predicate.where, facts, mode);
|
|
180
|
+
if (predicate.op === "some" && where.k === "false") {
|
|
181
|
+
return FALSE;
|
|
182
|
+
}
|
|
183
|
+
if (predicate.op === "none" && where.k === "false") {
|
|
184
|
+
return TRUE;
|
|
185
|
+
}
|
|
186
|
+
if (predicate.op === "every" && where.k === "true") {
|
|
187
|
+
return TRUE;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
k: predicate.op,
|
|
191
|
+
relation: predicate.relation,
|
|
192
|
+
target: predicate.target,
|
|
193
|
+
where,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
case "role": {
|
|
197
|
+
const role = ownOf(policies.roles, predicate.role);
|
|
198
|
+
if (role === undefined || !("on" in role)) {
|
|
199
|
+
return FALSE;
|
|
200
|
+
}
|
|
201
|
+
return resolveIn(policies, role.where, facts, mode);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Substitutes the principal into a row condition. A `me` value the principal lacks
|
|
207
|
+
* makes the whole rule `false` for a grant (`drop-rule`) and `true` for a deny
|
|
208
|
+
* (`match-rule`).
|
|
209
|
+
*/
|
|
210
|
+
export const resolvePredicate = (policies, predicate, facts, mode) => {
|
|
211
|
+
try {
|
|
212
|
+
return resolveIn(policies, predicate, facts, mode);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
if (error instanceof MissingPrincipalValue) {
|
|
216
|
+
return mode === "drop-rule" ? FALSE : TRUE;
|
|
217
|
+
}
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
/** Whether a rule names this action on this Resource. */
|
|
222
|
+
const covers = (resource, action, rule) => (rule.resource === resource || rule.resource === "*") &&
|
|
223
|
+
(rule.actions === "*" || rule.actions.includes(action));
|
|
224
|
+
/**
|
|
225
|
+
* The grants and denies a question about `action` on `resource` reads. A grant on
|
|
226
|
+
* `"*"` reaches the file's own Resources only: never a foreign one, and never one
|
|
227
|
+
* its schema does not list. A deny on `"*"` reaches every Resource.
|
|
228
|
+
*/
|
|
229
|
+
export const rulesFor = (policies, resource, action) => {
|
|
230
|
+
const facts = ownOf(policies.schema, resource);
|
|
231
|
+
const own = facts !== undefined && facts.foreign === undefined;
|
|
232
|
+
return {
|
|
233
|
+
grants: policies.grants.filter((grant) => (own || grant.resource !== "*") && covers(resource, action, grant)),
|
|
234
|
+
denies: policies.denies.filter((deny) => covers(resource, action, deny)),
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
/** What a subject contributes to a grant, folded: `true`, `false` or a row condition. */
|
|
238
|
+
const subjectCondition = (policies, grant, subject, facts) => {
|
|
239
|
+
if (subject === "anyone") {
|
|
240
|
+
return TRUE;
|
|
241
|
+
}
|
|
242
|
+
if (subject === "signedIn") {
|
|
243
|
+
return facts.principal === null ? FALSE : TRUE;
|
|
244
|
+
}
|
|
245
|
+
const role = ownOf(policies.roles, subject);
|
|
246
|
+
if (role === undefined) {
|
|
247
|
+
return FALSE;
|
|
248
|
+
}
|
|
249
|
+
if ("global" in role) {
|
|
250
|
+
return foldPrincipalCondition(role.global, facts, "grant") ? TRUE : FALSE;
|
|
251
|
+
}
|
|
252
|
+
const held = { op: "role", role: subject };
|
|
253
|
+
const predicate = grant.in === undefined
|
|
254
|
+
? held
|
|
255
|
+
: { op: "rel", relation: grant.in, target: role.on, where: held };
|
|
256
|
+
return resolvePredicate(policies, predicate, facts, "drop-rule");
|
|
257
|
+
};
|
|
258
|
+
/**
|
|
259
|
+
* Each grant's condition per subject: its `where`, then the subject's (its row
|
|
260
|
+
* role, through `in`), then, with `after`, the `after` condition. A grant that reads
|
|
261
|
+
* a `me` value the principal lacks drops.
|
|
262
|
+
*/
|
|
263
|
+
export const foldGrants = (policies, grants, facts, withAfter = false) => grants.flatMap((grant) => grant.subjects.map((subject) => {
|
|
264
|
+
const parts = [];
|
|
265
|
+
if (grant.where !== undefined) {
|
|
266
|
+
parts.push(resolvePredicate(policies, grant.where, facts, "drop-rule"));
|
|
267
|
+
}
|
|
268
|
+
parts.push(subjectCondition(policies, grant, subject, facts));
|
|
269
|
+
if (withAfter && grant.after !== undefined) {
|
|
270
|
+
parts.push(resolvePredicate(policies, grant.after, facts, "drop-rule"));
|
|
271
|
+
}
|
|
272
|
+
return { grant, subject, condition: allOf(parts) };
|
|
273
|
+
}));
|
|
274
|
+
/**
|
|
275
|
+
* Each deny's row condition: `false` when its `if` does not hold for this principal,
|
|
276
|
+
* `true` when its `where` reads a `me` value the principal lacks.
|
|
277
|
+
*/
|
|
278
|
+
export const foldDenies = (policies, denies, facts) => denies.map((deny) => {
|
|
279
|
+
if (deny.if !== undefined &&
|
|
280
|
+
!foldPrincipalCondition(deny.if, facts, "deny")) {
|
|
281
|
+
return { deny, condition: FALSE };
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
deny,
|
|
285
|
+
condition: deny.where === undefined
|
|
286
|
+
? TRUE
|
|
287
|
+
: resolvePredicate(policies, deny.where, facts, "match-rule"),
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
/** The rows a principal may `action`: OR of the grants, AND NOT each deny. */
|
|
291
|
+
export const scopeCondition = (grants, denies) => {
|
|
292
|
+
const allowed = orOf(grants.map((grant) => grant.condition));
|
|
293
|
+
if (allowed.k === "false") {
|
|
294
|
+
return FALSE;
|
|
295
|
+
}
|
|
296
|
+
return allOf([allowed, ...denies.map((deny) => notOf(deny.condition))]);
|
|
297
|
+
};
|
|
298
|
+
/** Whether the schema entry lists `field` as nullable. A file without `nullable` has none. */
|
|
299
|
+
const nullableIn = (policies, resource, field) => ownOf(policies.schema, resource)?.nullable?.includes(field) === true;
|
|
300
|
+
/**
|
|
301
|
+
* Whether a to-one step can find no row: `"nullable"` when one of its reference
|
|
302
|
+
* fields is nullable, `"required"` when it holds references and none is, and
|
|
303
|
+
* `"unstated"` for the end that holds no references (the other end's step).
|
|
304
|
+
*/
|
|
305
|
+
const stepKind = (policies, resource, node) => {
|
|
306
|
+
const fields = ownOf(ownOf(policies.schema, resource)?.relations, node.relation)?.referenceFields;
|
|
307
|
+
if (fields === undefined) {
|
|
308
|
+
return "unstated";
|
|
309
|
+
}
|
|
310
|
+
return fields.some((field) => nullableIn(policies, resource, field))
|
|
311
|
+
? "nullable"
|
|
312
|
+
: "required";
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Whether SQL can read a comparison as unknown where `can` reads it as false: a
|
|
316
|
+
* nullable field compared with a value (SQL's `NULL = v` is unknown; `f = NULL` is
|
|
317
|
+
* written `IS NULL` and never is), or an `in` list that holds `null`.
|
|
318
|
+
*/
|
|
319
|
+
const unknownIn = (policies, resource, node) => Array.isArray(node.value)
|
|
320
|
+
? node.value.includes(null) ||
|
|
321
|
+
nullableIn(policies, resource, node.field)
|
|
322
|
+
: node.value !== null && nullableIn(policies, resource, node.field);
|
|
323
|
+
/** Whether SQL reads `node` exactly as `can` does: no unknown comparison, no step that can find no row. */
|
|
324
|
+
const known = (policies, resource, node) => {
|
|
325
|
+
switch (node.k) {
|
|
326
|
+
case "true":
|
|
327
|
+
case "false":
|
|
328
|
+
return true;
|
|
329
|
+
case "cmp":
|
|
330
|
+
return !unknownIn(policies, resource, node);
|
|
331
|
+
case "and":
|
|
332
|
+
case "all":
|
|
333
|
+
case "or":
|
|
334
|
+
return node.args.every((arg) => known(policies, resource, arg));
|
|
335
|
+
case "not":
|
|
336
|
+
return known(policies, resource, node.arg);
|
|
337
|
+
case "rel":
|
|
338
|
+
return (stepKind(policies, resource, node) !== "nullable" &&
|
|
339
|
+
known(policies, node.target, node.where));
|
|
340
|
+
case "some":
|
|
341
|
+
case "every":
|
|
342
|
+
case "none":
|
|
343
|
+
return known(policies, node.target, node.where);
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
/**
|
|
347
|
+
* A comparison with the `null`s taken out of its `in` list: `can` never matches a
|
|
348
|
+
* NULL field through a list, and SQL's `IN (NULL, …)` is unknown for every row. A
|
|
349
|
+
* list left empty: `in` matches no row; `notIn` every row whose field is not NULL,
|
|
350
|
+
* which on a required field is every row.
|
|
351
|
+
*/
|
|
352
|
+
const listWithoutNull = (policies, resource, node) => {
|
|
353
|
+
if (!Array.isArray(node.value)) {
|
|
354
|
+
return node;
|
|
355
|
+
}
|
|
356
|
+
const value = [];
|
|
357
|
+
for (const item of node.value) {
|
|
358
|
+
if (item !== null) {
|
|
359
|
+
value.push(item);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
if (value.length > 0) {
|
|
363
|
+
return { k: "cmp", field: node.field, cmp: node.cmp, value };
|
|
364
|
+
}
|
|
365
|
+
if (node.cmp === "in") {
|
|
366
|
+
return FALSE;
|
|
367
|
+
}
|
|
368
|
+
return nullableIn(policies, resource, node.field)
|
|
369
|
+
? { k: "cmp", field: node.field, cmp: "ne", value: null }
|
|
370
|
+
: TRUE;
|
|
371
|
+
};
|
|
372
|
+
const isNull = (field) => ({
|
|
373
|
+
k: "cmp",
|
|
374
|
+
field,
|
|
375
|
+
cmp: "eq",
|
|
376
|
+
value: null,
|
|
377
|
+
});
|
|
378
|
+
/** The same to-one step, around another condition. */
|
|
379
|
+
const step = (node, where) => ({
|
|
380
|
+
k: "rel",
|
|
381
|
+
relation: node.relation,
|
|
382
|
+
target: node.target,
|
|
383
|
+
where,
|
|
384
|
+
});
|
|
385
|
+
/** A quantifier over the same to-many relation. */
|
|
386
|
+
const quantified = (k, node, where) => ({ k, relation: node.relation, target: node.target, where });
|
|
387
|
+
/** One rewrite applied to each condition of a list. */
|
|
388
|
+
const each = (rewrite, policies, resource, nodes) => {
|
|
389
|
+
const out = [];
|
|
390
|
+
for (const node of nodes) {
|
|
391
|
+
out.push(rewrite(policies, resource, node));
|
|
392
|
+
}
|
|
393
|
+
return out;
|
|
394
|
+
};
|
|
395
|
+
/** The condition, true on exactly the rows where `can` reads it as true. */
|
|
396
|
+
const holds = (policies, resource, node) => {
|
|
397
|
+
if (known(policies, resource, node)) {
|
|
398
|
+
return node;
|
|
399
|
+
}
|
|
400
|
+
switch (node.k) {
|
|
401
|
+
case "true":
|
|
402
|
+
case "false":
|
|
403
|
+
return node;
|
|
404
|
+
case "cmp":
|
|
405
|
+
return listWithoutNull(policies, resource, node);
|
|
406
|
+
case "and":
|
|
407
|
+
return andOf(each(holds, policies, resource, node.args));
|
|
408
|
+
case "all":
|
|
409
|
+
return allOf(each(holds, policies, resource, node.args));
|
|
410
|
+
case "or":
|
|
411
|
+
return orOf(each(holds, policies, resource, node.args));
|
|
412
|
+
case "not":
|
|
413
|
+
return fails(policies, resource, node.arg);
|
|
414
|
+
case "rel":
|
|
415
|
+
// A step that finds no row is false, in SQL as in `can`.
|
|
416
|
+
return step(node, holds(policies, node.target, node.where));
|
|
417
|
+
case "some":
|
|
418
|
+
case "none":
|
|
419
|
+
// `EXISTS` and `NOT EXISTS`: true or false around the rows they count.
|
|
420
|
+
return quantified(node.k, node, holds(policies, node.target, node.where));
|
|
421
|
+
case "every":
|
|
422
|
+
// An ORM may write `every` as `NOT EXISTS (… WHERE NOT condition)`, where an
|
|
423
|
+
// unknown condition passes: ask that no related row fails it instead.
|
|
424
|
+
return quantified("none", node, fails(policies, node.target, node.where));
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
/** The condition's negation, true on exactly the rows where `can` reads it as false. */
|
|
428
|
+
const fails = (policies, resource, node) => {
|
|
429
|
+
if (known(policies, resource, node)) {
|
|
430
|
+
return notOf(node);
|
|
431
|
+
}
|
|
432
|
+
switch (node.k) {
|
|
433
|
+
case "true":
|
|
434
|
+
case "false":
|
|
435
|
+
return notOf(node);
|
|
436
|
+
case "cmp": {
|
|
437
|
+
const negated = notOf(listWithoutNull(policies, resource, node));
|
|
438
|
+
return nullableIn(policies, resource, node.field)
|
|
439
|
+
? orOf([negated, isNull(node.field)])
|
|
440
|
+
: negated;
|
|
441
|
+
}
|
|
442
|
+
case "and":
|
|
443
|
+
case "all": {
|
|
444
|
+
// Comparisons only: `NOT (a AND b) OR a IS NULL OR b IS NULL`.
|
|
445
|
+
if (node.args.every((arg) => arg.k === "cmp")) {
|
|
446
|
+
const comparisons = node.args;
|
|
447
|
+
const guards = [];
|
|
448
|
+
const guarded = [];
|
|
449
|
+
for (const arg of comparisons) {
|
|
450
|
+
if (arg.value !== null &&
|
|
451
|
+
nullableIn(policies, resource, arg.field) &&
|
|
452
|
+
!guarded.includes(arg.field)) {
|
|
453
|
+
guarded.push(arg.field);
|
|
454
|
+
guards.push(isNull(arg.field));
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
const lists = [];
|
|
458
|
+
for (const arg of comparisons) {
|
|
459
|
+
lists.push(listWithoutNull(policies, resource, arg));
|
|
460
|
+
}
|
|
461
|
+
const strict = (node.k === "and" ? andOf : allOf)(lists);
|
|
462
|
+
return orOf([notOf(strict), ...guards]);
|
|
463
|
+
}
|
|
464
|
+
return orOf(each(fails, policies, resource, node.args));
|
|
465
|
+
}
|
|
466
|
+
case "or":
|
|
467
|
+
return allOf(each(fails, policies, resource, node.args));
|
|
468
|
+
case "not":
|
|
469
|
+
return holds(policies, resource, node.arg);
|
|
470
|
+
case "rel": {
|
|
471
|
+
const failing = step(node, fails(policies, node.target, node.where));
|
|
472
|
+
switch (stepKind(policies, resource, node)) {
|
|
473
|
+
case "required":
|
|
474
|
+
// The row is always there: the step fails where its condition does.
|
|
475
|
+
return failing;
|
|
476
|
+
case "nullable": {
|
|
477
|
+
// No row (a NULL reference) fails it too, said on this row's own fields.
|
|
478
|
+
const references = ownOf(ownOf(policies.schema, resource)?.relations, node.relation)
|
|
479
|
+
?.referenceFields ?? [];
|
|
480
|
+
const guards = [];
|
|
481
|
+
for (const field of references) {
|
|
482
|
+
guards.push(isNull(field));
|
|
483
|
+
}
|
|
484
|
+
guards.push(failing);
|
|
485
|
+
return orOf(guards);
|
|
486
|
+
}
|
|
487
|
+
case "unstated":
|
|
488
|
+
// No reference fields here to say "no row" with: the `NOT` of the step
|
|
489
|
+
// covers the missing row, the failing condition the row that is there.
|
|
490
|
+
return orOf([
|
|
491
|
+
notOf(step(node, holds(policies, node.target, node.where))),
|
|
492
|
+
failing,
|
|
493
|
+
]);
|
|
494
|
+
}
|
|
495
|
+
return failing;
|
|
496
|
+
}
|
|
497
|
+
case "some":
|
|
498
|
+
case "none":
|
|
499
|
+
return notOf(quantified(node.k, node, holds(policies, node.target, node.where)));
|
|
500
|
+
case "every":
|
|
501
|
+
return quantified("some", node, fails(policies, node.target, node.where));
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
/**
|
|
505
|
+
* A folded condition on `resource` in the form a database reads as `can` does.
|
|
506
|
+
* `can` reads a comparison on a NULL field as false, so its `NOT` keeps the row; SQL
|
|
507
|
+
* reads it as unknown, and its `NOT` drops the row. Each negated comparison on a
|
|
508
|
+
* nullable field (the schema entry's `nullable`) gets its own guard, `NOT (f = v) OR
|
|
509
|
+
* f IS NULL`, and a negated to-one step whose reference is nullable gets `OR fk IS
|
|
510
|
+
* NULL`. A condition with neither is returned as it is.
|
|
511
|
+
*/
|
|
512
|
+
export const nullSafe = (policies, resource, node) => holds(policies, resource, node);
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// SHA-256 (FIPS 180-4) over UTF-8 text, synchronous and dependency-free, so the engine
|
|
2
|
+
// entry hashes a policies file the same way in Node, a browser, Electron or a build
|
|
3
|
+
// step, without `node:crypto` and without WebCrypto's async API. It hashes a few
|
|
4
|
+
// kilobytes at build and startup time; it is not on any request path.
|
|
5
|
+
const K = new Uint32Array([
|
|
6
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
|
|
7
|
+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
|
|
8
|
+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
9
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
10
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
|
|
11
|
+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
12
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
|
|
13
|
+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
14
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
15
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
|
|
16
|
+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
|
|
17
|
+
]);
|
|
18
|
+
const rotr = (x, n) => (x >>> n) | (x << (32 - n));
|
|
19
|
+
/** The SHA-256 digest of `text`'s UTF-8 bytes, as 64 lowercase hex digits. */
|
|
20
|
+
export const sha256Hex = (text) => {
|
|
21
|
+
const bytes = new TextEncoder().encode(text);
|
|
22
|
+
const bitLength = bytes.length * 8;
|
|
23
|
+
// Message, a 0x80 byte, zero padding, and the 64-bit big-endian bit length.
|
|
24
|
+
const blocks = Math.ceil((bytes.length + 9) / 64);
|
|
25
|
+
const padded = new Uint8Array(blocks * 64);
|
|
26
|
+
padded.set(bytes);
|
|
27
|
+
padded[bytes.length] = 0x80;
|
|
28
|
+
const view = new DataView(padded.buffer);
|
|
29
|
+
view.setUint32(padded.length - 8, Math.floor(bitLength / 0x1_0000_0000));
|
|
30
|
+
view.setUint32(padded.length - 4, bitLength >>> 0);
|
|
31
|
+
const h = new Uint32Array([
|
|
32
|
+
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c,
|
|
33
|
+
0x1f83d9ab, 0x5be0cd19,
|
|
34
|
+
]);
|
|
35
|
+
const w = new Uint32Array(64);
|
|
36
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
37
|
+
for (let t = 0; t < 16; t++) {
|
|
38
|
+
w[t] = view.getUint32(offset + t * 4);
|
|
39
|
+
}
|
|
40
|
+
for (let t = 16; t < 64; t++) {
|
|
41
|
+
const w15 = w[t - 15];
|
|
42
|
+
const w2 = w[t - 2];
|
|
43
|
+
const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ (w15 >>> 3);
|
|
44
|
+
const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ (w2 >>> 10);
|
|
45
|
+
w[t] = (w[t - 16] + s0 + w[t - 7] + s1) >>> 0;
|
|
46
|
+
}
|
|
47
|
+
let a = h[0];
|
|
48
|
+
let b = h[1];
|
|
49
|
+
let c = h[2];
|
|
50
|
+
let d = h[3];
|
|
51
|
+
let e = h[4];
|
|
52
|
+
let f = h[5];
|
|
53
|
+
let g = h[6];
|
|
54
|
+
let hh = h[7];
|
|
55
|
+
for (let t = 0; t < 64; t++) {
|
|
56
|
+
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
57
|
+
const choose = (e & f) ^ (~e & g);
|
|
58
|
+
const t1 = (hh + s1 + choose + K[t] + w[t]) >>> 0;
|
|
59
|
+
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
60
|
+
const majority = (a & b) ^ (a & c) ^ (b & c);
|
|
61
|
+
const t2 = (s0 + majority) >>> 0;
|
|
62
|
+
hh = g;
|
|
63
|
+
g = f;
|
|
64
|
+
f = e;
|
|
65
|
+
e = (d + t1) >>> 0;
|
|
66
|
+
d = c;
|
|
67
|
+
c = b;
|
|
68
|
+
b = a;
|
|
69
|
+
a = (t1 + t2) >>> 0;
|
|
70
|
+
}
|
|
71
|
+
h[0] = (h[0] + a) >>> 0;
|
|
72
|
+
h[1] = (h[1] + b) >>> 0;
|
|
73
|
+
h[2] = (h[2] + c) >>> 0;
|
|
74
|
+
h[3] = (h[3] + d) >>> 0;
|
|
75
|
+
h[4] = (h[4] + e) >>> 0;
|
|
76
|
+
h[5] = (h[5] + f) >>> 0;
|
|
77
|
+
h[6] = (h[6] + g) >>> 0;
|
|
78
|
+
h[7] = (h[7] + hh) >>> 0;
|
|
79
|
+
}
|
|
80
|
+
return Array.from(h, (word) => word.toString(16).padStart(8, "0")).join("");
|
|
81
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** `serve`'s options. */
|
|
2
|
+
export type AccessServeOptions = {
|
|
3
|
+
/** The port to listen on; `0` picks a free one. Default `3000`. */
|
|
4
|
+
readonly port?: number;
|
|
5
|
+
/**
|
|
6
|
+
* The interface to listen on. Default `"127.0.0.1"`, this host only; `"0.0.0.0"`
|
|
7
|
+
* listens on every interface (in a container, behind a proxy that terminates TLS).
|
|
8
|
+
*/
|
|
9
|
+
readonly hostname?: string;
|
|
10
|
+
/** The largest request body taken, in bytes. Default 1 MiB (`1048576`). */
|
|
11
|
+
readonly maxBodyBytes?: number;
|
|
12
|
+
};
|
|
13
|
+
/** A running standalone host. */
|
|
14
|
+
export type AccessServer = {
|
|
15
|
+
/** The port it listens on (the one picked, when `port` was `0`). */
|
|
16
|
+
readonly port: number;
|
|
17
|
+
/** Its base URL, such as `http://127.0.0.1:3000`. */
|
|
18
|
+
readonly url: string;
|
|
19
|
+
/**
|
|
20
|
+
* Stops accepting, lets the requests in flight finish for up to 10 s, then ends
|
|
21
|
+
* the connections left; resolves once the server is closed.
|
|
22
|
+
*/
|
|
23
|
+
close(): Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* What `serve` takes: an instance with a handler (`createAccess`'s, or any), and the
|
|
27
|
+
* options Better Auth runs with, which it reads for the base path and how the
|
|
28
|
+
* client's address is read; and whether the registry is on.
|
|
29
|
+
*/
|
|
30
|
+
type Served = {
|
|
31
|
+
readonly handler: (request: Request) => Promise<Response>;
|
|
32
|
+
readonly options?: object;
|
|
33
|
+
readonly registry?: unknown;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Serves an Access instance over HTTP: its routes, as `access.handler` answers them
|
|
37
|
+
* (Better Auth's base path, `/api/auth` unless `betterAuth.basePath` says otherwise),
|
|
38
|
+
* and `GET /health`. Resolves once the server listens.
|
|
39
|
+
*/
|
|
40
|
+
export declare const serve: (access: Served, options?: AccessServeOptions) => Promise<AccessServer>;
|
|
41
|
+
export {};
|