@vantageos/cloud-identity 0.10.0 → 0.12.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.
@@ -0,0 +1,249 @@
1
+ /**
2
+ * The acting principal by ID, and the target checked by ID.
3
+ *
4
+ * An actor and a target are identified by their stored IDs, never by a name.
5
+ * A name is a display label: it never selects a row and never authorises one.
6
+ *
7
+ * Motivating incident: an agent of organisation A named "eta", calling by name
8
+ * through a service account, completed, updated and deleted the task of
9
+ * organisation B's agent also named "eta" at ten task doors, because every door
10
+ * admitted the caller by comparing names. Two different agents shared a label;
11
+ * nothing compared their IDs.
12
+ *
13
+ * Two functions close that class:
14
+ *
15
+ * - `resolveActingPrincipal(credential, lookups)` turns a VERIFIED credential
16
+ * into `{ principalId, orgId, kind }` read from stored rows, or refuses.
17
+ * - `assertTargetBelongsTo(principal, target, lookups, opts)` admits a target
18
+ * only when its stored organisation ID (and owner ID, where the door is
19
+ * owner-only) equals the principal's resolved IDs.
20
+ *
21
+ * The fleet is the OPERATOR ORGANISATION: the organisation the consumer's
22
+ * `orgKindOf` adapter reports as `"operator"`, read from stored data at run
23
+ * time. No organisation ID is reserved or spelled in this package; an
24
+ * adapter that answers anything else, or nothing, means "not the fleet".
25
+ *
26
+ * RULING 5: membership of the operator org is ORDINARY membership. Its agents
27
+ * and persons resolve as `kind: "agent"` / `"person"` of that org and reach its
28
+ * rows only. Cross-org reach (`kind: "fleet"` + `fleetCrossOrg`) belongs to the
29
+ * operator org's SERVICE ACCOUNT alone; `reserved-fleet-scope` refuses a claim
30
+ * of that scope by any other credential, never plain membership.
31
+ *
32
+ * Pure functions over injected lookups: no database, no framework, no network.
33
+ * The consumer performs each indexed read by ID and passes the function in;
34
+ * every failure (throwing lookup, miss, malformed row, inactive row, wrong
35
+ * organisation) is a typed `IdentityRefusal`, never a default principal.
36
+ *
37
+ * @security This module verifies NOTHING about the credential itself. The IDs
38
+ * in a credential must come from the consumer's verification of what the
39
+ * caller presented (a bearer row, a person's token, the service secret). A
40
+ * client-supplied argument passed as a credential becomes the principal.
41
+ */
42
+ import { z } from "zod";
43
+ import { type IdentityRefusal } from "./identity-refusal.js";
44
+ /**
45
+ * A verified credential, in the package's vocabulary. Every identity field is
46
+ * an ID. The schemas are STRICT: a credential carrying any other key (an
47
+ * `agentName`, a `callerOrchestrator`, a `name`) is refused whole, so a name
48
+ * cannot be smuggled in beside an ID.
49
+ *
50
+ * - `agent`: an agent's own bearer, verified by the consumer (for example by
51
+ * `validatePresentedBearer`): the agent ID and the organisation the bearer
52
+ * row is stamped with.
53
+ * - `person`: a person's token, verified by the consumer: the stored person
54
+ * ID (the verified subject) and the organisation the token is bound to.
55
+ * Optionally (0.12.0) the role the SAME token carries in that organisation
56
+ * (`verifiedOrgRole`, for example Clerk's `org_role`), read by
57
+ * `assertOrgAdmin`. Only a verified claim belongs here, never an argument.
58
+ * - `service`: the service account, verified by the consumer. When it acts
59
+ * for an agent it names that agent BY ID (`actingForAgentId`); the agent
60
+ * is resolved within the service account's own organisation.
61
+ */
62
+ export declare const actingCredentialSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
63
+ kind: z.ZodLiteral<"agent">;
64
+ agentId: z.ZodString;
65
+ verifiedOrgId: z.ZodString;
66
+ }, z.core.$strict>, z.ZodObject<{
67
+ kind: z.ZodLiteral<"person">;
68
+ personId: z.ZodString;
69
+ verifiedOrgId: z.ZodString;
70
+ verifiedOrgRole: z.ZodOptional<z.ZodString>;
71
+ }, z.core.$strict>, z.ZodObject<{
72
+ kind: z.ZodLiteral<"service">;
73
+ serviceAccountId: z.ZodString;
74
+ actingForAgentId: z.ZodOptional<z.ZodString>;
75
+ }, z.core.$strict>], "kind">;
76
+ export type ActingCredential = z.input<typeof actingCredentialSchema>;
77
+ /**
78
+ * Each row repeats its own ID, so a loose lookup (prefix match, wrong index)
79
+ * returning another row cannot become a principal. `active` is REQUIRED and
80
+ * must be `true`: an absent flag grants nothing. Extra fields (a display name)
81
+ * are allowed and ignored.
82
+ */
83
+ export declare const principalRowSchema: z.ZodObject<{
84
+ id: z.ZodString;
85
+ orgId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
86
+ active: z.ZodBoolean;
87
+ }, z.core.$strip>;
88
+ export type PrincipalRow = z.input<typeof principalRowSchema>;
89
+ export declare const organisationRowSchema: z.ZodObject<{
90
+ id: z.ZodString;
91
+ active: z.ZodBoolean;
92
+ }, z.core.$strip>;
93
+ export type OrganisationRow = z.input<typeof organisationRowSchema>;
94
+ /**
95
+ * The kind of a stored organisation, as the consumer's mapping row records it.
96
+ * `"operator"` is the fleet: the operator's own organisation, whose rows are
97
+ * the master scope. Any other answer, `null` included, is not the fleet.
98
+ */
99
+ export type OrgKind = "operator" | "client";
100
+ type Lookup<A extends unknown[], R> = (...args: A) => Promise<R | null | undefined> | R | null | undefined;
101
+ /** Indexed reads BY ID. Only the lookups of the presented path are called. */
102
+ export type PrincipalLookups = {
103
+ agentById?: Lookup<[agentId: string], PrincipalRow>;
104
+ /** A person's row IN one organisation (their membership there). */
105
+ personById?: Lookup<[personId: string, orgId: string], PrincipalRow>;
106
+ serviceAccountById?: Lookup<[serviceAccountId: string], PrincipalRow>;
107
+ /** The organisation row; the operator organisation is checked like any other. */
108
+ organisationById?: Lookup<[orgId: string], OrganisationRow>;
109
+ /**
110
+ * The stored kind of an organisation (for example `orgKind` on its mapping
111
+ * row). Decides, from data, whether an organisation is the operator org,
112
+ * that is the fleet. `null`, a miss, or any value other than exactly
113
+ * `"operator"` is NOT the fleet. A missing or throwing adapter refuses.
114
+ */
115
+ orgKindOf?: Lookup<[orgId: string], OrgKind>;
116
+ };
117
+ /** The one lookup `assertTargetBelongsTo` reads. */
118
+ export type OrgKindLookups = Pick<PrincipalLookups, "orgKindOf">;
119
+ export type ActingPrincipalKind = "agent" | "person" | "service" | "fleet";
120
+ export type ActingPrincipal = {
121
+ /** The stored ID of the actor: agent ID, person ID or service-account ID. */
122
+ principalId: string;
123
+ /** The stored organisation ID. The operator org's ID for `fleet`. */
124
+ orgId: string;
125
+ kind: ActingPrincipalKind;
126
+ /** Set when a service account acts for an agent: the service account's ID. */
127
+ viaServiceAccountId?: string;
128
+ /**
129
+ * `kind: "person"` only, and only when the credential carried
130
+ * `verifiedOrgRole`: the verified role in `orgId`. Read by `assertOrgAdmin`.
131
+ */
132
+ orgRole?: string;
133
+ };
134
+ export type ResolveActingPrincipalResult = {
135
+ ok: true;
136
+ principal: ActingPrincipal;
137
+ } | {
138
+ ok: false;
139
+ refusal: IdentityRefusal;
140
+ };
141
+ export type TargetIds = {
142
+ /** The target row's stored organisation ID. Absent: unstamped. */
143
+ orgId?: string | null;
144
+ /** The target row's stored owner ID (agent, person or service account). */
145
+ ownerId?: string | null;
146
+ };
147
+ export type AssertTargetOptions = {
148
+ /** The consumer's name for the door; carried on every refusal. */
149
+ door?: string;
150
+ /** Owner-only door: the target's `ownerId` must equal `principalId`. */
151
+ ownerOnly?: boolean;
152
+ /**
153
+ * A fleet principal may reach a row of a CLIENT organisation only when the
154
+ * door declares it (a master export), and only when `orgKindOf` reports that
155
+ * organisation as `"client"`. Default `false`: refused.
156
+ */
157
+ fleetCrossOrg?: boolean;
158
+ };
159
+ export type AssertTargetResult = {
160
+ ok: true;
161
+ } | {
162
+ ok: false;
163
+ refusal: IdentityRefusal;
164
+ };
165
+ /**
166
+ * Resolves the acting principal from a VERIFIED credential to stored IDs, or
167
+ * refuses. Refusal by default: a credential claiming the fleet kind
168
+ * (`reserved-fleet-scope`), a credential that does not parse (including one
169
+ * carrying any name field), a missing or throwing lookup, a miss, a malformed
170
+ * or inactive row, an unstamped row and an inactive organisation are all
171
+ * refused. Nothing is ever resolved by a name, and nothing falls back to a
172
+ * master principal.
173
+ *
174
+ * - agent: the agent row by `agentId`; its stored org must equal the org
175
+ * the credential was verified for. An agent of the operator org resolves
176
+ * as an ordinary `kind: "agent"` of that org (RULING 5).
177
+ * - person: the person row by (`personId`, `verifiedOrgId`); it must be
178
+ * stamped with that org. A person of the operator org resolves as an
179
+ * ordinary `kind: "person"` of that org (RULING 5).
180
+ * - service: the service-account row by `serviceAccountId`. Alone, it acts
181
+ * as itself (`kind: "service"`, or `kind: "fleet"` when `orgKindOf` reports
182
+ * its stored org as `"operator"`). With `actingForAgentId`, the agent row is read by
183
+ * ID and must be stamped with the service account's own org; the principal
184
+ * is that agent, with `viaServiceAccountId` recording the carrier.
185
+ */
186
+ export declare function resolveActingPrincipal(credential: ActingCredential | null | undefined, lookups: PrincipalLookups, door?: string): Promise<ResolveActingPrincipalResult>;
187
+ /**
188
+ * Admits a target only by comparing its STORED IDs with the principal's
189
+ * resolved IDs, byte for byte. No name field is read; there is none to read.
190
+ *
191
+ * Whether an organisation is the operator org (the fleet) is read from
192
+ * `lookups.orgKindOf`; a missing or throwing adapter refuses.
193
+ *
194
+ * - An absent principal, or one with an empty ID, is refused.
195
+ * - A `kind: "fleet"` principal outside the operator org is refused, and so
196
+ * is a `kind: "service"` principal inside it (the operator org's service
197
+ * account is the fleet). An agent or person of the operator org is an
198
+ * ordinary member of it (RULING 5).
199
+ * - An unstamped target (no `orgId`) is refused to EVERY principal, the
200
+ * fleet principal included. No right is inferred from an absence: a fleet
201
+ * row carries the operator org's ID explicitly, and a row with no
202
+ * organisation is never treated as a master row.
203
+ * - An operator-org row is refused to every client principal.
204
+ * - Otherwise the target's `orgId` must equal the principal's. A fleet
205
+ * principal reaches a client organisation's row only when the door sets
206
+ * `fleetCrossOrg` (a master export) AND `orgKindOf` reports that
207
+ * organisation as `"client"`; an unknown organisation is refused. Any
208
+ * other principal reaching across organisations under `fleetCrossOrg`
209
+ * claims the fleet scope and is refused `reserved-fleet-scope`.
210
+ * - `ownerOnly`: the target's `ownerId` must also equal `principalId`; an
211
+ * absent owner is refused.
212
+ */
213
+ export declare function assertTargetBelongsTo(principal: ActingPrincipal | null | undefined, target: TargetIds | null | undefined, lookups: OrgKindLookups, opts?: AssertTargetOptions): Promise<AssertTargetResult>;
214
+ export type AssertOrgAdminOptions = {
215
+ /**
216
+ * The role values that count as organisation admin, exactly as the verified
217
+ * claim spells them (for Clerk: `["org:admin"]`). Data supplied by the
218
+ * consumer; an empty or malformed list admits nobody.
219
+ */
220
+ adminRoles: readonly string[];
221
+ /** The consumer's name for the door; carried on every refusal. */
222
+ door?: string;
223
+ };
224
+ export type AssertOrgAdminResult = {
225
+ ok: true;
226
+ } | {
227
+ ok: false;
228
+ refusal: IdentityRefusal;
229
+ };
230
+ /**
231
+ * Admits a principal as ADMIN of the target organisation, deciding by ID only.
232
+ * The principal must come from `resolveActingPrincipal`; the role is the
233
+ * verified claim its person credential carried (`verifiedOrgRole`), so no host
234
+ * lookup is needed and none is read here.
235
+ *
236
+ * - An absent principal, or one with an empty ID, is refused.
237
+ * - Only a PERSON is an organisation admin. An agent, a service account and
238
+ * the fleet principal are refused `principal-not-a-person`, whatever they
239
+ * carry: the service account keeps its own rules and never becomes an
240
+ * admin through this check.
241
+ * - The target organisation must be present and byte-equal to the
242
+ * principal's stored organisation: an admin of A is not an admin of B.
243
+ * - The role must be byte-equal to one entry of `adminRoles`. An absent
244
+ * role, an unknown role, and an empty or malformed `adminRoles` refuse.
245
+ * No case folding, no prefix stripping, no default role.
246
+ */
247
+ export declare function assertOrgAdmin(principal: ActingPrincipal | null | undefined, targetOrgId: string | null | undefined, opts: AssertOrgAdminOptions): AssertOrgAdminResult;
248
+ export {};
249
+ //# sourceMappingURL=principal-by-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principal-by-id.d.ts","sourceRoot":"","sources":["../src/principal-by-id.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,388 @@
1
+ /**
2
+ * The acting principal by ID, and the target checked by ID.
3
+ *
4
+ * An actor and a target are identified by their stored IDs, never by a name.
5
+ * A name is a display label: it never selects a row and never authorises one.
6
+ *
7
+ * Motivating incident: an agent of organisation A named "eta", calling by name
8
+ * through a service account, completed, updated and deleted the task of
9
+ * organisation B's agent also named "eta" at ten task doors, because every door
10
+ * admitted the caller by comparing names. Two different agents shared a label;
11
+ * nothing compared their IDs.
12
+ *
13
+ * Two functions close that class:
14
+ *
15
+ * - `resolveActingPrincipal(credential, lookups)` turns a VERIFIED credential
16
+ * into `{ principalId, orgId, kind }` read from stored rows, or refuses.
17
+ * - `assertTargetBelongsTo(principal, target, lookups, opts)` admits a target
18
+ * only when its stored organisation ID (and owner ID, where the door is
19
+ * owner-only) equals the principal's resolved IDs.
20
+ *
21
+ * The fleet is the OPERATOR ORGANISATION: the organisation the consumer's
22
+ * `orgKindOf` adapter reports as `"operator"`, read from stored data at run
23
+ * time. No organisation ID is reserved or spelled in this package; an
24
+ * adapter that answers anything else, or nothing, means "not the fleet".
25
+ *
26
+ * RULING 5: membership of the operator org is ORDINARY membership. Its agents
27
+ * and persons resolve as `kind: "agent"` / `"person"` of that org and reach its
28
+ * rows only. Cross-org reach (`kind: "fleet"` + `fleetCrossOrg`) belongs to the
29
+ * operator org's SERVICE ACCOUNT alone; `reserved-fleet-scope` refuses a claim
30
+ * of that scope by any other credential, never plain membership.
31
+ *
32
+ * Pure functions over injected lookups: no database, no framework, no network.
33
+ * The consumer performs each indexed read by ID and passes the function in;
34
+ * every failure (throwing lookup, miss, malformed row, inactive row, wrong
35
+ * organisation) is a typed `IdentityRefusal`, never a default principal.
36
+ *
37
+ * @security This module verifies NOTHING about the credential itself. The IDs
38
+ * in a credential must come from the consumer's verification of what the
39
+ * caller presented (a bearer row, a person's token, the service secret). A
40
+ * client-supplied argument passed as a credential becomes the principal.
41
+ */
42
+ import { z } from "zod";
43
+ import { refusal } from "./identity-refusal.js";
44
+ // ---------------------------------------------------------------------------
45
+ // The credential: verified IDs only, no name field anywhere
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * A verified credential, in the package's vocabulary. Every identity field is
49
+ * an ID. The schemas are STRICT: a credential carrying any other key (an
50
+ * `agentName`, a `callerOrchestrator`, a `name`) is refused whole, so a name
51
+ * cannot be smuggled in beside an ID.
52
+ *
53
+ * - `agent`: an agent's own bearer, verified by the consumer (for example by
54
+ * `validatePresentedBearer`): the agent ID and the organisation the bearer
55
+ * row is stamped with.
56
+ * - `person`: a person's token, verified by the consumer: the stored person
57
+ * ID (the verified subject) and the organisation the token is bound to.
58
+ * Optionally (0.12.0) the role the SAME token carries in that organisation
59
+ * (`verifiedOrgRole`, for example Clerk's `org_role`), read by
60
+ * `assertOrgAdmin`. Only a verified claim belongs here, never an argument.
61
+ * - `service`: the service account, verified by the consumer. When it acts
62
+ * for an agent it names that agent BY ID (`actingForAgentId`); the agent
63
+ * is resolved within the service account's own organisation.
64
+ */
65
+ export const actingCredentialSchema = z.discriminatedUnion("kind", [
66
+ z.strictObject({
67
+ kind: z.literal("agent"),
68
+ agentId: z.string().min(1),
69
+ verifiedOrgId: z.string().min(1),
70
+ }),
71
+ z.strictObject({
72
+ kind: z.literal("person"),
73
+ personId: z.string().min(1),
74
+ verifiedOrgId: z.string().min(1),
75
+ verifiedOrgRole: z.string().min(1).optional(),
76
+ }),
77
+ z.strictObject({
78
+ kind: z.literal("service"),
79
+ serviceAccountId: z.string().min(1),
80
+ actingForAgentId: z.string().min(1).optional(),
81
+ }),
82
+ ]);
83
+ // ---------------------------------------------------------------------------
84
+ // The stored rows the consumer's lookups return
85
+ // ---------------------------------------------------------------------------
86
+ /**
87
+ * Each row repeats its own ID, so a loose lookup (prefix match, wrong index)
88
+ * returning another row cannot become a principal. `active` is REQUIRED and
89
+ * must be `true`: an absent flag grants nothing. Extra fields (a display name)
90
+ * are allowed and ignored.
91
+ */
92
+ export const principalRowSchema = z.object({
93
+ id: z.string().min(1),
94
+ orgId: z.string().min(1).nullish(),
95
+ active: z.boolean(),
96
+ });
97
+ export const organisationRowSchema = z.object({
98
+ id: z.string().min(1),
99
+ active: z.boolean(),
100
+ });
101
+ function nonEmpty(value) {
102
+ return typeof value === "string" && value.length > 0;
103
+ }
104
+ function denied(reason, door, detail) {
105
+ return { ok: false, refusal: refusal("RBAC_DENIED", reason, door, detail) };
106
+ }
107
+ /**
108
+ * Reads one principal row BY ID and checks it: the lookup is supplied and does
109
+ * not throw, the row exists, is well formed, repeats the ID asked for, and is
110
+ * active. The lookup's own error text is never surfaced.
111
+ */
112
+ async function loadPrincipalRow(lookup, args, expectedId, what, door) {
113
+ if (typeof lookup !== "function") {
114
+ return denied("principal-lookup-failed", door, `No lookup was supplied for the ${what}.`);
115
+ }
116
+ let raw;
117
+ try {
118
+ raw = await lookup(...args);
119
+ }
120
+ catch {
121
+ return denied("principal-lookup-failed", door, `The ${what} could not be read, so none is resolved.`);
122
+ }
123
+ if (raw === null || raw === undefined) {
124
+ return denied("principal-not-found", door, `No ${what} has this ID.`);
125
+ }
126
+ const parsed = principalRowSchema.safeParse(raw);
127
+ if (!parsed.success || parsed.data.id !== expectedId) {
128
+ return denied("principal-record-invalid", door, `The ${what} record is not well formed.`);
129
+ }
130
+ if (parsed.data.active !== true) {
131
+ return denied("principal-inactive", door, `The ${what} is not active.`);
132
+ }
133
+ return { ok: true, row: parsed.data };
134
+ }
135
+ /**
136
+ * Reads the stored kind of an organisation. Refusal by default: a missing or
137
+ * throwing adapter refuses; a miss or any answer other than exactly
138
+ * `"operator"` / `"client"` is `null`, which is never the fleet.
139
+ */
140
+ async function loadOrgKind(lookups, orgId, door) {
141
+ const lookup = lookups?.orgKindOf;
142
+ if (typeof lookup !== "function") {
143
+ return denied("principal-lookup-failed", door, "No lookup was supplied for the organisation kind.");
144
+ }
145
+ let raw;
146
+ try {
147
+ raw = await lookup(orgId);
148
+ }
149
+ catch {
150
+ return denied("principal-lookup-failed", door, "The organisation kind could not be read, so none is resolved.");
151
+ }
152
+ return { ok: true, kind: raw === "operator" || raw === "client" ? raw : null };
153
+ }
154
+ /** Every organisation, the operator org included, must exist and be active. */
155
+ async function checkOrganisation(orgId, lookups, door) {
156
+ const lookup = lookups.organisationById;
157
+ if (typeof lookup !== "function") {
158
+ return denied("principal-lookup-failed", door, "No lookup was supplied for the organisation.");
159
+ }
160
+ let raw;
161
+ try {
162
+ raw = await lookup(orgId);
163
+ }
164
+ catch {
165
+ return denied("principal-lookup-failed", door, "The organisation could not be read, so none is resolved.");
166
+ }
167
+ const parsed = organisationRowSchema.safeParse(raw);
168
+ if (!parsed.success || parsed.data.id !== orgId || parsed.data.active !== true) {
169
+ return denied("organisation-not-active", door, "The organisation is not mapped or not active.");
170
+ }
171
+ return null;
172
+ }
173
+ function served(principal) {
174
+ return { ok: true, principal };
175
+ }
176
+ /**
177
+ * Resolves the acting principal from a VERIFIED credential to stored IDs, or
178
+ * refuses. Refusal by default: a credential claiming the fleet kind
179
+ * (`reserved-fleet-scope`), a credential that does not parse (including one
180
+ * carrying any name field), a missing or throwing lookup, a miss, a malformed
181
+ * or inactive row, an unstamped row and an inactive organisation are all
182
+ * refused. Nothing is ever resolved by a name, and nothing falls back to a
183
+ * master principal.
184
+ *
185
+ * - agent: the agent row by `agentId`; its stored org must equal the org
186
+ * the credential was verified for. An agent of the operator org resolves
187
+ * as an ordinary `kind: "agent"` of that org (RULING 5).
188
+ * - person: the person row by (`personId`, `verifiedOrgId`); it must be
189
+ * stamped with that org. A person of the operator org resolves as an
190
+ * ordinary `kind: "person"` of that org (RULING 5).
191
+ * - service: the service-account row by `serviceAccountId`. Alone, it acts
192
+ * as itself (`kind: "service"`, or `kind: "fleet"` when `orgKindOf` reports
193
+ * its stored org as `"operator"`). With `actingForAgentId`, the agent row is read by
194
+ * ID and must be stamped with the service account's own org; the principal
195
+ * is that agent, with `viaServiceAccountId` recording the carrier.
196
+ */
197
+ export async function resolveActingPrincipal(credential, lookups, door = "resolveActingPrincipal") {
198
+ if (credential !== null &&
199
+ typeof credential === "object" &&
200
+ credential.kind === "fleet") {
201
+ return denied("reserved-fleet-scope", door, "The fleet scope is held only by the operator organisation's service account; no credential claims it.");
202
+ }
203
+ const parsed = actingCredentialSchema.safeParse(credential);
204
+ if (!parsed.success) {
205
+ return denied("credential-invalid", door, "The credential does not identify a principal by ID.");
206
+ }
207
+ const cred = parsed.data;
208
+ const look = lookups ?? {};
209
+ if (cred.kind === "agent" || cred.kind === "person") {
210
+ // The org's kind is read so a missing or throwing adapter refuses here as
211
+ // on every path; an operator-org member is NOT refused (RULING 5).
212
+ const credKind = await loadOrgKind(look, cred.verifiedOrgId, door);
213
+ if (!credKind.ok)
214
+ return credKind;
215
+ const loaded = cred.kind === "agent"
216
+ ? await loadPrincipalRow(look.agentById, [cred.agentId], cred.agentId, "agent", door)
217
+ : await loadPrincipalRow(look.personById, [cred.personId, cred.verifiedOrgId], cred.personId, "person", door);
218
+ if (!loaded.ok)
219
+ return loaded;
220
+ const rowOrg = loaded.row.orgId;
221
+ if (!nonEmpty(rowOrg)) {
222
+ return denied("no-verified-organisation", door, `The ${cred.kind} is stamped with no organisation.`);
223
+ }
224
+ if (rowOrg !== cred.verifiedOrgId) {
225
+ return denied("other-organisation", door, `The ${cred.kind} belongs to another organisation than its credential.`);
226
+ }
227
+ const orgRefusal = await checkOrganisation(rowOrg, look, door);
228
+ if (orgRefusal !== null)
229
+ return orgRefusal;
230
+ if (cred.kind === "person" && cred.verifiedOrgRole !== undefined) {
231
+ return served({
232
+ principalId: loaded.row.id,
233
+ orgId: rowOrg,
234
+ kind: cred.kind,
235
+ orgRole: cred.verifiedOrgRole,
236
+ });
237
+ }
238
+ return served({ principalId: loaded.row.id, orgId: rowOrg, kind: cred.kind });
239
+ }
240
+ // service account
241
+ const svc = await loadPrincipalRow(look.serviceAccountById, [cred.serviceAccountId], cred.serviceAccountId, "service account", door);
242
+ if (!svc.ok)
243
+ return svc;
244
+ const svcOrg = svc.row.orgId;
245
+ if (!nonEmpty(svcOrg)) {
246
+ return denied("no-verified-organisation", door, "The service account is stamped with no organisation.");
247
+ }
248
+ const svcKind = await loadOrgKind(look, svcOrg, door);
249
+ if (!svcKind.ok)
250
+ return svcKind;
251
+ const orgRefusal = await checkOrganisation(svcOrg, look, door);
252
+ if (orgRefusal !== null)
253
+ return orgRefusal;
254
+ if (cred.actingForAgentId === undefined) {
255
+ return served({
256
+ principalId: svc.row.id,
257
+ orgId: svcOrg,
258
+ kind: svcKind.kind === "operator" ? "fleet" : "service",
259
+ });
260
+ }
261
+ const agent = await loadPrincipalRow(look.agentById, [cred.actingForAgentId], cred.actingForAgentId, "agent", door);
262
+ if (!agent.ok)
263
+ return agent;
264
+ if (agent.row.orgId !== svcOrg) {
265
+ return denied("acting-agent-other-organisation", door, "The service account may act only for an agent of its own organisation.");
266
+ }
267
+ return served({
268
+ principalId: agent.row.id,
269
+ orgId: svcOrg,
270
+ kind: "agent",
271
+ viaServiceAccountId: svc.row.id,
272
+ });
273
+ }
274
+ // ---------------------------------------------------------------------------
275
+ // assertTargetBelongsTo
276
+ // ---------------------------------------------------------------------------
277
+ /**
278
+ * Admits a target only by comparing its STORED IDs with the principal's
279
+ * resolved IDs, byte for byte. No name field is read; there is none to read.
280
+ *
281
+ * Whether an organisation is the operator org (the fleet) is read from
282
+ * `lookups.orgKindOf`; a missing or throwing adapter refuses.
283
+ *
284
+ * - An absent principal, or one with an empty ID, is refused.
285
+ * - A `kind: "fleet"` principal outside the operator org is refused, and so
286
+ * is a `kind: "service"` principal inside it (the operator org's service
287
+ * account is the fleet). An agent or person of the operator org is an
288
+ * ordinary member of it (RULING 5).
289
+ * - An unstamped target (no `orgId`) is refused to EVERY principal, the
290
+ * fleet principal included. No right is inferred from an absence: a fleet
291
+ * row carries the operator org's ID explicitly, and a row with no
292
+ * organisation is never treated as a master row.
293
+ * - An operator-org row is refused to every client principal.
294
+ * - Otherwise the target's `orgId` must equal the principal's. A fleet
295
+ * principal reaches a client organisation's row only when the door sets
296
+ * `fleetCrossOrg` (a master export) AND `orgKindOf` reports that
297
+ * organisation as `"client"`; an unknown organisation is refused. Any
298
+ * other principal reaching across organisations under `fleetCrossOrg`
299
+ * claims the fleet scope and is refused `reserved-fleet-scope`.
300
+ * - `ownerOnly`: the target's `ownerId` must also equal `principalId`; an
301
+ * absent owner is refused.
302
+ */
303
+ export async function assertTargetBelongsTo(principal, target, lookups, opts = {}) {
304
+ const door = opts.door ?? "assertTargetBelongsTo";
305
+ if (principal === null ||
306
+ typeof principal !== "object" ||
307
+ !nonEmpty(principal.principalId) ||
308
+ !nonEmpty(principal.orgId)) {
309
+ return denied("credential-invalid", door, "No resolved principal stands behind this call.");
310
+ }
311
+ const principalKind = await loadOrgKind(lookups, principal.orgId, door);
312
+ if (!principalKind.ok)
313
+ return principalKind;
314
+ const inFleet = principalKind.kind === "operator";
315
+ const isFleet = principal.kind === "fleet";
316
+ if ((isFleet && !inFleet) || (inFleet && principal.kind === "service")) {
317
+ return denied("reserved-fleet-scope", door, "The principal does not hold the fleet scope.");
318
+ }
319
+ const targetOrg = target?.orgId;
320
+ if (!nonEmpty(targetOrg)) {
321
+ return denied("target-unstamped", door, "The target carries no organisation, so it is not reachable.");
322
+ }
323
+ if (targetOrg !== principal.orgId) {
324
+ const targetKind = await loadOrgKind(lookups, targetOrg, door);
325
+ if (!targetKind.ok)
326
+ return targetKind;
327
+ if (targetKind.kind === "operator") {
328
+ return denied("reserved-fleet-scope", door, "The target is in the fleet scope.");
329
+ }
330
+ if (!isFleet && opts.fleetCrossOrg === true) {
331
+ return denied("reserved-fleet-scope", door, "Cross-organisation reach is held only by the operator organisation's service account.");
332
+ }
333
+ const masterExport = isFleet && opts.fleetCrossOrg === true && targetKind.kind === "client";
334
+ if (!masterExport) {
335
+ return denied("target-other-organisation", door, "The target belongs to another organisation.");
336
+ }
337
+ }
338
+ if (opts.ownerOnly === true) {
339
+ const owner = target?.ownerId;
340
+ if (!nonEmpty(owner) || owner !== principal.principalId) {
341
+ return denied("target-owner-mismatch", door, "Only the target's owner may act on it.");
342
+ }
343
+ }
344
+ return { ok: true };
345
+ }
346
+ /**
347
+ * Admits a principal as ADMIN of the target organisation, deciding by ID only.
348
+ * The principal must come from `resolveActingPrincipal`; the role is the
349
+ * verified claim its person credential carried (`verifiedOrgRole`), so no host
350
+ * lookup is needed and none is read here.
351
+ *
352
+ * - An absent principal, or one with an empty ID, is refused.
353
+ * - Only a PERSON is an organisation admin. An agent, a service account and
354
+ * the fleet principal are refused `principal-not-a-person`, whatever they
355
+ * carry: the service account keeps its own rules and never becomes an
356
+ * admin through this check.
357
+ * - The target organisation must be present and byte-equal to the
358
+ * principal's stored organisation: an admin of A is not an admin of B.
359
+ * - The role must be byte-equal to one entry of `adminRoles`. An absent
360
+ * role, an unknown role, and an empty or malformed `adminRoles` refuse.
361
+ * No case folding, no prefix stripping, no default role.
362
+ */
363
+ export function assertOrgAdmin(principal, targetOrgId, opts) {
364
+ const door = opts?.door ?? "assertOrgAdmin";
365
+ if (principal === null ||
366
+ typeof principal !== "object" ||
367
+ !nonEmpty(principal.principalId) ||
368
+ !nonEmpty(principal.orgId)) {
369
+ return denied("credential-invalid", door, "No resolved principal stands behind this call.");
370
+ }
371
+ if (principal.kind !== "person") {
372
+ return denied("principal-not-a-person", door, "Only a person is an organisation admin; this principal is not a person.");
373
+ }
374
+ if (!nonEmpty(targetOrgId)) {
375
+ return denied("target-unstamped", door, "No target organisation was named, so none is administered.");
376
+ }
377
+ if (targetOrgId !== principal.orgId) {
378
+ return denied("target-other-organisation", door, "The target organisation is not the principal's organisation.");
379
+ }
380
+ const adminRoles = opts?.adminRoles;
381
+ const rolesValid = Array.isArray(adminRoles) && adminRoles.length > 0 && adminRoles.every((r) => nonEmpty(r));
382
+ const role = principal.orgRole;
383
+ if (!rolesValid || !nonEmpty(role) || !adminRoles.includes(role)) {
384
+ return denied("role-not-admin", door, "The principal's role is not an organisation-admin role.");
385
+ }
386
+ return { ok: true };
387
+ }
388
+ //# sourceMappingURL=principal-by-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principal-by-id.js","sourceRoot":"","sources":["../src/principal-by-id.ts"],"names":[],"mappings":"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"}