@vantageos/cloud-identity 0.9.1 → 0.11.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 +110 -0
- package/README.md +254 -1
- package/dist/identity-refusal.d.ts +32 -0
- package/dist/identity-refusal.d.ts.map +1 -0
- package/dist/identity-refusal.js +28 -0
- package/dist/identity-refusal.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/namespace-write.d.ts +20 -0
- package/dist/namespace-write.d.ts.map +1 -0
- package/dist/namespace-write.js +40 -0
- package/dist/namespace-write.js.map +1 -0
- package/dist/person-principal.d.ts +7 -0
- package/dist/person-principal.d.ts.map +1 -1
- package/dist/person-principal.js +7 -0
- package/dist/person-principal.js.map +1 -1
- package/dist/principal-by-id.d.ts +206 -0
- package/dist/principal-by-id.d.ts.map +1 -0
- package/dist/principal-by-id.js +334 -0
- package/dist/principal-by-id.js.map +1 -0
- package/dist/role-policy.d.ts +72 -0
- package/dist/role-policy.d.ts.map +1 -0
- package/dist/role-policy.js +101 -0
- package/dist/role-policy.js.map +1 -0
- package/dist/scope-filter.d.ts +26 -0
- package/dist/scope-filter.d.ts.map +1 -1
- package/dist/scope-filter.js +29 -3
- package/dist/scope-filter.js.map +1 -1
- package/dist/secret-compare-sync.d.ts +29 -0
- package/dist/secret-compare-sync.d.ts.map +1 -0
- package/dist/secret-compare-sync.js +64 -0
- package/dist/secret-compare-sync.js.map +1 -0
- package/dist/tenant-membership.d.ts +85 -0
- package/dist/tenant-membership.d.ts.map +1 -0
- package/dist/tenant-membership.js +113 -0
- package/dist/tenant-membership.js.map +1 -0
- package/package.json +25 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,116 @@
|
|
|
3
3
|
All notable changes to `@vantageos/cloud-identity` are documented here.
|
|
4
4
|
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
5
5
|
|
|
6
|
+
## [0.11.0]
|
|
7
|
+
|
|
8
|
+
**Additive — the acting principal by ID, and the target checked by ID
|
|
9
|
+
(`./principal-by-id`).** An actor and a target are identified by their stored
|
|
10
|
+
IDs; a name is a display label and never selects or authorises a row. Motivating
|
|
11
|
+
incident: an agent of one organisation, calling by name through the service
|
|
12
|
+
account, landed on another organisation's same-named agent's task at ten task
|
|
13
|
+
doors, because admission compared names. No existing function changes behaviour.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `resolveActingPrincipal(credential, lookups, door?)`: a verified credential
|
|
18
|
+
(agent bearer, person token, or the service account acting as itself or for
|
|
19
|
+
an agent named BY ID within its own organisation) to `{ principalId, orgId,
|
|
20
|
+
kind, viaServiceAccountId? }` read from stored rows, or a typed refusal.
|
|
21
|
+
Strict credential schemas: a credential carrying a name field is refused
|
|
22
|
+
whole. Missing or throwing lookups, misses, malformed, inactive, unstamped
|
|
23
|
+
and wrong-organisation rows, and inactive organisations all refuse.
|
|
24
|
+
- `assertTargetBelongsTo(principal, target, lookups, opts?)` (async):
|
|
25
|
+
compares the target's stored `orgId` (and `ownerId` with `ownerOnly`) with
|
|
26
|
+
the resolved IDs. An unstamped target is refused to every principal, the
|
|
27
|
+
fleet principal included: no right is inferred from an absence, and a fleet
|
|
28
|
+
row carries the operator organisation's ID explicitly.
|
|
29
|
+
- The fleet is the OPERATOR ORGANISATION, decided from data by the adapter
|
|
30
|
+
method `orgKindOf(orgId) => "operator" | "client" | null` on
|
|
31
|
+
`PrincipalLookups` (and `OrgKindLookups` for `assertTargetBelongsTo`). `null`,
|
|
32
|
+
a miss or any other answer is not the fleet; a missing or throwing adapter
|
|
33
|
+
refuses. An operator-organisation row is refused to every client; the
|
|
34
|
+
fleet principal reaches a client organisation's row only with
|
|
35
|
+
`fleetCrossOrg: true` and only when the adapter reports it as `"client"`.
|
|
36
|
+
- RULING 5: membership of the operator organisation is ordinary membership.
|
|
37
|
+
Its agents and persons resolve as `kind: "agent"` / `"person"` of that
|
|
38
|
+
organisation and reach its rows only; cross-organisation reach (`kind:
|
|
39
|
+
"fleet"` + `fleetCrossOrg`) belongs to its service account alone, and
|
|
40
|
+
`reserved-fleet-scope` refuses a claim of that scope (a `kind: "fleet"`
|
|
41
|
+
credential, or `fleetCrossOrg` used by any other principal), never plain
|
|
42
|
+
membership.
|
|
43
|
+
The operator organisation is checked by `organisationById` like any other.
|
|
44
|
+
- `FLEET_SCOPE_ORG_ID` (a reserved `"vantageos:fleet"` constant, merged on
|
|
45
|
+
`main` but never published) is REMOVED before release, with no alias (RULING
|
|
46
|
+
4). A row stamped with that literal is an unknown organisation and is refused
|
|
47
|
+
to every caller.
|
|
48
|
+
- `actingCredentialSchema`, `principalRowSchema`, `organisationRowSchema` and
|
|
49
|
+
the types `ActingCredential`, `PrincipalRow`, `OrganisationRow`,
|
|
50
|
+
`PrincipalLookups`, `OrgKind`, `OrgKindLookups`, `ActingPrincipalKind`,
|
|
51
|
+
`ActingPrincipal`,
|
|
52
|
+
`ResolveActingPrincipalResult`, `TargetIds`, `AssertTargetOptions`,
|
|
53
|
+
`AssertTargetResult`.
|
|
54
|
+
- New `IdentityRefusalReason` values: `credential-invalid`,
|
|
55
|
+
`principal-lookup-failed`, `principal-not-found`, `principal-record-invalid`,
|
|
56
|
+
`principal-inactive`, `organisation-not-active`, `reserved-fleet-scope`,
|
|
57
|
+
`acting-agent-other-organisation`, `target-unstamped`,
|
|
58
|
+
`target-other-organisation`, `target-owner-mismatch`.
|
|
59
|
+
- New subpath export `./principal-by-id`.
|
|
60
|
+
- A test that reads the exported signatures with the TypeScript checker and
|
|
61
|
+
fails if any identity input of this module is spelled like a name.
|
|
62
|
+
|
|
63
|
+
### Deprecated
|
|
64
|
+
|
|
65
|
+
- `resolvePersonActingName` (0.9.0). It takes names (`claimedName`,
|
|
66
|
+
`agentCredential.agentName`) as identity input; backend standard R-53
|
|
67
|
+
clause 4 makes it non-conforming for any new door. Use
|
|
68
|
+
`resolveActingPrincipal` and `assertTargetBelongsTo`. Behaviour unchanged;
|
|
69
|
+
removal is a major change, made only after consumers migrate off it.
|
|
70
|
+
- `passesScopeFilter`, `scopeFilterGet` and `scopeFilterList` (the last two
|
|
71
|
+
through the first). A `createdBy` name matched against `fromAllowList`
|
|
72
|
+
selects the row; backend standard R-53 clause 1 forbids it. Use
|
|
73
|
+
`resolveActingPrincipal` and `assertTargetBelongsTo`. Behaviour unchanged;
|
|
74
|
+
they stay exported until consumers migrate off them.
|
|
75
|
+
- The name-input test now covers every callable export of the package root: a
|
|
76
|
+
new export taking a name-like identity input fails. The deprecated legacy
|
|
77
|
+
exceptions are exactly `resolvePersonActingName`, `passesScopeFilter`,
|
|
78
|
+
`scopeFilterGet` and `scopeFilterList`, and the test fails if any of them
|
|
79
|
+
loses its `@deprecated Since 0.11.0` tag. The scan reads a generic input
|
|
80
|
+
through its constraint, so a name field behind `T extends ScopeFilterable`
|
|
81
|
+
is seen. Two 0.10.0 exports that carry a name-like field without selecting a
|
|
82
|
+
row by it (`isPersonActorName`: `name`; `checkPersonCallShape`:
|
|
83
|
+
`actingName`, accepted only to be refused) are pinned in a frozen map that
|
|
84
|
+
can only shrink.
|
|
85
|
+
|
|
86
|
+
### Migration (required consumer step)
|
|
87
|
+
|
|
88
|
+
- Before switching a door to `assertTargetBelongsTo`, stamp every existing row
|
|
89
|
+
that has no `orgId` with its real organisation ID or with the operator
|
|
90
|
+
organisation's ID, and supply `orgKindOf` reading the stored `orgKind`. Unstamped rows are refused to every caller, so a door
|
|
91
|
+
switched before the backfill stops serving them.
|
|
92
|
+
|
|
93
|
+
## [0.10.0]
|
|
94
|
+
|
|
95
|
+
**Additive — identity gaps a consumer had are now in the package.** Every
|
|
96
|
+
consumer adapts these; none copies them. No existing function changes behaviour.
|
|
97
|
+
|
|
98
|
+
### Added
|
|
99
|
+
|
|
100
|
+
- `resolveMinRole` / `assertMinRole`: minimum-role check over an order the
|
|
101
|
+
caller supplies as data. Unknown role, unknown minimum, invalid order refuse.
|
|
102
|
+
- `mapRoleClaim`: claim to role through caller data, with a fallback that must
|
|
103
|
+
be explicit. Absent or unmapped claim without a fallback refuses.
|
|
104
|
+
- `resolveMembership` (injected async lookup; error, miss, inactive, malformed
|
|
105
|
+
and other-organisation all refuse), `isRowInTenant` (strict equality,
|
|
106
|
+
unstamped row refused), `membershipRecordSchema`.
|
|
107
|
+
- `requireOrgAdmin`: the proof is bound to the caller's verified organisation.
|
|
108
|
+
- `assertNamespaceWrite` and the shared boundary rule `namespaceMatchesPrefix`
|
|
109
|
+
(now also used by the read filter, same behaviour).
|
|
110
|
+
- `timingSafeEqualSync` / `assertSecretSync`: synchronous constant-time
|
|
111
|
+
comparison; an unset expected secret matches nothing.
|
|
112
|
+
- `IdentityRefusalError` and the `IdentityRefusal` types.
|
|
113
|
+
- New subpath exports `./identity-refusal`, `./role-policy`,
|
|
114
|
+
`./tenant-membership`, `./namespace-write`, `./secret-compare-sync`.
|
|
115
|
+
|
|
6
116
|
## [0.9.1]
|
|
7
117
|
|
|
8
118
|
### Fixed
|
package/README.md
CHANGED
|
@@ -174,13 +174,24 @@ await resolveTenant({ session: null }); // throws Error("Unauthenticated: no ses
|
|
|
174
174
|
**Row filtering**
|
|
175
175
|
|
|
176
176
|
- `passesScopeFilter(ctx, row)` — true when this row is visible to this
|
|
177
|
-
caller.
|
|
177
|
+
caller. **Deprecated since 0.11.0** (see below).
|
|
178
178
|
- `scopeFilterList(ctx, rows)` — the subset of rows the caller may see.
|
|
179
|
+
**Deprecated since 0.11.0** (see below).
|
|
179
180
|
- `scopeFilterGet(ctx, row)` — the row, or `null` if the caller may not see it.
|
|
181
|
+
**Deprecated since 0.11.0** (see below).
|
|
180
182
|
- `isMasterScope(ctx)` / `isWildcardScope(ctx)` — true when the caller holds
|
|
181
183
|
unrestricted access. Useful for skipping filtering you know is pointless;
|
|
182
184
|
never as a substitute for it.
|
|
183
185
|
|
|
186
|
+
**Deprecated since 0.11.0: `passesScopeFilter`, `scopeFilterGet` and
|
|
187
|
+
`scopeFilterList`.** All three (the last two through the first) admit a row
|
|
188
|
+
when its `createdBy` NAME appears in the caller's `fromAllowList`, so a name
|
|
189
|
+
selects the row, which backend standard R-53 clause 1 forbids. For a target
|
|
190
|
+
that carries an organisation or owner ID, use `resolveActingPrincipal` and
|
|
191
|
+
`assertTargetBelongsTo`, which compare stored IDs (see "The acting principal
|
|
192
|
+
by ID, and the target checked by ID"). Their behaviour is unchanged; they stay
|
|
193
|
+
exported until consumers migrate off them, and are removed in a major release.
|
|
194
|
+
|
|
184
195
|
Visibility is granted two ways: the row's `createdBy` appears in the caller's
|
|
185
196
|
`fromAllowList`, or the row's `namespace` sits under one of the caller's
|
|
186
197
|
`namespaceReadPrefixes`. Prefixes match on a path boundary, so a caller allowed
|
|
@@ -410,6 +421,7 @@ const who = await resolvePersonPrincipal(
|
|
|
410
421
|
if (!who.ok) return deny(who.refusal);
|
|
411
422
|
|
|
412
423
|
// 2. Under what name: the token's, never the argument's.
|
|
424
|
+
// Deprecated since 0.11.0: a new door uses resolveActingPrincipal instead.
|
|
413
425
|
const name = resolvePersonActingName({
|
|
414
426
|
principal: who.principal, claimedName: args.createdBy,
|
|
415
427
|
agentCredential: presentedAgent, door: "my-tool",
|
|
@@ -427,6 +439,12 @@ const mayWrite = resolveWriterRole({
|
|
|
427
439
|
- `resolvePersonPrincipal(token, deps, door)` refuses a token that is expired,
|
|
428
440
|
revoked, not a person's, bound to no organisation, or bound to an inactive or
|
|
429
441
|
unmapped one. It looks up only the token's own organisation.
|
|
442
|
+
- **Deprecated since 0.11.0:** `resolvePersonActingName`. It takes names
|
|
443
|
+
(`claimedName`, `agentCredential.agentName`) as identity input, which backend
|
|
444
|
+
standard R-53 clause 4 makes non-conforming for any new door. Use
|
|
445
|
+
`resolveActingPrincipal` and `assertTargetBelongsTo` (see "The acting
|
|
446
|
+
principal by ID, and the target checked by ID"). Its behaviour is unchanged;
|
|
447
|
+
it will be removed in a major release once consumers have migrated off it.
|
|
430
448
|
- `resolvePersonActingName` returns `{ ok: true, actingAs: "person", actor }`
|
|
431
449
|
for no name or the person's own (`user:<subject>`), and refuses another
|
|
432
450
|
user's name (`PERSON_ACTS_AS_ITSELF`) and an agent's name without that
|
|
@@ -450,6 +468,241 @@ const mayWrite = resolveWriterRole({
|
|
|
450
468
|
The module verifies no credential. The token record must come from your own
|
|
451
469
|
lookup of the bearer the person presented, never from a request body.
|
|
452
470
|
|
|
471
|
+
## Role policy, membership, namespace writes and secrets (0.10.0)
|
|
472
|
+
|
|
473
|
+
Seven primitives that consumers used to copy. Each is pure, takes its policy as
|
|
474
|
+
DATA from the call site, and refuses on every absence: no role, no claim, no
|
|
475
|
+
list, no prefix, no secret and a failing lookup are all refusals, never a
|
|
476
|
+
default grant. Refusals share one shape, `IdentityRefusal` (`code`, `reason`,
|
|
477
|
+
`door`, `detail`), thrown inside an `IdentityRefusalError` by the asserting
|
|
478
|
+
forms. `IdentityRefusalCode` is `RBAC_DENIED` or `CREDENTIAL_REFUSED`;
|
|
479
|
+
`IdentityRefusalReason` is the closed list of reasons. Every function that
|
|
480
|
+
refuses accepts an optional `door` so the refusal names your own entry point.
|
|
481
|
+
|
|
482
|
+
**Minimum role over an order you supply** (`./role-policy`)
|
|
483
|
+
|
|
484
|
+
- `resolveMinRole({ role, minimum, order, door? })` — `order` is your ordered
|
|
485
|
+
list, MOST privileged first (for example `["admin", "editor", "viewer"]`).
|
|
486
|
+
Returns `{ ok: true, role }` or `{ ok: false, refusal }` (`MinRoleInput`,
|
|
487
|
+
`MinRoleResult`). Refuses an empty or duplicated order, a `minimum` outside
|
|
488
|
+
the order, an absent or unknown role, and a role below the minimum. Matching
|
|
489
|
+
is exact.
|
|
490
|
+
- `assertMinRole(input)` — the throwing form; returns the role or throws
|
|
491
|
+
`IdentityRefusalError`.
|
|
492
|
+
|
|
493
|
+
**Role-claim mapping with an explicit fallback** (`./role-policy`)
|
|
494
|
+
|
|
495
|
+
- `mapRoleClaim({ claim, mapping, fallback?, door? })` — maps a verified claim
|
|
496
|
+
(for example `"org:admin"`) to your role through `mapping`, your data
|
|
497
|
+
(`MapRoleClaimInput`, `MapRoleClaimResult`). The package never picks a
|
|
498
|
+
default: an absent or unmapped claim is served only when YOU pass `fallback`
|
|
499
|
+
(use your least-privileged role), and is refused otherwise. A claim that is
|
|
500
|
+
not a string counts as absent; prototype keys are never mapped. The result
|
|
501
|
+
says whether the role was `mapped` or came from the `fallback`.
|
|
502
|
+
|
|
503
|
+
**Membership and tenant visibility** (`./tenant-membership`)
|
|
504
|
+
|
|
505
|
+
- `resolveMembership({ subject, orgId, lookup, door? })` — async. `lookup` is
|
|
506
|
+
your read of one membership (`MembershipRecord`: `active`, optional `role`,
|
|
507
|
+
optional `orgId`). A throwing lookup, a miss, an inactive row, a malformed
|
|
508
|
+
row and a row naming another organisation are all refusals; the lookup's own
|
|
509
|
+
error text is not surfaced. Served result: `{ ok: true, membership }`
|
|
510
|
+
(`ResolvedMembership`, `ResolveMembershipInput`, `ResolveMembershipResult`).
|
|
511
|
+
`membershipRecordSchema` is the zod schema of the record.
|
|
512
|
+
- `isRowInTenant({ rowOrgId, callerOrgId })` — `true` only on strict equality
|
|
513
|
+
of two non-empty ids. An unstamped row is never visible, not even to a caller
|
|
514
|
+
with no organisation.
|
|
515
|
+
- `requireOrgAdmin({ verifiedOrgId, targetOrgId, role, adminRoles, door? })` —
|
|
516
|
+
the organisation-admin proof, bound to the caller's VERIFIED organisation:
|
|
517
|
+
the target must equal it, and the role must be one of `adminRoles` (your
|
|
518
|
+
data; an empty list admits nobody). Throws `IdentityRefusalError`
|
|
519
|
+
(`RequireOrgAdminInput`).
|
|
520
|
+
|
|
521
|
+
**Namespace writes** (`./namespace-write`, `./scope-filter`)
|
|
522
|
+
|
|
523
|
+
- `assertNamespaceWrite(oauthCtx, namespace, door?)` — throws unless the
|
|
524
|
+
caller's `namespaceWritePrefixes` admit the namespace. Master scope passes;
|
|
525
|
+
a non-master with no write prefix is refused; read prefixes never grant a
|
|
526
|
+
write; an empty-string prefix grants nothing.
|
|
527
|
+
- `namespaceMatchesPrefix(namespace, prefix)` — the one path-boundary rule:
|
|
528
|
+
equality, or the prefix followed by `/`. `team/finance-archive` is not inside
|
|
529
|
+
`team/finance`. The read filter and `assertNamespaceWrite` both use it.
|
|
530
|
+
|
|
531
|
+
**Synchronous constant-time comparison** (`./secret-compare-sync`)
|
|
532
|
+
|
|
533
|
+
- `timingSafeEqualSync(a, b)` — strings (UTF-8) or `Uint8Array`, pure
|
|
534
|
+
JavaScript, no early exit on content. It walks the longer length and folds a
|
|
535
|
+
length difference into the result. Any other input type is `false`. The async
|
|
536
|
+
`timingSafeEqual` is unchanged.
|
|
537
|
+
- `assertSecretSync(presented, expected, door?)` — throws `IdentityRefusalError`
|
|
538
|
+
(`CREDENTIAL_REFUSED`) on mismatch. An unset `expected` (undefined, null or
|
|
539
|
+
empty) matches nothing, so a missing configuration never opens a door. The
|
|
540
|
+
refusal carries neither value.
|
|
541
|
+
|
|
542
|
+
## The acting principal by ID, and the target checked by ID (0.11.0)
|
|
543
|
+
|
|
544
|
+
**Why this exists.** Two organisations each had an agent displayed as `eta`.
|
|
545
|
+
An organisation A agent, calling through the shared service account and naming
|
|
546
|
+
itself `eta`, completed, updated and deleted organisation B's `eta` task at ten
|
|
547
|
+
different task doors. Every door admitted the call by comparing the typed name
|
|
548
|
+
with a name stored on the task. The names were equal; the two agents were not.
|
|
549
|
+
Nothing compared an ID.
|
|
550
|
+
|
|
551
|
+
The rule this module carries: **an actor and a target are identified by their
|
|
552
|
+
stored IDs. A name is a display label; it never selects a row and never
|
|
553
|
+
authorises one.** Resolving the principal and checking the target both happen
|
|
554
|
+
here, so a product never writes its own identity layer.
|
|
555
|
+
|
|
556
|
+
```js
|
|
557
|
+
import {
|
|
558
|
+
resolveActingPrincipal,
|
|
559
|
+
assertTargetBelongsTo,
|
|
560
|
+
} from "@vantageos/cloud-identity";
|
|
561
|
+
|
|
562
|
+
// The operator org is DATA: the org whose mapping row says orgKind "operator".
|
|
563
|
+
const orgKindOf = async (id) => (await db.orgs.get(id))?.orgKind ?? null;
|
|
564
|
+
|
|
565
|
+
// 1. Who is acting? IDs from the VERIFIED credential, rows read by ID.
|
|
566
|
+
const who = await resolveActingPrincipal(
|
|
567
|
+
{ kind: "service", serviceAccountId: "svc_a", actingForAgentId: "agent_a_eta" },
|
|
568
|
+
{
|
|
569
|
+
agentById: (id) => db.agents.get(id), // -> { id, orgId, active }
|
|
570
|
+
serviceAccountById: (id) => db.services.get(id),
|
|
571
|
+
organisationById: (id) => db.orgs.get(id), // -> { id, active }
|
|
572
|
+
orgKindOf, // -> "operator" | "client" | null
|
|
573
|
+
},
|
|
574
|
+
"tasks:complete",
|
|
575
|
+
);
|
|
576
|
+
if (!who.ok) throw toHttpError(who.refusal); // RBAC_DENIED, typed reason
|
|
577
|
+
|
|
578
|
+
// 2. Does the target belong to them? Stored IDs against resolved IDs.
|
|
579
|
+
const task = await db.tasks.get(taskId);
|
|
580
|
+
const may = await assertTargetBelongsTo(
|
|
581
|
+
who.principal,
|
|
582
|
+
{ orgId: task.orgId, ownerId: task.ownerId },
|
|
583
|
+
{ orgKindOf },
|
|
584
|
+
{ ownerOnly: true, door: "tasks:complete" },
|
|
585
|
+
);
|
|
586
|
+
if (!may.ok) throw toHttpError(may.refusal);
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Organisation B's `eta` task carries `orgId: "org_b"`; the principal above
|
|
590
|
+
resolved to `orgId: "org_a"`. The target check refuses it with
|
|
591
|
+
`target-other-organisation`, whatever either agent is called.
|
|
592
|
+
|
|
593
|
+
**`resolveActingPrincipal(credential, lookups, door?)`** (`./principal-by-id`)
|
|
594
|
+
|
|
595
|
+
Async. Returns `{ ok: true, principal }` or `{ ok: false, refusal }`
|
|
596
|
+
(`ResolveActingPrincipalResult`). The `principal` (`ActingPrincipal`) is
|
|
597
|
+
`{ principalId, orgId, kind, viaServiceAccountId? }`, where `kind` is
|
|
598
|
+
`"agent" | "person" | "service" | "fleet"` (`ActingPrincipalKind`).
|
|
599
|
+
|
|
600
|
+
`credential` (`ActingCredential`, validated by `actingCredentialSchema`) is one
|
|
601
|
+
of three shapes, every identity field an ID:
|
|
602
|
+
|
|
603
|
+
- `{ kind: "agent", agentId, verifiedOrgId }` — the machine path: an agent's
|
|
604
|
+
own bearer, already verified by you (for example with
|
|
605
|
+
`validatePresentedBearer`). The agent row's stored `orgId` must equal
|
|
606
|
+
`verifiedOrgId`.
|
|
607
|
+
- `{ kind: "person", personId, verifiedOrgId }` — the human path: a person's
|
|
608
|
+
token, already verified by you. The person's row in that organisation is read
|
|
609
|
+
by `(personId, verifiedOrgId)`.
|
|
610
|
+
- `{ kind: "service", serviceAccountId, actingForAgentId? }` — the service
|
|
611
|
+
account. Alone it acts as itself (`kind: "service"`, or `kind: "fleet"` if
|
|
612
|
+
`orgKindOf` reports its organisation as `"operator"`). With `actingForAgentId` it acts for that
|
|
613
|
+
agent, named BY ID and resolved within the service account's OWN
|
|
614
|
+
organisation; the principal is the agent, and `viaServiceAccountId` records
|
|
615
|
+
the carrier.
|
|
616
|
+
|
|
617
|
+
The schemas are strict: a credential carrying any other key (`agentName`,
|
|
618
|
+
`callerOrchestrator`, `name`, `assignedTo`) is refused whole with
|
|
619
|
+
`credential-invalid`, before any lookup runs.
|
|
620
|
+
|
|
621
|
+
`lookups` (`PrincipalLookups`) are your indexed reads by ID: `agentById(id)`,
|
|
622
|
+
`personById(id, orgId)`, `serviceAccountById(id)` returning a `PrincipalRow`
|
|
623
|
+
(`{ id, orgId?, active }`, schema `principalRowSchema`), and
|
|
624
|
+
`organisationById(id)` returning an `OrganisationRow` (`{ id, active }`, schema
|
|
625
|
+
`organisationRowSchema`), and `orgKindOf(orgId)` returning an `OrgKind`
|
|
626
|
+
(`"operator" | "client"`) or `null`. Only the lookups of the presented path
|
|
627
|
+
are called; `orgKindOf` is called on every path.
|
|
628
|
+
Each row must repeat the ID that was asked for, and `active` is required and
|
|
629
|
+
must be `true`.
|
|
630
|
+
|
|
631
|
+
Refused, each with a typed `IdentityRefusal` reason: a credential that does not
|
|
632
|
+
parse; a missing or throwing lookup (its error text is never surfaced); a miss;
|
|
633
|
+
a malformed, inactive or unstamped row; a row of another organisation than its
|
|
634
|
+
credential; an unmapped or inactive organisation; a service account acting for
|
|
635
|
+
an agent of another organisation. Nothing falls back to a default or master
|
|
636
|
+
principal.
|
|
637
|
+
|
|
638
|
+
**`assertTargetBelongsTo(principal, target, lookups, opts?)`** (`./principal-by-id`)
|
|
639
|
+
|
|
640
|
+
Async. Returns `{ ok: true }` or `{ ok: false, refusal }`
|
|
641
|
+
(`AssertTargetResult`). `target` (`TargetIds`) is `{ orgId?, ownerId? }` read
|
|
642
|
+
from the STORED row; `lookups` (`OrgKindLookups`) is `{ orgKindOf }`, the same
|
|
643
|
+
adapter as above, and is required (a missing or throwing adapter refuses with
|
|
644
|
+
`principal-lookup-failed`); `opts` (`AssertTargetOptions`) is
|
|
645
|
+
`{ door?, ownerOnly?, fleetCrossOrg? }`. IDs are compared byte for byte.
|
|
646
|
+
|
|
647
|
+
- The target's `orgId` must equal the principal's `orgId`
|
|
648
|
+
(`target-other-organisation` otherwise).
|
|
649
|
+
- `ownerOnly: true` also requires `ownerId` to equal `principalId`; a target
|
|
650
|
+
with no owner is refused (`target-owner-mismatch`).
|
|
651
|
+
- A target with no `orgId` is refused (`target-unstamped`) to EVERY
|
|
652
|
+
principal, the fleet principal included, with or without `fleetCrossOrg`.
|
|
653
|
+
No right is inferred from an absence: a row with no organisation is never
|
|
654
|
+
treated as a master row.
|
|
655
|
+
- An absent principal, or one with an empty ID, is refused
|
|
656
|
+
(`credential-invalid`).
|
|
657
|
+
|
|
658
|
+
**The fleet is the operator organisation.** The fleet is not a reserved ID: it
|
|
659
|
+
is the organisation your `orgKindOf` adapter reports as `"operator"` (for
|
|
660
|
+
example the organisation whose mapping row carries `orgKind: "operator"`),
|
|
661
|
+
read from your data at run time. The package spells no organisation ID. An
|
|
662
|
+
adapter answering `null`, a miss, or anything other than exactly
|
|
663
|
+
`"operator"` means "not the fleet"; a missing or throwing adapter refuses.
|
|
664
|
+
|
|
665
|
+
**RULING 5: membership of the operator organisation is ordinary membership.**
|
|
666
|
+
Its agents and persons resolve as `kind: "agent"` / `"person"` of that
|
|
667
|
+
organisation and reach its rows only; cross-organisation reach (`kind: "fleet"`
|
|
668
|
+
with `fleetCrossOrg`) belongs to the operator organisation's service account
|
|
669
|
+
alone, and `reserved-fleet-scope` refuses a CLAIM of that scope, never plain
|
|
670
|
+
membership:
|
|
671
|
+
|
|
672
|
+
- an agent or person credential verified for the operator organisation
|
|
673
|
+
resolves as an ordinary principal of it, with no cross-organisation reach;
|
|
674
|
+
only a service account stamped with it resolves to `kind: "fleet"`;
|
|
675
|
+
- a credential carrying `kind: "fleet"` is refused (`reserved-fleet-scope`),
|
|
676
|
+
whoever presents it;
|
|
677
|
+
- any non-fleet principal reaching another organisation's row under
|
|
678
|
+
`fleetCrossOrg: true` is refused (`reserved-fleet-scope`); without it,
|
|
679
|
+
`target-other-organisation`;
|
|
680
|
+
- an operator-organisation row is refused to every client principal, with or
|
|
681
|
+
without `fleetCrossOrg` (`reserved-fleet-scope`);
|
|
682
|
+
- a `kind: "fleet"` principal whose organisation the adapter does not report
|
|
683
|
+
as `"operator"` is refused, and so is a `kind: "service"` principal in the
|
|
684
|
+
operator organisation (its service account is the fleet);
|
|
685
|
+
- the fleet principal reaches a CLIENT organisation's row only when the door
|
|
686
|
+
passes `fleetCrossOrg: true` (a master export) AND the adapter reports that
|
|
687
|
+
organisation as `"client"`. An unknown organisation is refused;
|
|
688
|
+
- the operator organisation is an ordinary stored organisation:
|
|
689
|
+
`organisationById` must return it mapped and active, like any other;
|
|
690
|
+
- a fleet row is a row stamped with the operator organisation's ID explicitly.
|
|
691
|
+
A row with no `orgId` is not a fleet row and is refused to the fleet
|
|
692
|
+
principal too.
|
|
693
|
+
|
|
694
|
+
**Migration (required consumer step).** Before a product switches any door to
|
|
695
|
+
`assertTargetBelongsTo`, it must stamp every existing row that has no `orgId`:
|
|
696
|
+
with its real organisation ID, or with the operator organisation's ID if it
|
|
697
|
+
belongs to the operator's own scope. An unstamped row is refused to every caller,
|
|
698
|
+
including the fleet principal, so a door switched before the backfill stops
|
|
699
|
+
serving those rows. Run the backfill first, confirm that no unstamped row is
|
|
700
|
+
left in the tables the door reads, then switch the door.
|
|
701
|
+
|
|
702
|
+
`viaServiceAccountId` and `door` are not identity; no function in this module
|
|
703
|
+
takes a name as an identity input, and a test reads the exported signatures to
|
|
704
|
+
keep it that way.
|
|
705
|
+
|
|
453
706
|
## What this package does not do
|
|
454
707
|
|
|
455
708
|
Worth reading before you rely on it.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed refusal shared by the 0.10.0 primitives (role policy, membership,
|
|
3
|
+
* org-admin proof, namespace write, secret comparison) and the 0.11.0
|
|
4
|
+
* principal-by-id resolver and target check.
|
|
5
|
+
*
|
|
6
|
+
* A refusal is a value carrying its own code, reason and door, so a caller
|
|
7
|
+
* branches on content rather than on the mere presence of an exception, and an
|
|
8
|
+
* absence can never be mistaken for a refusal. It carries no credential.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the shape of `PersonRefusal` / `PersonRefusalError` on purpose: same
|
|
11
|
+
* `code`/`reason`/`door`/`detail` fields, same `ERROR: detail (reason, door)`
|
|
12
|
+
* message form. It is a separate type only because `PersonRefusalCode` is a
|
|
13
|
+
* closed vocabulary for the person path.
|
|
14
|
+
*/
|
|
15
|
+
export type IdentityRefusalCode = "RBAC_DENIED" | "CREDENTIAL_REFUSED";
|
|
16
|
+
export type IdentityRefusalReason = "unknown-role" | "unknown-minimum-role" | "invalid-role-order" | "role-below-minimum" | "role-claim-absent" | "role-claim-unmapped" | "invalid-role-mapping" | "membership-invalid-input" | "membership-lookup-failed" | "membership-not-found" | "membership-record-invalid" | "membership-inactive" | "other-organisation" | "no-verified-organisation" | "role-not-admin" | "namespace-invalid" | "no-write-prefixes" | "namespace-write-denied" | "secret-not-configured" | "secret-mismatch" | "credential-invalid" | "principal-lookup-failed" | "principal-not-found" | "principal-record-invalid" | "principal-inactive" | "organisation-not-active" | "reserved-fleet-scope" | "acting-agent-other-organisation" | "target-unstamped" | "target-other-organisation" | "target-owner-mismatch";
|
|
17
|
+
export type IdentityRefusal = {
|
|
18
|
+
code: IdentityRefusalCode;
|
|
19
|
+
reason: IdentityRefusalReason;
|
|
20
|
+
/** The consumer's own name for the entry point that refused. */
|
|
21
|
+
door: string;
|
|
22
|
+
/** One sentence, safe to show to the caller. Never a credential. */
|
|
23
|
+
detail: string;
|
|
24
|
+
};
|
|
25
|
+
/** Thrown by the asserting forms. Carries the typed refusal untouched. */
|
|
26
|
+
export declare class IdentityRefusalError extends Error {
|
|
27
|
+
readonly refusal: IdentityRefusal;
|
|
28
|
+
constructor(refusal: IdentityRefusal);
|
|
29
|
+
}
|
|
30
|
+
/** @internal Build a refusal. Not exported from the package. */
|
|
31
|
+
export declare function refusal(code: IdentityRefusalCode, reason: IdentityRefusalReason, door: string, detail: string): IdentityRefusal;
|
|
32
|
+
//# sourceMappingURL=identity-refusal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-refusal.d.ts","sourceRoot":"","sources":["../src/identity-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,mBAAmB,GAAG,aAAa,GAAG,oBAAoB,CAAC;AAEvE,MAAM,MAAM,qBAAqB,GAE7B,cAAc,GACd,sBAAsB,GACtB,oBAAoB,GACpB,oBAAoB,GACpB,mBAAmB,GACnB,qBAAqB,GACrB,sBAAsB,GAEtB,0BAA0B,GAC1B,0BAA0B,GAC1B,sBAAsB,GACtB,2BAA2B,GAC3B,qBAAqB,GACrB,oBAAoB,GAEpB,0BAA0B,GAC1B,gBAAgB,GAEhB,mBAAmB,GACnB,mBAAmB,GACnB,wBAAwB,GAExB,uBAAuB,GACvB,iBAAiB,GAEjB,oBAAoB,GACpB,yBAAyB,GACzB,qBAAqB,GACrB,0BAA0B,GAC1B,oBAAoB,GACpB,yBAAyB,GACzB,sBAAsB,GACtB,iCAAiC,GACjC,kBAAkB,GAClB,2BAA2B,GAC3B,uBAAuB,CAAC;AAE5B,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,mBAAmB,CAAC;IAC1B,MAAM,EAAE,qBAAqB,CAAC;IAC9B,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,0EAA0E;AAC1E,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;gBACtB,OAAO,EAAE,eAAe;CAKrC;AAED,gEAAgE;AAChE,wBAAgB,OAAO,CACrB,IAAI,EAAE,mBAAmB,EACzB,MAAM,EAAE,qBAAqB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,eAAe,CAEjB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed refusal shared by the 0.10.0 primitives (role policy, membership,
|
|
3
|
+
* org-admin proof, namespace write, secret comparison) and the 0.11.0
|
|
4
|
+
* principal-by-id resolver and target check.
|
|
5
|
+
*
|
|
6
|
+
* A refusal is a value carrying its own code, reason and door, so a caller
|
|
7
|
+
* branches on content rather than on the mere presence of an exception, and an
|
|
8
|
+
* absence can never be mistaken for a refusal. It carries no credential.
|
|
9
|
+
*
|
|
10
|
+
* Mirrors the shape of `PersonRefusal` / `PersonRefusalError` on purpose: same
|
|
11
|
+
* `code`/`reason`/`door`/`detail` fields, same `ERROR: detail (reason, door)`
|
|
12
|
+
* message form. It is a separate type only because `PersonRefusalCode` is a
|
|
13
|
+
* closed vocabulary for the person path.
|
|
14
|
+
*/
|
|
15
|
+
/** Thrown by the asserting forms. Carries the typed refusal untouched. */
|
|
16
|
+
export class IdentityRefusalError extends Error {
|
|
17
|
+
refusal;
|
|
18
|
+
constructor(refusal) {
|
|
19
|
+
super(`${refusal.code}: ${refusal.detail} (${refusal.reason}, ${refusal.door})`);
|
|
20
|
+
this.name = "IdentityRefusalError";
|
|
21
|
+
this.refusal = refusal;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** @internal Build a refusal. Not exported from the package. */
|
|
25
|
+
export function refusal(code, reason, door, detail) {
|
|
26
|
+
return { code, reason, door, detail };
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=identity-refusal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"identity-refusal.js","sourceRoot":"","sources":["../src/identity-refusal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAoDH,0EAA0E;AAC1E,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IACpC,OAAO,CAAkB;IAClC,YAAY,OAAwB;QAClC,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC;QACjF,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,UAAU,OAAO,CACrB,IAAyB,EACzB,MAA6B,EAC7B,IAAY,EACZ,MAAc;IAEd,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AACxC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* validation.
|
|
7
7
|
*/
|
|
8
8
|
export { timingSafeEqual, sha256Hex } from "./crypto.js";
|
|
9
|
-
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, LEGACY_WILDCARD_CTX, type ScopeFilterable, } from "./scope-filter.js";
|
|
9
|
+
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, namespaceMatchesPrefix, LEGACY_WILDCARD_CTX, type ScopeFilterable, } from "./scope-filter.js";
|
|
10
10
|
export { validateMasterBearer } from "./bearer-validation.js";
|
|
11
11
|
export type { OAuthCtx, ScopeProfile, NamespacePrefix, FromAllowListEntry, ValidateMasterBearerResult, } from "./types.js";
|
|
12
12
|
export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
|
|
@@ -29,4 +29,14 @@ export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js"
|
|
|
29
29
|
export type { AuthorizeRefusal, AuthorizeRefusalReason, } from "./authorize-shared.js";
|
|
30
30
|
export { PERSON_ACTOR_PREFIX, PersonRefusalError, personActorName, isPersonActorName, personTokenRecordSchema, resolvePersonPrincipal, resolvePersonActingName, checkPersonCallShape, resolvePersonTenantAccess, resolveWriterRole, requireWriterRole, } from "./person-principal.js";
|
|
31
31
|
export type { PersonRefusal, PersonRefusalCode, PersonRefusalReason, PersonTokenRecord, PersonPrincipal, OrganisationState, PersonPrincipalDeps, PersonPrincipalResult, PersonActingNameResult, PersonAccessResult, } from "./person-principal.js";
|
|
32
|
+
export { IdentityRefusalError } from "./identity-refusal.js";
|
|
33
|
+
export type { IdentityRefusal, IdentityRefusalCode, IdentityRefusalReason, } from "./identity-refusal.js";
|
|
34
|
+
export { resolveMinRole, assertMinRole, mapRoleClaim } from "./role-policy.js";
|
|
35
|
+
export type { MinRoleInput, MinRoleResult, MapRoleClaimInput, MapRoleClaimResult, } from "./role-policy.js";
|
|
36
|
+
export { isRowInTenant, resolveMembership, requireOrgAdmin, membershipRecordSchema, } from "./tenant-membership.js";
|
|
37
|
+
export type { MembershipRecord, ResolvedMembership, ResolveMembershipInput, ResolveMembershipResult, RequireOrgAdminInput, } from "./tenant-membership.js";
|
|
38
|
+
export { assertNamespaceWrite } from "./namespace-write.js";
|
|
39
|
+
export { timingSafeEqualSync, assertSecretSync } from "./secret-compare-sync.js";
|
|
40
|
+
export { actingCredentialSchema, principalRowSchema, organisationRowSchema, resolveActingPrincipal, assertTargetBelongsTo, } from "./principal-by-id.js";
|
|
41
|
+
export type { ActingCredential, PrincipalRow, OrganisationRow, PrincipalLookups, OrgKind, OrgKindLookups, ActingPrincipalKind, ActingPrincipal, ResolveActingPrincipalResult, TargetIds, AssertTargetOptions, AssertTargetResult, } from "./principal-by-id.js";
|
|
32
42
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* validation.
|
|
7
7
|
*/
|
|
8
8
|
export { timingSafeEqual, sha256Hex } from "./crypto.js";
|
|
9
|
-
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, LEGACY_WILDCARD_CTX, } from "./scope-filter.js";
|
|
9
|
+
export { passesScopeFilter, scopeFilterList, scopeFilterGet, isMasterScope, isWildcardScope, namespaceMatchesPrefix, LEGACY_WILDCARD_CTX, } from "./scope-filter.js";
|
|
10
10
|
export { validateMasterBearer } from "./bearer-validation.js";
|
|
11
11
|
// 0.2.0 — Domain-tenancy layer (additive)
|
|
12
12
|
export { workspaceSchema, workspaceMemberSchema, workspaceRoleSchema, tenantContextSchema, ScopeViolationError, getEffectiveTenantId, decodeUnverifiedBearer, } from "./tenancy-domain.js";
|
|
@@ -29,4 +29,13 @@ export { oauthErrorFor, pkceChallengeFromVerifier } from "./authorize-shared.js"
|
|
|
29
29
|
// pure data, the own-name actor rule, the tenant compare and the writer-role
|
|
30
30
|
// assertion. Every consumer wires a person the same way.
|
|
31
31
|
export { PERSON_ACTOR_PREFIX, PersonRefusalError, personActorName, isPersonActorName, personTokenRecordSchema, resolvePersonPrincipal, resolvePersonActingName, checkPersonCallShape, resolvePersonTenantAccess, resolveWriterRole, requireWriterRole, } from "./person-principal.js";
|
|
32
|
+
// 0.10.0 — Identity gaps closed in the package so consumers adapt, never copy.
|
|
33
|
+
export { IdentityRefusalError } from "./identity-refusal.js";
|
|
34
|
+
export { resolveMinRole, assertMinRole, mapRoleClaim } from "./role-policy.js";
|
|
35
|
+
export { isRowInTenant, resolveMembership, requireOrgAdmin, membershipRecordSchema, } from "./tenant-membership.js";
|
|
36
|
+
export { assertNamespaceWrite } from "./namespace-write.js";
|
|
37
|
+
export { timingSafeEqualSync, assertSecretSync } from "./secret-compare-sync.js";
|
|
38
|
+
// 0.11.0 — The acting principal by ID and the target checked by ID. A name is
|
|
39
|
+
// a display label: it never selects a row and never authorises one.
|
|
40
|
+
export { actingCredentialSchema, principalRowSchema, organisationRowSchema, resolveActingPrincipal, assertTargetBelongsTo, } from "./principal-by-id.js";
|
|
32
41
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAU1E,kFAAkF;AAClF,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AASlE,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAMjF,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,aAAa,EACb,eAAe,EACf,sBAAsB,EACtB,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAS9D,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAU7B,gFAAgF;AAChF,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AAU1E,kFAAkF;AAClF,OAAO,EACL,sBAAsB,EACtB,6BAA6B,EAC7B,gBAAgB,EAChB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AAWzB,sEAAsE;AACtE,yEAAyE;AACzE,mEAAmE;AACnE,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAY/B,+EAA+E;AAC/E,mEAAmE;AACnE,OAAO,EACL,cAAc,EACd,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAOhE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAQ7D,OAAO,EAAE,sBAAsB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AASlE,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAMjF,8EAA8E;AAC9E,6EAA6E;AAC7E,yDAAyD;AACzD,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,EACpB,yBAAyB,EACzB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAc/B,+EAA+E;AAC/E,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAM7D,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAO/E,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,sBAAsB,GACvB,MAAM,wBAAwB,CAAC;AAQhC,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjF,8EAA8E;AAC9E,oEAAoE;AACpE,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace WRITE assertion.
|
|
3
|
+
*
|
|
4
|
+
* `OAuthCtx.namespaceWritePrefixes` has always been on the type with the note
|
|
5
|
+
* "enforced by the caller". This is that enforcement, so no consumer carries
|
|
6
|
+
* its own copy. It uses `namespaceMatchesPrefix` from `./scope-filter`, the
|
|
7
|
+
* same boundary rule as the read filter: prefix equality, or prefix + '/'.
|
|
8
|
+
*
|
|
9
|
+
* A write is never granted by a READ prefix, by an empty prefix list (for a
|
|
10
|
+
* non-master) or by an empty-string prefix.
|
|
11
|
+
*/
|
|
12
|
+
import type { OAuthCtx } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Throws `IdentityRefusalError` unless the caller may write `namespace`.
|
|
15
|
+
* Master scope (`scope === "master"` or `"*"` in `fromAllowList`) passes.
|
|
16
|
+
*
|
|
17
|
+
* @param door The consumer's own name for the entry point, for the refusal.
|
|
18
|
+
*/
|
|
19
|
+
export declare function assertNamespaceWrite(oauthCtx: OAuthCtx, namespace: string, door?: string): void;
|
|
20
|
+
//# sourceMappingURL=namespace-write.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace-write.d.ts","sourceRoot":"","sources":["../src/namespace-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,SAAyB,GAC5B,IAAI,CA2BN"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Namespace WRITE assertion.
|
|
3
|
+
*
|
|
4
|
+
* `OAuthCtx.namespaceWritePrefixes` has always been on the type with the note
|
|
5
|
+
* "enforced by the caller". This is that enforcement, so no consumer carries
|
|
6
|
+
* its own copy. It uses `namespaceMatchesPrefix` from `./scope-filter`, the
|
|
7
|
+
* same boundary rule as the read filter: prefix equality, or prefix + '/'.
|
|
8
|
+
*
|
|
9
|
+
* A write is never granted by a READ prefix, by an empty prefix list (for a
|
|
10
|
+
* non-master) or by an empty-string prefix.
|
|
11
|
+
*/
|
|
12
|
+
import { IdentityRefusalError, refusal } from "./identity-refusal.js";
|
|
13
|
+
import { isMasterScope, namespaceMatchesPrefix } from "./scope-filter.js";
|
|
14
|
+
/**
|
|
15
|
+
* Throws `IdentityRefusalError` unless the caller may write `namespace`.
|
|
16
|
+
* Master scope (`scope === "master"` or `"*"` in `fromAllowList`) passes.
|
|
17
|
+
*
|
|
18
|
+
* @param door The consumer's own name for the entry point, for the refusal.
|
|
19
|
+
*/
|
|
20
|
+
export function assertNamespaceWrite(oauthCtx, namespace, door = "assertNamespaceWrite") {
|
|
21
|
+
if (oauthCtx == null) {
|
|
22
|
+
throw new TypeError("assertNamespaceWrite: oauthCtx is required — pass an explicit OAuthCtx.");
|
|
23
|
+
}
|
|
24
|
+
const deny = (reason, detail) => {
|
|
25
|
+
throw new IdentityRefusalError(refusal("RBAC_DENIED", reason, door, detail));
|
|
26
|
+
};
|
|
27
|
+
if (isMasterScope(oauthCtx))
|
|
28
|
+
return;
|
|
29
|
+
if (typeof namespace !== "string" || namespace.length === 0) {
|
|
30
|
+
deny("namespace-invalid", "A non-empty namespace is required.");
|
|
31
|
+
}
|
|
32
|
+
const prefixes = (oauthCtx.namespaceWritePrefixes ?? []).filter((p) => typeof p === "string" && p.length > 0);
|
|
33
|
+
if (prefixes.length === 0) {
|
|
34
|
+
deny("no-write-prefixes", "This caller holds no namespace write prefix.");
|
|
35
|
+
}
|
|
36
|
+
if (!prefixes.some((p) => namespaceMatchesPrefix(namespace, p))) {
|
|
37
|
+
deny("namespace-write-denied", "The namespace is outside this caller's write prefixes.");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=namespace-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"namespace-write.js","sourceRoot":"","sources":["../src/namespace-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG1E;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAkB,EAClB,SAAiB,EACjB,IAAI,GAAG,sBAAsB;IAE7B,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;QACrB,MAAM,IAAI,SAAS,CACjB,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAAG,CACX,MAA4E,EAC5E,MAAc,EACP,EAAE;QACT,MAAM,IAAI,oBAAoB,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/E,CAAC,CAAC;IAEF,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO;IAEpC,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,mBAAmB,EAAE,oCAAoC,CAAC,CAAC;IAClE,CAAC;IACD,MAAM,QAAQ,GAAG,CAAC,QAAQ,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,MAAM,CAC7D,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAC7C,CAAC;IACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,mBAAmB,EAAE,8CAA8C,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,IAAI,CAAC,wBAAwB,EAAE,wDAAwD,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC"}
|
|
@@ -136,6 +136,13 @@ export type PersonActingNameResult = {
|
|
|
136
136
|
* resolved by the consumer): none ->
|
|
137
137
|
* AGENT_CREDENTIAL_REQUIRED, a
|
|
138
138
|
* different agent -> AGENT_IDENTITY_MISMATCH.
|
|
139
|
+
*
|
|
140
|
+
* @deprecated Since 0.11.0. Use `resolveActingPrincipal` (and
|
|
141
|
+
* `assertTargetBelongsTo` for the target), which resolve the actor by stored
|
|
142
|
+
* ID. This function takes names (`claimedName`, `agentCredential.agentName`)
|
|
143
|
+
* as identity input, which backend standard R-53 clause 4 makes
|
|
144
|
+
* non-conforming for any new door. Behaviour is unchanged; it will be removed
|
|
145
|
+
* in a major release once consumers have migrated off it.
|
|
139
146
|
*/
|
|
140
147
|
export declare function resolvePersonActingName(input: {
|
|
141
148
|
/** Only the actor name is read: the rule needs no organisation. */
|