@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,48 @@
|
|
|
1
|
+
/** A scalar field as discovery states it. */
|
|
2
|
+
export type AccessSchemaScalarField = {
|
|
3
|
+
readonly kind: "scalar";
|
|
4
|
+
readonly type: {
|
|
5
|
+
readonly scalar: string;
|
|
6
|
+
} | {
|
|
7
|
+
readonly enum: string;
|
|
8
|
+
};
|
|
9
|
+
readonly nullable: boolean;
|
|
10
|
+
readonly list: boolean;
|
|
11
|
+
};
|
|
12
|
+
/** A relation field as discovery states it, with the `references` it holds. */
|
|
13
|
+
export type AccessSchemaRelationField = {
|
|
14
|
+
readonly kind: "relation";
|
|
15
|
+
readonly target: string;
|
|
16
|
+
readonly cardinality: "one" | "many";
|
|
17
|
+
readonly nullable: boolean;
|
|
18
|
+
readonly inverse?: string;
|
|
19
|
+
readonly referenceFields?: readonly [string, ...string[]];
|
|
20
|
+
readonly references?: readonly [string, ...string[]];
|
|
21
|
+
};
|
|
22
|
+
/** One field of a Resource. */
|
|
23
|
+
export type AccessSchemaField = AccessSchemaScalarField | AccessSchemaRelationField;
|
|
24
|
+
/** One Resource: its fields and the field tuples that identify a row. */
|
|
25
|
+
export type AccessSchemaResource = {
|
|
26
|
+
readonly fields: {
|
|
27
|
+
readonly [field: string]: AccessSchemaField;
|
|
28
|
+
};
|
|
29
|
+
readonly identifiers: readonly (readonly string[])[];
|
|
30
|
+
/**
|
|
31
|
+
* The namespace that owns this Resource, when another one does (`{ foreign: "campaign" }`).
|
|
32
|
+
* Rules read a foreign Resource in conditions and never grant on it.
|
|
33
|
+
*/
|
|
34
|
+
readonly foreign?: string;
|
|
35
|
+
};
|
|
36
|
+
/** The schema facts Access reads: the enums and the Resources. */
|
|
37
|
+
export type AccessSchema = {
|
|
38
|
+
readonly enums: {
|
|
39
|
+
readonly [name: string]: {
|
|
40
|
+
readonly values: readonly string[];
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
readonly resources: {
|
|
44
|
+
readonly [resource: string]: AccessSchemaResource;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
/** The TypeScript value a built-in scalar compares with. Unknown scalars compare with `unknown`. */
|
|
48
|
+
export type AccessScalarValue<Scalar extends string> = Scalar extends "string" ? string : Scalar extends "boolean" ? boolean : Scalar extends "int" | "float" ? number : Scalar extends "bigint" ? bigint : Scalar extends "decimal" ? number | string : Scalar extends "datetime" ? Date | string : unknown;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// The schema shape Access is typed from and compiles against. It is a structural
|
|
2
|
+
// subset of core's `AvAdapterModel` (a Prisma discovery artifact's `model`), so an
|
|
3
|
+
// Aventara app passes its discovery model in and Access imports nothing from core.
|
|
4
|
+
// The type test `test/core-seam.test-d.ts` holds the two together.
|
|
5
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// How the engine's checks against a hosting framework warn: Node.js's
|
|
2
|
+
// `process.emitWarning` (type `AventaraAccessWarning`, the check's code, silenced
|
|
3
|
+
// with `--disable-warning=<code>`), else `console.warn`. Each message is emitted
|
|
4
|
+
// once per process and namespace, however many Frameworks host the rules.
|
|
5
|
+
const emitted = new Set();
|
|
6
|
+
/** Emits `message` under `code`, once per process and `namespace`. */
|
|
7
|
+
export const emitAccessWarning = (code, namespace, message) => {
|
|
8
|
+
const key = `${code}\u0000${namespace ?? ""}\u0000${message}`;
|
|
9
|
+
if (emitted.has(key)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
emitted.add(key);
|
|
13
|
+
const runtime = globalThis.process;
|
|
14
|
+
if (typeof runtime?.emitWarning === "function") {
|
|
15
|
+
runtime.emitWarning(message, { type: "AventaraAccessWarning", code });
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
globalThis.console?.warn(`${code}: ${message}`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import { type AccessEngine, type AccessEngineOptions } from "./access.engine.js";
|
|
2
|
+
import type { AccessScalarValue, AccessSchema } from "./access-schema.js";
|
|
3
|
+
import type { AccessAction, AccessBuiltInSubject, AccessOrigin, AccessPolicies } from "./policies-contract.js";
|
|
4
|
+
declare const principalRefBrand: unique symbol;
|
|
5
|
+
/**
|
|
6
|
+
* A value read off the principal when a question is asked (`me.id`). `T` is the
|
|
7
|
+
* value's type, so `me.id` compares only with a field of that type.
|
|
8
|
+
*/
|
|
9
|
+
export type AccessPrincipalRef<T = unknown> = {
|
|
10
|
+
readonly [principalRefBrand]: T;
|
|
11
|
+
readonly path: readonly [string, ...string[]];
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The principal, symbolically, typed from `.principal<P>()`: one key per key of
|
|
15
|
+
* `P`, so `me.profleId` is a tsc error. `me.id` is a token the compiler writes
|
|
16
|
+
* into the file as `{ principal: ["id"] }`, never a value. A rule about `me` is
|
|
17
|
+
* false for an anonymous (null) principal, and for one that lacks the value.
|
|
18
|
+
*/
|
|
19
|
+
export type AccessMe<P> = {
|
|
20
|
+
readonly [K in Extract<keyof NonNullable<P>, string>]-?: AccessPrincipalRef<NonNullable<NonNullable<P>[K]>>;
|
|
21
|
+
};
|
|
22
|
+
type Simplify<T> = {
|
|
23
|
+
[K in keyof T]: T[K];
|
|
24
|
+
} & {};
|
|
25
|
+
type GovernedShape = {
|
|
26
|
+
readonly [resource: string]: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
/** A row role as the builder's type records it: the Resource it is held on. */
|
|
29
|
+
type RoleShape = {
|
|
30
|
+
readonly on: string;
|
|
31
|
+
} | {
|
|
32
|
+
readonly global: unknown;
|
|
33
|
+
};
|
|
34
|
+
/** The roles in scope inside a role's own `where`: any name (the compiler checks them). */
|
|
35
|
+
type AnyRoles = {
|
|
36
|
+
readonly [role: string]: {
|
|
37
|
+
readonly on: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
type ResourceOf<S extends AccessSchema> = Extract<keyof S["resources"], string>;
|
|
41
|
+
/**
|
|
42
|
+
* The file's own Resources: all but the foreign ones (`{ foreign: "<namespace>" }`).
|
|
43
|
+
* Grants, denies and governed fields name only these; a foreign Resource is read in
|
|
44
|
+
* conditions alone.
|
|
45
|
+
*/
|
|
46
|
+
type OwnResourceOf<S extends AccessSchema> = {
|
|
47
|
+
[R in ResourceOf<S>]: S["resources"][R] extends {
|
|
48
|
+
readonly foreign: string;
|
|
49
|
+
} ? never : R;
|
|
50
|
+
}[ResourceOf<S>];
|
|
51
|
+
type FieldMap<S extends AccessSchema, R extends ResourceOf<S>> = S["resources"][R]["fields"];
|
|
52
|
+
/** A Resource's field names. */
|
|
53
|
+
export type AccessFieldOf<S extends AccessSchema, R extends ResourceOf<S>> = Extract<keyof FieldMap<S, R>, string>;
|
|
54
|
+
type NullIf<F> = F extends {
|
|
55
|
+
readonly nullable: true;
|
|
56
|
+
} ? null : never;
|
|
57
|
+
/** The literal a scalar field compares with: its enum's values, or its scalar's TS type. */
|
|
58
|
+
type ScalarValue<S extends AccessSchema, F> = F extends {
|
|
59
|
+
readonly list: true;
|
|
60
|
+
} ? never : F extends {
|
|
61
|
+
readonly type: {
|
|
62
|
+
readonly enum: infer E extends keyof S["enums"];
|
|
63
|
+
};
|
|
64
|
+
} ? S["enums"][E]["values"][number] | NullIf<F> : F extends {
|
|
65
|
+
readonly type: {
|
|
66
|
+
readonly scalar: infer T extends string;
|
|
67
|
+
};
|
|
68
|
+
} ? AccessScalarValue<T> | NullIf<F> : never;
|
|
69
|
+
type Operand<V> = V | AccessPrincipalRef<V>;
|
|
70
|
+
type ScalarCondition<V> = Operand<V> | {
|
|
71
|
+
readonly equals?: Operand<V>;
|
|
72
|
+
readonly not?: Operand<V>;
|
|
73
|
+
readonly in?: readonly Operand<V>[];
|
|
74
|
+
readonly notIn?: readonly Operand<V>[];
|
|
75
|
+
};
|
|
76
|
+
type RelationCondition<S extends AccessSchema, F, Roles> = F extends {
|
|
77
|
+
readonly cardinality: "one";
|
|
78
|
+
readonly target: infer T extends ResourceOf<S>;
|
|
79
|
+
} ? AccessWhereInput<S, T, Roles> : F extends {
|
|
80
|
+
readonly cardinality: "many";
|
|
81
|
+
readonly target: infer T extends ResourceOf<S>;
|
|
82
|
+
} ? {
|
|
83
|
+
readonly some?: AccessWhereInput<S, T, Roles>;
|
|
84
|
+
readonly every?: AccessWhereInput<S, T, Roles>;
|
|
85
|
+
readonly none?: AccessWhereInput<S, T, Roles>;
|
|
86
|
+
} : never;
|
|
87
|
+
type FieldCondition<S extends AccessSchema, F, Roles> = F extends {
|
|
88
|
+
readonly kind: "relation";
|
|
89
|
+
} ? RelationCondition<S, F, Roles> : ScalarCondition<ScalarValue<S, F>>;
|
|
90
|
+
/** The row roles held on Resource `R`. */
|
|
91
|
+
type RoleOn<Roles, R extends string> = string extends keyof Roles ? string : {
|
|
92
|
+
[K in keyof Roles]: Roles[K] extends {
|
|
93
|
+
readonly on: R;
|
|
94
|
+
} ? K : never;
|
|
95
|
+
}[keyof Roles] & string;
|
|
96
|
+
/**
|
|
97
|
+
* A row condition on Resource `R`, in Aventara's `where` vocabulary: fields by
|
|
98
|
+
* name (a value is `equals`), `AND` / `OR` / `NOT`, a to-one relation by direct
|
|
99
|
+
* match, a to-many by `some` / `every` / `none`, and `$role`, a row role held on
|
|
100
|
+
* `R`.
|
|
101
|
+
*/
|
|
102
|
+
export type AccessWhereInput<S extends AccessSchema, R extends ResourceOf<S>, Roles> = {
|
|
103
|
+
readonly [F in AccessFieldOf<S, R>]?: FieldCondition<S, FieldMap<S, R>[F], Roles>;
|
|
104
|
+
} & {
|
|
105
|
+
readonly AND?: readonly AccessWhereInput<S, R, Roles>[];
|
|
106
|
+
readonly OR?: readonly AccessWhereInput<S, R, Roles>[];
|
|
107
|
+
readonly NOT?: AccessWhereInput<S, R, Roles>;
|
|
108
|
+
readonly $role?: RoleOn<Roles, R>;
|
|
109
|
+
};
|
|
110
|
+
type ClaimsOf<P> = P extends {
|
|
111
|
+
readonly claims?: infer C;
|
|
112
|
+
} ? NonNullable<C> : never;
|
|
113
|
+
/** A condition on the principal's claims: each named claim equals the value. */
|
|
114
|
+
export type AccessClaimsCondition<P> = [ClaimsOf<P>] extends [never] ? never : {
|
|
115
|
+
readonly [K in keyof ClaimsOf<P>]?: Exclude<ClaimsOf<P>[K], undefined>;
|
|
116
|
+
};
|
|
117
|
+
type RowRoleDefinition<S extends AccessSchema> = {
|
|
118
|
+
[R in ResourceOf<S>]: {
|
|
119
|
+
readonly on: R;
|
|
120
|
+
readonly where: AccessWhereInput<S, R, AnyRoles>;
|
|
121
|
+
};
|
|
122
|
+
}[ResourceOf<S>];
|
|
123
|
+
type GlobalRoleDefinition<P> = {
|
|
124
|
+
readonly global: {
|
|
125
|
+
readonly claims: AccessClaimsCondition<P>;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
/** The `.roles({...})` argument: global roles from claims, row roles on one Resource. */
|
|
129
|
+
export type AccessRoleDefinitions<S extends AccessSchema, P> = {
|
|
130
|
+
readonly [role: string]: GlobalRoleDefinition<P> | RowRoleDefinition<S>;
|
|
131
|
+
};
|
|
132
|
+
/** Each role of `D` checked against its own shape; built-in subject names refused. */
|
|
133
|
+
type CheckedRoles<S extends AccessSchema, P, D> = {
|
|
134
|
+
readonly [K in keyof D]: K extends AccessBuiltInSubject ? never : D[K] extends {
|
|
135
|
+
readonly on: infer R extends ResourceOf<S>;
|
|
136
|
+
readonly where: infer W;
|
|
137
|
+
} ? {
|
|
138
|
+
readonly on: R;
|
|
139
|
+
readonly where: NoExtraKeys<W, AccessWhereInput<S, R, AnyRoles>, "a field, relation or operator">;
|
|
140
|
+
} : D[K] extends {
|
|
141
|
+
readonly global: {
|
|
142
|
+
readonly claims: infer C;
|
|
143
|
+
};
|
|
144
|
+
} ? {
|
|
145
|
+
readonly global: {
|
|
146
|
+
readonly claims: NoExtraKeys<C, AccessClaimsCondition<P>, "a claim of the principal">;
|
|
147
|
+
};
|
|
148
|
+
} : AccessRoleDefinitions<S, P>[string];
|
|
149
|
+
};
|
|
150
|
+
/** Every key of every object member of a union. */
|
|
151
|
+
type KeysOfUnion<U> = U extends unknown ? keyof U : never;
|
|
152
|
+
/** The type of key `K` across the object members of a union that have it. */
|
|
153
|
+
type MemberOf<U, K> = U extends unknown ? K extends keyof U ? U[K] : never : never;
|
|
154
|
+
/**
|
|
155
|
+
* `T` checked against `Shape`, at any depth: each key `Shape` does not have is
|
|
156
|
+
* typed as the sentence that says so, and each value as `Shape` allows it.
|
|
157
|
+
*/
|
|
158
|
+
type NoExtraKeys<T, Shape, Noun extends string> = T extends string | number | boolean | bigint | symbol | null | undefined | AccessPrincipalRef ? Shape : T extends readonly (infer E)[] ? Shape extends unknown ? readonly NoExtraKeys<E, NonNullable<Shape> extends readonly (infer SE)[] ? SE : never, Noun>[] : never : {
|
|
159
|
+
readonly [K in keyof T]: K extends KeysOfUnion<Exclude<Extract<NonNullable<Shape>, object>, AccessPrincipalRef>> ? NoExtraKeys<T[K], MemberOf<Exclude<Extract<NonNullable<Shape>, object>, AccessPrincipalRef>, K>, Noun> : `Error: "${K & string}" is not ${Noun} here`;
|
|
160
|
+
};
|
|
161
|
+
/** Who a grant is for: a built-in subject or a role. */
|
|
162
|
+
type Subject<Roles> = AccessBuiltInSubject | Extract<keyof Roles, string>;
|
|
163
|
+
type OneOrMany<T> = T | readonly [T, ...T[]];
|
|
164
|
+
type Each<T> = T extends readonly (infer X)[] ? X : T;
|
|
165
|
+
/** The Resource every subject's row role is held on; `never` when one subject is not a row role. */
|
|
166
|
+
type SubjectsOn<Roles, Sub> = [Sub] extends [never] ? never : Sub extends keyof Roles ? Roles[Sub] extends {
|
|
167
|
+
readonly on: infer On extends string;
|
|
168
|
+
} ? On : "~not-a-row-role" : "~not-a-row-role";
|
|
169
|
+
/** The to-one relations of `R` that lead to `On`. */
|
|
170
|
+
type InRelation<S extends AccessSchema, R extends ResourceOf<S>, On> = {
|
|
171
|
+
[F in AccessFieldOf<S, R>]: FieldMap<S, R>[F] extends {
|
|
172
|
+
readonly kind: "relation";
|
|
173
|
+
readonly cardinality: "one";
|
|
174
|
+
readonly target: infer T;
|
|
175
|
+
} ? T extends On ? F : never : never;
|
|
176
|
+
}[AccessFieldOf<S, R>];
|
|
177
|
+
type InOption<S extends AccessSchema, R, Roles, Sub> = R extends ResourceOf<S> ? "~not-a-row-role" extends SubjectsOn<Roles, Each<Sub>> ? never : InRelation<S, R, SubjectsOn<Roles, Each<Sub>>> : never;
|
|
178
|
+
/**
|
|
179
|
+
* A governed field named `Resource.field`. On `"*"`, any Resource's governed fields.
|
|
180
|
+
* A Resource with no governed field has none to name: the type is the sentence that
|
|
181
|
+
* says to govern one first, so naming a field reads as what to change.
|
|
182
|
+
*/
|
|
183
|
+
type GovernedRef<Gov, R> = R extends "*" ? [keyof Gov] extends [never] ? "Error: no field is governed; govern one first, .govern(<Resource>, [<field>]), and a grant can then open it" : {
|
|
184
|
+
[K in keyof Gov]: `${K & string}.${Gov[K] extends readonly (infer F extends string)[] ? F : never}`;
|
|
185
|
+
}[keyof Gov] : R extends keyof Gov ? `${R & string}.${Gov[R] extends readonly (infer F extends string)[] ? F : never}` : `Error: no field of "${R & string}" is governed; govern it first, .govern("${R & string}", [<field>]), and a grant can then open it`;
|
|
186
|
+
type ResourceWhere<S extends AccessSchema, R, Roles> = R extends ResourceOf<S> ? AccessWhereInput<S, R, Roles> : never;
|
|
187
|
+
/** The options of `grant(subjects, actions, resource, options)`. */
|
|
188
|
+
export type AccessGrantOptions<S extends AccessSchema, Roles, Gov, R, Sub, A> = {
|
|
189
|
+
/** A name for the rule's id (else `Resource/actions/subjects#n`). */
|
|
190
|
+
readonly name?: string;
|
|
191
|
+
/** The rows the grant covers. */
|
|
192
|
+
readonly where?: ResourceWhere<S, R, Roles>;
|
|
193
|
+
/** `in: "campaign"`: each subject holds its row role on this row's `campaign`. */
|
|
194
|
+
readonly in?: InOption<S, R, Roles, Sub>;
|
|
195
|
+
/** Governed fields the grant opens, as `Resource.field`. */
|
|
196
|
+
readonly fields?: readonly GovernedRef<Gov, R>[];
|
|
197
|
+
/** Fields a create or update by this grant may not write. */
|
|
198
|
+
readonly write?: Extract<Each<A>, "create" | "update"> extends never ? never : R extends ResourceOf<S> ? {
|
|
199
|
+
readonly except: readonly AccessFieldOf<S, R>[];
|
|
200
|
+
} : never;
|
|
201
|
+
/** What the row an update leaves must still meet. */
|
|
202
|
+
readonly after?: "update" extends Each<A> ? ResourceWhere<S, R, Roles> : never;
|
|
203
|
+
};
|
|
204
|
+
/** The options of `deny(actions, resource, options)`. */
|
|
205
|
+
export type AccessDenyOptions<S extends AccessSchema, P, Roles, R> = {
|
|
206
|
+
/** A name for the rule's id (else `Resource/actions/deny#n`). */
|
|
207
|
+
readonly name?: string;
|
|
208
|
+
/** The rows the deny covers. */
|
|
209
|
+
readonly where?: ResourceWhere<S, R, Roles>;
|
|
210
|
+
/** A condition on the principal alone: its claims and the request's origin. */
|
|
211
|
+
readonly if?: {
|
|
212
|
+
readonly claims?: AccessClaimsCondition<P>;
|
|
213
|
+
readonly origin?: AccessOrigin;
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
declare const grantBrand: unique symbol;
|
|
217
|
+
declare const denyBrand: unique symbol;
|
|
218
|
+
/** A grant, as data, before compilation. */
|
|
219
|
+
export type AccessGrant = {
|
|
220
|
+
readonly [grantBrand]: true;
|
|
221
|
+
readonly subjects: OneOrMany<string>;
|
|
222
|
+
readonly actions: OneOrMany<AccessAction>;
|
|
223
|
+
readonly resource: string;
|
|
224
|
+
readonly options: {
|
|
225
|
+
readonly [option: string]: unknown;
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
/** A deny, as data, before compilation. */
|
|
229
|
+
export type AccessDeny = {
|
|
230
|
+
readonly [denyBrand]: true;
|
|
231
|
+
readonly actions: "*" | OneOrMany<AccessAction>;
|
|
232
|
+
readonly resource: string;
|
|
233
|
+
readonly options: {
|
|
234
|
+
readonly [option: string]: unknown;
|
|
235
|
+
};
|
|
236
|
+
};
|
|
237
|
+
/** `grant(subjects, actions, resource, options?)`: any grant allows. */
|
|
238
|
+
export type AccessGrantFunction<S extends AccessSchema, Roles, Gov> = <const Sub extends OneOrMany<Subject<Roles>>, const A extends OneOrMany<AccessAction>, const R extends OwnResourceOf<S> | "*">(subjects: Sub, actions: A, resource: R, options?: AccessGrantOptions<S, Roles, Gov, NoInfer<R>, NoInfer<Sub>, NoInfer<A>>) => AccessGrant;
|
|
239
|
+
/** `deny(actions, resource, options?)`: any deny wins. */
|
|
240
|
+
export type AccessDenyFunction<S extends AccessSchema, P, Roles> = <const A extends "*" | OneOrMany<AccessAction>, const R extends OwnResourceOf<S> | "*">(actions: A, resource: R, options?: AccessDenyOptions<S, P, Roles, NoInfer<R>>) => AccessDeny;
|
|
241
|
+
/** The rules as written, before compilation. */
|
|
242
|
+
export type AccessDefinition = {
|
|
243
|
+
readonly schema: AccessSchema;
|
|
244
|
+
readonly namespace?: string;
|
|
245
|
+
readonly roles: {
|
|
246
|
+
readonly [role: string]: unknown;
|
|
247
|
+
};
|
|
248
|
+
readonly govern: {
|
|
249
|
+
readonly [resource: string]: readonly string[];
|
|
250
|
+
};
|
|
251
|
+
readonly grants: readonly AccessGrant[];
|
|
252
|
+
readonly denies: readonly AccessDeny[];
|
|
253
|
+
};
|
|
254
|
+
/** The builder `definePolicies` returns. Each call returns a new builder; none has side effects. */
|
|
255
|
+
export interface AccessBuilder<S extends AccessSchema, P, Roles extends {
|
|
256
|
+
readonly [role: string]: RoleShape;
|
|
257
|
+
}, Gov extends GovernedShape> {
|
|
258
|
+
/** Types the principal. Anonymous is `null`; rules about `me` are false for it. */
|
|
259
|
+
principal<T>(): AccessBuilder<S, T, Roles, Gov>;
|
|
260
|
+
/** Global roles (`global: { claims }`) and roles held on a row (`on`, `where`). */
|
|
261
|
+
roles<const D extends {
|
|
262
|
+
readonly [role: string]: RoleShape;
|
|
263
|
+
}>(definitions: (tools: {
|
|
264
|
+
readonly me: AccessMe<P>;
|
|
265
|
+
}) => D extends CheckedRoles<S, P, D> ? D : CheckedRoles<S, P, D>): AccessBuilder<S, P, Simplify<Roles & D>, Gov>;
|
|
266
|
+
/** Governed fields: out of every default projection; only a grant that names one opens it, and only when selected. */
|
|
267
|
+
govern<R extends OwnResourceOf<S>, const F extends readonly [AccessFieldOf<S, R>, ...AccessFieldOf<S, R>[]]>(resource: R, fields: F): AccessBuilder<S, P, Roles, Simplify<Gov & {
|
|
268
|
+
readonly [K in R]: F;
|
|
269
|
+
}>>;
|
|
270
|
+
/** Grants. Default deny; any grant allows; order never matters. */
|
|
271
|
+
grants(rules: (tools: {
|
|
272
|
+
readonly grant: AccessGrantFunction<S, Roles, Gov>;
|
|
273
|
+
readonly me: AccessMe<P>;
|
|
274
|
+
}) => readonly AccessGrant[]): AccessBuilder<S, P, Roles, Gov>;
|
|
275
|
+
/** Denies. Any deny wins over every grant; order never matters. */
|
|
276
|
+
denies(rules: (tools: {
|
|
277
|
+
readonly deny: AccessDenyFunction<S, P, Roles>;
|
|
278
|
+
readonly me: AccessMe<P>;
|
|
279
|
+
}) => readonly AccessDeny[]): AccessBuilder<S, P, Roles, Gov>;
|
|
280
|
+
/** The compiled `@1` policies file. */
|
|
281
|
+
contract(): AccessPolicies<Gov>;
|
|
282
|
+
/**
|
|
283
|
+
* The engine, run from the compiled file and, with `guardrails`, the org's
|
|
284
|
+
* guardrails on top of it. With `lock`, the guardrails are held to the hash
|
|
285
|
+
* `avAccess.lock.json` pins, and the rules' namespace to its namespace.
|
|
286
|
+
* Satisfies core's `AvPolicyEngine` by shape.
|
|
287
|
+
*/
|
|
288
|
+
engine(options?: AccessEngineOptions): AccessEngine<Gov>;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Starts the rules for an app, typed from its schema (an Aventara discovery
|
|
292
|
+
* artifact's `model`, or any object of the {@link AccessSchema} shape).
|
|
293
|
+
* `namespace` names the namespace that owns these Resources; it is written into
|
|
294
|
+
* the compiled file.
|
|
295
|
+
*/
|
|
296
|
+
export declare const definePolicies: <const S extends AccessSchema>(schema: S, options?: {
|
|
297
|
+
readonly namespace?: string;
|
|
298
|
+
}) => AccessBuilder<S, unknown, Record<never, never>, Record<never, never>>;
|
|
299
|
+
export {};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// `definePolicies(model)`: the typed authoring API. It captures the author's
|
|
2
|
+
// rules as plain data and compiles them to the `@1` policies file; the engine runs
|
|
3
|
+
// from that file alone. Every name a rule uses (Resource, field,
|
|
4
|
+
// relation, enum value, role, governed field) is checked at its key against the
|
|
5
|
+
// schema the builder was created from.
|
|
6
|
+
import { engineOf, } from "./access.engine.js";
|
|
7
|
+
import { compilePolicies, PRINCIPAL_REF } from "./policies.compiler.js";
|
|
8
|
+
/** The one runtime `me`: any key reads as a principal reference. */
|
|
9
|
+
const me = new Proxy({}, {
|
|
10
|
+
get(_target, key) {
|
|
11
|
+
if (typeof key !== "string") {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
[PRINCIPAL_REF]: true,
|
|
16
|
+
path: Object.freeze([key]),
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const grant = (subjects, actions, resource, options = {}) => Object.freeze({
|
|
21
|
+
subjects,
|
|
22
|
+
actions,
|
|
23
|
+
resource,
|
|
24
|
+
options,
|
|
25
|
+
});
|
|
26
|
+
const deny = (actions, resource, options = {}) => Object.freeze({ actions, resource, options });
|
|
27
|
+
const builderOf = (definition) => ({
|
|
28
|
+
principal: () => builderOf(definition),
|
|
29
|
+
roles: (definitions) => builderOf({
|
|
30
|
+
...definition,
|
|
31
|
+
roles: { ...definition.roles, ...definitions({ me }) },
|
|
32
|
+
}),
|
|
33
|
+
govern: (resource, fields) => builderOf({
|
|
34
|
+
...definition,
|
|
35
|
+
govern: {
|
|
36
|
+
...definition.govern,
|
|
37
|
+
[resource]: [...(definition.govern[resource] ?? []), ...fields],
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
grants: (rules) => builderOf({
|
|
41
|
+
...definition,
|
|
42
|
+
grants: [...definition.grants, ...rules({ grant, me })],
|
|
43
|
+
}),
|
|
44
|
+
denies: (rules) => builderOf({
|
|
45
|
+
...definition,
|
|
46
|
+
denies: [...definition.denies, ...rules({ deny, me })],
|
|
47
|
+
}),
|
|
48
|
+
contract: () => compilePolicies(definition),
|
|
49
|
+
engine: (options) => engineOf(compilePolicies(definition), options, "rules"),
|
|
50
|
+
});
|
|
51
|
+
/**
|
|
52
|
+
* Starts the rules for an app, typed from its schema (an Aventara discovery
|
|
53
|
+
* artifact's `model`, or any object of the {@link AccessSchema} shape).
|
|
54
|
+
* `namespace` names the namespace that owns these Resources; it is written into
|
|
55
|
+
* the compiled file.
|
|
56
|
+
*/
|
|
57
|
+
export const definePolicies = (schema, options) => builderOf({
|
|
58
|
+
schema,
|
|
59
|
+
...(options?.namespace !== undefined
|
|
60
|
+
? { namespace: options.namespace }
|
|
61
|
+
: {}),
|
|
62
|
+
roles: {},
|
|
63
|
+
govern: {},
|
|
64
|
+
grants: [],
|
|
65
|
+
denies: [],
|
|
66
|
+
});
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { AccessWhere } from "./aventara-where.translator.js";
|
|
2
|
+
import { type AccessBeaconPins, type AccessBeaconReader } from "./beacon/beacon.check.js";
|
|
3
|
+
import type { AccessGuardrails, AccessGuardrailsJson } from "./guardrails/guardrails-contract.js";
|
|
4
|
+
import type { AccessLock } from "./lock/lock-contract.js";
|
|
5
|
+
import { type AccessCanQuestion, type AccessFields, type AccessFieldsQuestion, type AccessGrantsQuestion, type AccessRuleOutcome, type AccessScopeQuestion } from "./policies.decider.js";
|
|
6
|
+
import type { AccessPolicies, AccessPoliciesJson } from "./policies-contract.js";
|
|
7
|
+
import type { AccessLookup } from "./predicate.evaluator.js";
|
|
8
|
+
import { type AuthTablesGuard } from "./resources/auth-tables.guard.js";
|
|
9
|
+
type GovernedShape = {
|
|
10
|
+
readonly [resource: string]: readonly string[];
|
|
11
|
+
};
|
|
12
|
+
/** `explain`'s answer: the decision, each rule's part in it, and the `exists` lookups. */
|
|
13
|
+
export type AccessExplanation = {
|
|
14
|
+
readonly allowed: boolean;
|
|
15
|
+
readonly grants: readonly {
|
|
16
|
+
readonly id: string;
|
|
17
|
+
readonly subject: string;
|
|
18
|
+
readonly outcome: AccessRuleOutcome;
|
|
19
|
+
}[];
|
|
20
|
+
readonly denies: readonly {
|
|
21
|
+
readonly id: string;
|
|
22
|
+
readonly outcome: AccessRuleOutcome;
|
|
23
|
+
}[];
|
|
24
|
+
readonly lookups: readonly AccessLookup[];
|
|
25
|
+
};
|
|
26
|
+
/** The engine `access.engine()` and `createAccessEngine(policies)` return. */
|
|
27
|
+
export interface AccessEngine<Governed extends GovernedShape = GovernedShape> {
|
|
28
|
+
/** The governed fields, by Resource: out of every default projection. */
|
|
29
|
+
readonly governed: Governed;
|
|
30
|
+
/** Every relation path and operator each rule filters on, by rule id. */
|
|
31
|
+
readonly paths: {
|
|
32
|
+
readonly [rule: string]: readonly (readonly string[])[];
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The Resources where the row an update leaves is checked: an update grant there
|
|
36
|
+
* reads the row (`where`, `after`, a role held on a row), or an update deny has a
|
|
37
|
+
* `where`.
|
|
38
|
+
*/
|
|
39
|
+
readonly afterChecks: {
|
|
40
|
+
readonly [resource: string]: readonly "update"[];
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The Resources the rules read as foreign: read in conditions, never granted on,
|
|
44
|
+
* so no caller reads a row of one. Aventara (`@aventara/core` `1.0.0-rc.10` or
|
|
45
|
+
* later) then holds no written reference to one to the caller's read scope: the
|
|
46
|
+
* write grant decides which row may be linked (its `where`, its `after`).
|
|
47
|
+
*/
|
|
48
|
+
readonly unheld: readonly string[];
|
|
49
|
+
can(question: AccessCanQuestion): boolean | Promise<boolean>;
|
|
50
|
+
/**
|
|
51
|
+
* The update grants that cover `row`, the row an update in `afterChecks` will
|
|
52
|
+
* change, as it is before the write: each grant's `where` and its subject's row
|
|
53
|
+
* condition, relation conditions asked through `exists`. Core asks it before the
|
|
54
|
+
* write and hands the answer back to `can` as `beforeGrants` (`@aventara/core`
|
|
55
|
+
* `1.0.0-rc.10` or later). The keys are opaque: a grant's place in the file, no
|
|
56
|
+
* rule data.
|
|
57
|
+
*/
|
|
58
|
+
grantsOf(question: AccessGrantsQuestion): readonly string[] | Promise<readonly string[]>;
|
|
59
|
+
scopeOf(question: AccessScopeQuestion): boolean | AccessWhere;
|
|
60
|
+
fieldsOf(question: AccessFieldsQuestion): AccessFields;
|
|
61
|
+
/**
|
|
62
|
+
* Called by the framework once it is built on this engine (`@aventara/core`
|
|
63
|
+
* `1.0.0-rc.10` or later): `held` lists the Resources a written reference is
|
|
64
|
+
* held to the caller's read scope of. The rules are checked against them, and a
|
|
65
|
+
* role that may write a reference to one no read grant reaches for it is warned
|
|
66
|
+
* about (ACCESS_REFERENCE_UNREADABLE), and so is a role whose create or update
|
|
67
|
+
* grant may write a reference to a foreign Resource with no condition through
|
|
68
|
+
* that relation (ACCESS_FOREIGN_REFERENCE_UNCHECKED), each once per process and
|
|
69
|
+
* namespace. It changes nothing the engine answers.
|
|
70
|
+
*/
|
|
71
|
+
hosted(host: {
|
|
72
|
+
readonly held: readonly string[];
|
|
73
|
+
}): void;
|
|
74
|
+
/** Development only: why `can` answers as it does. Throws when `NODE_ENV` is `production`. */
|
|
75
|
+
explain(question: AccessCanQuestion): Promise<AccessExplanation>;
|
|
76
|
+
/**
|
|
77
|
+
* The startup check that the service reaches the database of each namespace
|
|
78
|
+
* whose Resources the rules read as foreign: its row in `_aventara_beacon`, read
|
|
79
|
+
* through `reader` (the service's own connection), hashes to its pin in `pins`,
|
|
80
|
+
* the pins of `environment` (`avAccess.beacons.<environment>.json`). Await it
|
|
81
|
+
* before the service takes requests: an engine whose rules read another
|
|
82
|
+
* namespace answers no question until it has passed, and none once a check has
|
|
83
|
+
* failed, for good (`ACCESS_BEACON_MISMATCH`); calls run one after another. An
|
|
84
|
+
* engine that reads no other namespace passes at once.
|
|
85
|
+
*/
|
|
86
|
+
checkBeacons(reader: AccessBeaconReader, pins: AccessBeaconPins, environment: string): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/** What an engine is built with beside its policies file. */
|
|
89
|
+
export type AccessEngineOptions = {
|
|
90
|
+
/**
|
|
91
|
+
* The org's guardrails, as pulled from the registry: deny-only rules on top of the
|
|
92
|
+
* file's own. They only narrow; the governed fields, the paths and the types are
|
|
93
|
+
* the file's.
|
|
94
|
+
*/
|
|
95
|
+
readonly guardrails?: AccessGuardrails | AccessGuardrailsJson;
|
|
96
|
+
/**
|
|
97
|
+
* The project's `avAccess.lock.json`, as `avaccess pull` wrote it. With it, the
|
|
98
|
+
* engine refuses to start (`ACCESS_LOCK_MISMATCH`) unless what it runs from is
|
|
99
|
+
* what was pulled: the guardrails hash to the lock's pin (and are given exactly
|
|
100
|
+
* when it pins some), and a policies file handed to `createAccessEngine` hashes
|
|
101
|
+
* to its own. Typed rules (`rules.engine()`) are the project's own source: only
|
|
102
|
+
* their namespace is held to the lock's, unless `requireLock` is set.
|
|
103
|
+
*/
|
|
104
|
+
readonly lock?: AccessLock;
|
|
105
|
+
/**
|
|
106
|
+
* `true`: refuse to build without `lock` (`ACCESS_LOCK_MISMATCH`), so a deploy that
|
|
107
|
+
* lost its `avAccess.lock.json` fails at startup instead of running unpinned files;
|
|
108
|
+
* typed rules are then held to the lock's policies hash too. Recommended in
|
|
109
|
+
* production.
|
|
110
|
+
*/
|
|
111
|
+
readonly requireLock?: boolean;
|
|
112
|
+
};
|
|
113
|
+
/** Where an engine's policies file comes from: a file handed over, or the project's typed rules. */
|
|
114
|
+
export type PoliciesSource = "file" | "rules";
|
|
115
|
+
/**
|
|
116
|
+
* What decided one `can` question: the grant that allowed it, or the deny that
|
|
117
|
+
* refused it; `null` when it was refused because no grant matched.
|
|
118
|
+
*/
|
|
119
|
+
export type DecidingRule = {
|
|
120
|
+
readonly kind: "grant" | "deny";
|
|
121
|
+
readonly id: string;
|
|
122
|
+
} | null;
|
|
123
|
+
/**
|
|
124
|
+
* Told of every `can` answer, after it is known and before `can` returns it: the
|
|
125
|
+
* audit log's hook into the engine. `can` waits for what it returns.
|
|
126
|
+
*/
|
|
127
|
+
export type DecisionObserver = (question: AccessCanQuestion, allowed: boolean, rule: DecidingRule) => void | Promise<void>;
|
|
128
|
+
/**
|
|
129
|
+
* Builds the engine from a policies file and, optionally, the org's guardrails. Both
|
|
130
|
+
* are checked first; a bad one throws, naming the rule. With `lock`, both are held
|
|
131
|
+
* to the hashes `avAccess.lock.json` pins (`ACCESS_LOCK_MISMATCH`).
|
|
132
|
+
*/
|
|
133
|
+
export declare const createAccessEngine: <Governed extends GovernedShape = GovernedShape>(policies: AccessPolicies<Governed> | AccessPoliciesJson<Governed>, options?: AccessEngineOptions) => AccessEngine<Governed>;
|
|
134
|
+
/**
|
|
135
|
+
* The engine, from a policies file that came from `source`: with a `lock`, a file
|
|
136
|
+
* handed over is held to the lock's pin, and typed rules (the project's own
|
|
137
|
+
* source) to its namespace only. `createAccess` adds its built-in rules over Better
|
|
138
|
+
* Auth's tables (`guard`): their denies join the file's, and `fieldsOf` never lists a
|
|
139
|
+
* secret column or a field a write may not set.
|
|
140
|
+
*/
|
|
141
|
+
export declare const engineOf: <Governed extends GovernedShape = GovernedShape>(policies: AccessPolicies<Governed>, options: AccessEngineOptions | undefined, source: PoliciesSource, observe?: DecisionObserver, guard?: AuthTablesGuard) => AccessEngine<Governed>;
|
|
142
|
+
export {};
|