@fuzdev/fuz_app 0.114.0 → 0.115.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/dist/auth/CLAUDE.md +52 -8
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +4 -3
- package/dist/auth/audit_log_schema.d.ts +3 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +13 -3
- package/dist/auth/cell_action_specs.js +1 -1
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +33 -4
- package/dist/auth/cell_authorize.d.ts +3 -0
- package/dist/auth/cell_authorize.d.ts.map +1 -1
- package/dist/auth/cell_authorize.js +7 -4
- package/dist/auth/request_context.d.ts +12 -3
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +12 -3
- package/dist/auth/role_grant_offer_action_specs.d.ts +30 -5
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +53 -17
- package/dist/auth/role_grant_offer_actions.d.ts +23 -12
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +132 -38
- package/dist/auth/role_grant_offer_queries.d.ts +36 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +76 -0
- package/dist/auth/role_schema.d.ts +12 -0
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +12 -0
- package/dist/auth/self_service_role_actions.d.ts +14 -0
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +25 -1
- package/dist/server/serve_fact_route.d.ts +3 -1
- package/dist/server/serve_fact_route.d.ts.map +1 -1
- package/dist/server/serve_fact_route.js +7 -4
- package/dist/testing/CLAUDE.md +12 -4
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +23 -1
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +34 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +12 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +13 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +9 -7
- package/dist/testing/entities.d.ts +6 -3
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +4 -1
- package/dist/ui/BootstrapForm.svelte +14 -10
- package/dist/ui/BootstrapForm.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +19 -7
- package/dist/ui/LoginForm.svelte +8 -4
- package/dist/ui/LoginForm.svelte.d.ts.map +1 -1
- package/dist/ui/RoleGrantOfferForm.svelte +29 -14
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts +2 -3
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -1
- package/dist/ui/SignupForm.svelte +14 -13
- package/dist/ui/form_state.svelte.d.ts +15 -7
- package/dist/ui/form_state.svelte.d.ts.map +1 -1
- package/dist/ui/form_state.svelte.js +10 -2
- package/dist/ui/role_grant_offers_state.svelte.d.ts +25 -15
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/role_grant_offers_state.svelte.js +7 -1
- package/package.json +1 -21
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { z } from 'zod';
|
|
24
24
|
import { Uuid } from '@fuzdev/fuz_util/id.ts';
|
|
25
|
-
import { ERROR_ROLE_GRANT_NOT_FOUND, ERROR_ROLE_NOT_WEB_GRANTABLE } from '../http/error_schemas.js';
|
|
25
|
+
import { ERROR_INSUFFICIENT_PERMISSIONS, ERROR_ROLE_GRANT_NOT_FOUND, ERROR_ROLE_NOT_WEB_GRANTABLE } from '../http/error_schemas.js';
|
|
26
26
|
import { RoleName } from './role_schema.js';
|
|
27
27
|
import { ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX, RoleGrantOfferJson } from './role_grant_offer_schema.js';
|
|
28
28
|
import { ROLE_GRANT_REVOKED_REASON_LENGTH_MAX } from './account_schema.js';
|
|
@@ -43,6 +43,29 @@ export const ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED = 'role_grant_offer_not_autho
|
|
|
43
43
|
export const ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH = 'role_grant_offer_actor_mismatch';
|
|
44
44
|
/** Error reason — `role_grant_offer_create` was called with a `to_actor_id` that does not belong to `to_account_id`. */
|
|
45
45
|
export const ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH = 'role_grant_offer_actor_account_mismatch';
|
|
46
|
+
/**
|
|
47
|
+
* Error reason — a grant path was asked to bind a builtin role to a scope
|
|
48
|
+
* (builtin roles are global-only, see `is_builtin_role`). Refused by
|
|
49
|
+
* `role_grant_assign`, `role_grant_offer_create`, and `role_grant_offer_accept`.
|
|
50
|
+
*/
|
|
51
|
+
export const ERROR_ROLE_GRANT_BUILTIN_SCOPED = 'role_grant_builtin_scoped';
|
|
52
|
+
/**
|
|
53
|
+
* Whether a grant input's `scope_kind` + `scope_id` are paired — both absent
|
|
54
|
+
* (a global grant) or both present (a scoped one), the
|
|
55
|
+
* `role_grant_scope_kind_paired` / `role_grant_offer_scope_kind_paired`
|
|
56
|
+
* CHECKs. The grant inputs refine on it so a half-scoped call is a `-32602`
|
|
57
|
+
* rather than a constraint-violation 500.
|
|
58
|
+
*
|
|
59
|
+
* @param input - the grant input's scope pair
|
|
60
|
+
* @returns `true` when both are set or both are absent
|
|
61
|
+
*/
|
|
62
|
+
export const is_grant_scope_paired = (input) => (input.scope_kind == null) === (input.scope_id == null);
|
|
63
|
+
/** The message a half-scoped grant input fails with — the grant inputs' refine, and the UI's pre-RPC guard. */
|
|
64
|
+
export const GRANT_SCOPE_PAIRED_MESSAGE = "'scope_kind' and 'scope_id' must be given together (both for a scoped grant, neither for a global one)";
|
|
65
|
+
/** The `scope_kind` field both grant inputs share, paired with `scope_id`. */
|
|
66
|
+
const GrantScopeKind = z.string().nullish().meta({
|
|
67
|
+
description: 'Machine-readable kind tag for `scope_id` — paired-null with `scope_id` (both null for global, both non-null for scoped). Required iff `scope_id` is set.'
|
|
68
|
+
});
|
|
46
69
|
// -- Input/output schemas ---------------------------------------------------
|
|
47
70
|
/**
|
|
48
71
|
* Input for `role_grant_offer_create`.
|
|
@@ -55,15 +78,14 @@ export const ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH = 'role_grant_offer_a
|
|
|
55
78
|
* events. Omit (or pass null) for the account-grain default — any actor
|
|
56
79
|
* on `to_account_id` may accept.
|
|
57
80
|
*/
|
|
58
|
-
export const RoleGrantOfferCreateInput = z
|
|
81
|
+
export const RoleGrantOfferCreateInput = z
|
|
82
|
+
.strictObject({
|
|
59
83
|
to_account_id: Uuid.meta({ description: 'Account id of the recipient.' }),
|
|
60
84
|
to_actor_id: Uuid.nullish().meta({
|
|
61
85
|
description: 'Optional actor-grain target on the recipient account. When set, only this actor may accept and the audit envelope carries it on offer-shape events. Must belong to `to_account_id`.'
|
|
62
86
|
}),
|
|
63
87
|
role: RoleName.meta({ description: 'Role being offered.' }),
|
|
64
|
-
scope_kind:
|
|
65
|
-
description: 'Machine-readable kind tag for `scope_id` — paired-null with `scope_id` (both null for global, both non-null for scoped). Required iff `scope_id` is set.'
|
|
66
|
-
}),
|
|
88
|
+
scope_kind: GrantScopeKind,
|
|
67
89
|
scope_id: Uuid.nullish().meta({
|
|
68
90
|
description: 'Scope id for resource-scoped grants (e.g. classroom id). `null` for global.'
|
|
69
91
|
}),
|
|
@@ -73,7 +95,8 @@ export const RoleGrantOfferCreateInput = z.strictObject({
|
|
|
73
95
|
.nullish()
|
|
74
96
|
.meta({ description: 'Optional free-form note from the grantor.' }),
|
|
75
97
|
acting: ActingActor
|
|
76
|
-
})
|
|
98
|
+
})
|
|
99
|
+
.refine(is_grant_scope_paired, { message: GRANT_SCOPE_PAIRED_MESSAGE });
|
|
77
100
|
/** Input for `role_grant_offer_accept`. */
|
|
78
101
|
export const RoleGrantOfferAcceptInput = z.strictObject({
|
|
79
102
|
offer_id: Uuid.meta({ description: 'The offer to accept.' }),
|
|
@@ -162,10 +185,12 @@ export const RoleGrantRevokeOutput = z.strictObject({
|
|
|
162
185
|
* Input for `role_grant_assign` — the immediate admin-only conferral path, the
|
|
163
186
|
* consent-free sibling of `role_grant_offer_create`. An admin assigns a
|
|
164
187
|
* role_grant straight onto the target actor; there is no offer for a grantee to
|
|
165
|
-
* accept. No `message` (no offer to carry it on)
|
|
166
|
-
*
|
|
188
|
+
* accept. No `message` (no offer to carry it on). `scope_kind` pairs with
|
|
189
|
+
* `scope_id` exactly as on `role_grant_offer_create` — both or neither — and
|
|
190
|
+
* the grant row carries the pair.
|
|
167
191
|
*/
|
|
168
|
-
export const RoleGrantAssignInput = z
|
|
192
|
+
export const RoleGrantAssignInput = z
|
|
193
|
+
.strictObject({
|
|
169
194
|
to_account_id: Uuid.meta({ description: 'Account id of the grantee.' }),
|
|
170
195
|
to_actor_id: Uuid.nullish().meta({
|
|
171
196
|
description: "Optional actor-grain target on the grantee account. When set, must belong to `to_account_id`. Omit to resolve the account's sole active actor — a multi-actor account must name one."
|
|
@@ -173,11 +198,13 @@ export const RoleGrantAssignInput = z.strictObject({
|
|
|
173
198
|
role: RoleName.meta({
|
|
174
199
|
description: "Role to assign. Must be admin-grantable (its `grant_paths` includes `'admin'`)."
|
|
175
200
|
}),
|
|
201
|
+
scope_kind: GrantScopeKind,
|
|
176
202
|
scope_id: Uuid.nullish().meta({
|
|
177
|
-
description: 'Scope id for resource-scoped grants. `null` for a global role_grant.'
|
|
203
|
+
description: 'Scope id for resource-scoped grants. `null` for a global role_grant. Builtin roles are global-only.'
|
|
178
204
|
}),
|
|
179
205
|
acting: ActingActor
|
|
180
|
-
})
|
|
206
|
+
})
|
|
207
|
+
.refine(is_grant_scope_paired, { message: GRANT_SCOPE_PAIRED_MESSAGE });
|
|
181
208
|
/** Output for `role_grant_assign`. */
|
|
182
209
|
export const RoleGrantAssignOutput = z.strictObject({
|
|
183
210
|
ok: z.literal(true),
|
|
@@ -203,12 +230,13 @@ export const role_grant_offer_create_action_spec = {
|
|
|
203
230
|
input: RoleGrantOfferCreateInput,
|
|
204
231
|
output: RoleGrantOfferCreateOutput,
|
|
205
232
|
async: true,
|
|
206
|
-
description: "Offer a role_grant to another account.
|
|
233
|
+
description: "Offer a role_grant to another account. Admin-only by default (a consumer authorize callback may widen it for app roles); role's `grant_paths` must include `'admin'`. A builtin role is global-only and offered only by a global admin, whatever the callback says.",
|
|
207
234
|
error_reasons: [
|
|
208
235
|
ERROR_ROLE_GRANT_OFFER_SELF_TARGET,
|
|
209
236
|
ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE,
|
|
210
237
|
ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED,
|
|
211
|
-
ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH
|
|
238
|
+
ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH,
|
|
239
|
+
ERROR_ROLE_GRANT_BUILTIN_SCOPED
|
|
212
240
|
],
|
|
213
241
|
rate_limit: 'account'
|
|
214
242
|
};
|
|
@@ -232,12 +260,15 @@ export const role_grant_offer_accept_action_spec = {
|
|
|
232
260
|
input: RoleGrantOfferAcceptInput,
|
|
233
261
|
output: RoleGrantOfferAcceptOutput,
|
|
234
262
|
async: true,
|
|
235
|
-
description: 'Accept an offer. Atomically marks the offer accepted, inserts the role_grant, and supersedes sibling pending offers for the same (account, role, scope).',
|
|
263
|
+
description: 'Accept an offer. Atomically marks the offer accepted, inserts the role_grant, and supersedes sibling pending offers for the same (account, role, scope). A builtin-role offer is refused (and stays pending) when it is `keeper`, scoped, or its grantor no longer holds a global admin.',
|
|
236
264
|
error_reasons: [
|
|
237
265
|
ERROR_ROLE_GRANT_OFFER_NOT_FOUND,
|
|
238
266
|
ERROR_ROLE_GRANT_OFFER_TERMINAL,
|
|
239
267
|
ERROR_ROLE_GRANT_OFFER_EXPIRED,
|
|
240
|
-
ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH
|
|
268
|
+
ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH,
|
|
269
|
+
ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE,
|
|
270
|
+
ERROR_ROLE_GRANT_BUILTIN_SCOPED,
|
|
271
|
+
ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED
|
|
241
272
|
]
|
|
242
273
|
};
|
|
243
274
|
export const role_grant_offer_decline_action_spec = {
|
|
@@ -319,8 +350,13 @@ export const role_grant_assign_action_spec = {
|
|
|
319
350
|
input: RoleGrantAssignInput,
|
|
320
351
|
output: RoleGrantAssignOutput,
|
|
321
352
|
async: true,
|
|
322
|
-
description: "Immediately assign a role_grant to a target account — admin-only, no consent step (the capability-unlock UX). The role's `grant_paths` must include `'admin'
|
|
323
|
-
error_reasons: [
|
|
353
|
+
description: "Immediately assign a role_grant to a target account — global-admin-only, no consent step (the capability-unlock UX). The role's `grant_paths` must include `'admin'`; `scope_kind` + `scope_id` are paired, and a builtin role is global-only. Idempotent: re-assigning an active grant returns it. Emits a role_grant_create audit event.",
|
|
354
|
+
error_reasons: [
|
|
355
|
+
ERROR_INSUFFICIENT_PERMISSIONS,
|
|
356
|
+
ERROR_ROLE_NOT_WEB_GRANTABLE,
|
|
357
|
+
ERROR_ROLE_GRANT_BUILTIN_SCOPED,
|
|
358
|
+
ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH
|
|
359
|
+
],
|
|
324
360
|
rate_limit: 'account'
|
|
325
361
|
};
|
|
326
362
|
/**
|
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
* `side_effects: false` so they are addressable via GET.
|
|
12
12
|
*
|
|
13
13
|
* Authorization:
|
|
14
|
-
* - `role_grant_offer_create` — the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* - `role_grant_offer_create` — the role's `grant_paths` must include `'admin'`,
|
|
15
|
+
* and by default the grantor must hold a **global** `admin`. Consumers needing
|
|
16
|
+
* a richer policy (e.g., "teacher may offer student in *their* classroom")
|
|
17
|
+
* pass an `authorize` callback that overrides the default — for app roles
|
|
18
|
+
* only: a builtin role (`admin` / `keeper`) is global-only (a scoped one is
|
|
19
|
+
* refused with `role_grant_builtin_scoped`) and offered only by a global
|
|
20
|
+
* admin, both decided before the callback runs.
|
|
18
21
|
* - `role_grant_offer_accept` / `role_grant_offer_decline` — keyed to the caller's
|
|
19
|
-
* account; `query_*` helpers enforce the IDOR guard.
|
|
22
|
+
* account; `query_*` helpers enforce the IDOR guard. Accept also refuses a
|
|
23
|
+
* builtin-role offer that must not mint (`keeper`, scoped, or a grantor who
|
|
24
|
+
* no longer holds a global admin), auditing the refusal and leaving the
|
|
25
|
+
* offer pending.
|
|
20
26
|
* - `role_grant_offer_retract` — keyed to the caller's actor.
|
|
21
27
|
* - `role_grant_offer_list` / `role_grant_offer_history` — self by default;
|
|
22
28
|
* `{account_id}` is admin-only.
|
|
@@ -24,10 +30,12 @@
|
|
|
24
30
|
* dispatcher rejects non-admin callers before the handler runs.
|
|
25
31
|
* The admin-grant-path gate prevents revoking keeper / daemon-scoped
|
|
26
32
|
* roles via this surface. Keys on `actor_id` to survive multi-actor accounts.
|
|
27
|
-
* - `role_grant_assign` — spec-level `auth: {role: 'admin'}
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
33
|
+
* - `role_grant_assign` — spec-level `auth: {role: 'admin'}` (a **global**
|
|
34
|
+
* grant, re-checked in the handler); the immediate consent-free conferral
|
|
35
|
+
* path. The admin-grant-path gate prevents assigning keeper / daemon-scoped
|
|
36
|
+
* roles via this surface, and a scoped `admin` is refused. No
|
|
37
|
+
* holder-propagation — only a global admin confers; holding a role grants
|
|
38
|
+
* no power to assign it.
|
|
31
39
|
*
|
|
32
40
|
* Audit events are emitted in-transaction by the query layer (atomic with
|
|
33
41
|
* the role_grant write on accept/revoke) or by the handler via the bound
|
|
@@ -77,9 +85,12 @@ export interface RoleGrantOfferActionOptions {
|
|
|
77
85
|
default_ttl_ms?: number;
|
|
78
86
|
/**
|
|
79
87
|
* Custom authorization for `role_grant_offer_create`. The default requires the
|
|
80
|
-
* caller to hold
|
|
81
|
-
* `RoleSpec.grant_paths`
|
|
82
|
-
* policies (scope-aware, chained roles) override this.
|
|
88
|
+
* caller to hold a **global** `admin` grant; the role's
|
|
89
|
+
* `RoleSpec.grant_paths` must include `'admin'` either way. Consumers with
|
|
90
|
+
* richer policies (scope-aware, chained roles) override this. A builtin
|
|
91
|
+
* role never reaches the callback unless the caller holds a global `admin`
|
|
92
|
+
* and the offer is unscoped — the spine decides builtin-role offers itself,
|
|
93
|
+
* so a callback needs no builtin handling of its own.
|
|
83
94
|
*/
|
|
84
95
|
authorize?: RoleGrantOfferCreateAuthorize;
|
|
85
96
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"role_grant_offer_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAGN,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAKN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AA4B1B,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,EAON,KAAK,kBAAkB,EACvB,MAAM,qCAAqC,CAAC;AAyC7C;;;;;;;;GAQG;AACH,MAAM,MAAM,6BAA6B,GAAG,CAC3C,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACvE,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACrB,GAAG,EAAE,aAAa,KACd,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEhC,qDAAqD;AACrD,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB,0FAA0F;IAC1F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;OAQG;IACH,SAAS,CAAC,EAAE,6BAA6B,CAAC;CAC1C;AA6CD;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,iBAAiB,GAAG;IACzB,mBAAmB,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;CAChD,EACD,UAAS,2BAAgC,KACvC,KAAK,CAAC,SAAS,CAsnBjB,CAAC"}
|
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
* `side_effects: false` so they are addressable via GET.
|
|
12
12
|
*
|
|
13
13
|
* Authorization:
|
|
14
|
-
* - `role_grant_offer_create` — the
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* - `role_grant_offer_create` — the role's `grant_paths` must include `'admin'`,
|
|
15
|
+
* and by default the grantor must hold a **global** `admin`. Consumers needing
|
|
16
|
+
* a richer policy (e.g., "teacher may offer student in *their* classroom")
|
|
17
|
+
* pass an `authorize` callback that overrides the default — for app roles
|
|
18
|
+
* only: a builtin role (`admin` / `keeper`) is global-only (a scoped one is
|
|
19
|
+
* refused with `role_grant_builtin_scoped`) and offered only by a global
|
|
20
|
+
* admin, both decided before the callback runs.
|
|
18
21
|
* - `role_grant_offer_accept` / `role_grant_offer_decline` — keyed to the caller's
|
|
19
|
-
* account; `query_*` helpers enforce the IDOR guard.
|
|
22
|
+
* account; `query_*` helpers enforce the IDOR guard. Accept also refuses a
|
|
23
|
+
* builtin-role offer that must not mint (`keeper`, scoped, or a grantor who
|
|
24
|
+
* no longer holds a global admin), auditing the refusal and leaving the
|
|
25
|
+
* offer pending.
|
|
20
26
|
* - `role_grant_offer_retract` — keyed to the caller's actor.
|
|
21
27
|
* - `role_grant_offer_list` / `role_grant_offer_history` — self by default;
|
|
22
28
|
* `{account_id}` is admin-only.
|
|
@@ -24,10 +30,12 @@
|
|
|
24
30
|
* dispatcher rejects non-admin callers before the handler runs.
|
|
25
31
|
* The admin-grant-path gate prevents revoking keeper / daemon-scoped
|
|
26
32
|
* roles via this surface. Keys on `actor_id` to survive multi-actor accounts.
|
|
27
|
-
* - `role_grant_assign` — spec-level `auth: {role: 'admin'}
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
33
|
+
* - `role_grant_assign` — spec-level `auth: {role: 'admin'}` (a **global**
|
|
34
|
+
* grant, re-checked in the handler); the immediate consent-free conferral
|
|
35
|
+
* path. The admin-grant-path gate prevents assigning keeper / daemon-scoped
|
|
36
|
+
* roles via this surface, and a scoped `admin` is refused. No
|
|
37
|
+
* holder-propagation — only a global admin confers; holding a role grants
|
|
38
|
+
* no power to assign it.
|
|
31
39
|
*
|
|
32
40
|
* Audit events are emitted in-transaction by the query layer (atomic with
|
|
33
41
|
* the role_grant write on accept/revoke) or by the handler via the bound
|
|
@@ -45,16 +53,16 @@
|
|
|
45
53
|
import { rpc_action } from '../actions/action_rpc.js';
|
|
46
54
|
import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
|
|
47
55
|
import { emit_after_commit } from '../http/pending_effects.js';
|
|
48
|
-
import { builtin_role_specs_by_name, ROLE_ADMIN, role_has_grant_path } from './role_schema.js';
|
|
56
|
+
import { builtin_role_specs_by_name, is_builtin_role, ROLE_ADMIN, role_has_grant_path } from './role_schema.js';
|
|
49
57
|
import { GRANT_PATH_ADMIN } from './grant_path_schema.js';
|
|
50
58
|
import { ROLE_GRANT_OFFER_DEFAULT_TTL_MS, to_role_grant_offer_json } from './role_grant_offer_schema.js';
|
|
51
|
-
import { query_role_grant_offer_create, query_role_grant_offer_decline, query_role_grant_offer_retract, query_role_grant_offer_list, query_role_grant_offer_history_for_account, query_accept_offer, RoleGrantOfferActorAccountMismatchError, RoleGrantOfferActorMismatchError, RoleGrantOfferAlreadyTerminalError, RoleGrantOfferExpiredError, RoleGrantOfferNotFoundError, RoleGrantOfferSelfTargetError } from './role_grant_offer_queries.js';
|
|
59
|
+
import { query_role_grant_offer_create, query_role_grant_offer_decline, query_role_grant_offer_retract, query_role_grant_offer_list, query_role_grant_offer_history_for_account, query_accept_offer, RoleGrantOfferActorAccountMismatchError, RoleGrantOfferActorMismatchError, RoleGrantOfferAlreadyTerminalError, RoleGrantOfferBuiltinRefusedError, RoleGrantOfferExpiredError, RoleGrantOfferNotFoundError, RoleGrantOfferSelfTargetError } from './role_grant_offer_queries.js';
|
|
52
60
|
import { query_create_role_grant, query_role_grant_find_active_role_for_actor, query_revoke_role_grant } from './role_grant_queries.js';
|
|
53
61
|
import { query_actor_by_id, query_active_actors_by_account } from './account_queries.js';
|
|
54
62
|
import { has_scoped_role } from './request_context.js';
|
|
55
63
|
import { build_role_grant_offer_accepted_notification, build_role_grant_offer_declined_notification, build_role_grant_offer_received_notification, build_role_grant_offer_retracted_notification, build_role_grant_offer_supersede_notification, build_role_grant_revoke_notification } from './role_grant_offer_notifications.js';
|
|
56
|
-
import { ERROR_ROLE_GRANT_NOT_FOUND, ERROR_ROLE_NOT_WEB_GRANTABLE } from '../http/error_schemas.js';
|
|
57
|
-
import { ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH, ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH, ERROR_ROLE_GRANT_OFFER_EXPIRED, ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED, ERROR_ROLE_GRANT_OFFER_NOT_FOUND, ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE, ERROR_ROLE_GRANT_OFFER_SELF_TARGET, ERROR_ROLE_GRANT_OFFER_TERMINAL, role_grant_offer_create_action_spec, role_grant_offer_accept_action_spec, role_grant_offer_decline_action_spec, role_grant_offer_retract_action_spec, role_grant_offer_list_action_spec, role_grant_offer_history_action_spec, role_grant_revoke_action_spec, role_grant_assign_action_spec } from './role_grant_offer_action_specs.js';
|
|
64
|
+
import { ERROR_INSUFFICIENT_PERMISSIONS, ERROR_ROLE_GRANT_NOT_FOUND, ERROR_ROLE_NOT_WEB_GRANTABLE } from '../http/error_schemas.js';
|
|
65
|
+
import { ERROR_ROLE_GRANT_BUILTIN_SCOPED, ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH, ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH, ERROR_ROLE_GRANT_OFFER_EXPIRED, ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED, ERROR_ROLE_GRANT_OFFER_NOT_FOUND, ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE, ERROR_ROLE_GRANT_OFFER_SELF_TARGET, ERROR_ROLE_GRANT_OFFER_TERMINAL, role_grant_offer_create_action_spec, role_grant_offer_accept_action_spec, role_grant_offer_decline_action_spec, role_grant_offer_retract_action_spec, role_grant_offer_list_action_spec, role_grant_offer_history_action_spec, role_grant_revoke_action_spec, role_grant_assign_action_spec } from './role_grant_offer_action_specs.js';
|
|
58
66
|
// -- Helpers ----------------------------------------------------------------
|
|
59
67
|
/**
|
|
60
68
|
* Fan out a batch of pre-written audit rows to the bound emitter's
|
|
@@ -71,13 +79,22 @@ const fan_out_audit_events = (events, audit) => {
|
|
|
71
79
|
audit.notify(event);
|
|
72
80
|
}
|
|
73
81
|
};
|
|
82
|
+
/**
|
|
83
|
+
* Whether a grant input binds a builtin role to a scope — refused on every
|
|
84
|
+
* grant path (see `ERROR_ROLE_GRANT_BUILTIN_SCOPED`).
|
|
85
|
+
*/
|
|
86
|
+
const is_scoped_builtin = (role, scope_id) => scope_id != null && is_builtin_role(role);
|
|
87
|
+
/** The `invalid_params` a scoped builtin-role grant input answers. */
|
|
88
|
+
const builtin_scoped_error = () => jsonrpc_errors.invalid_params('builtin roles are global-only and cannot be scoped', {
|
|
89
|
+
reason: ERROR_ROLE_GRANT_BUILTIN_SCOPED
|
|
90
|
+
});
|
|
74
91
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
75
92
|
const default_authorize = async (auth, _input, _deps, _ctx) => {
|
|
76
93
|
// Admin-only conferral: the caller must hold a *global* `admin` grant. Holding
|
|
77
|
-
// the offered role itself confers no power to offer it (no holder-propagation
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
94
|
+
// the offered role itself confers no power to offer it (no holder-propagation).
|
|
95
|
+
// The global check (scope_id null) stops a scoped `admin` escalating into
|
|
96
|
+
// global authority over the offer surface. Reads the in-memory
|
|
97
|
+
// `auth.role_grants` snapshot; no DB roundtrip.
|
|
81
98
|
return has_scoped_role(auth, ROLE_ADMIN, null);
|
|
82
99
|
};
|
|
83
100
|
// -- Action factory ---------------------------------------------------------
|
|
@@ -94,11 +111,11 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
94
111
|
const role_specs = options.roles?.role_specs ?? builtin_role_specs_by_name;
|
|
95
112
|
const default_ttl_ms = options.default_ttl_ms ?? ROLE_GRANT_OFFER_DEFAULT_TTL_MS;
|
|
96
113
|
const authorize = options.authorize ?? default_authorize;
|
|
97
|
-
//
|
|
98
|
-
// actor-account mismatch)
|
|
99
|
-
// event. `target_actor_id` is populated when the
|
|
100
|
-
// `to_actor_id` so failure rows match the success-shape
|
|
101
|
-
// actor-targeted offers.
|
|
114
|
+
// Every denial path (admin-grant-path gate, scoped builtin, builtin without a
|
|
115
|
+
// global admin, authorize, self-target, actor-account mismatch) emits the
|
|
116
|
+
// same failure-outcome audit event. `target_actor_id` is populated when the
|
|
117
|
+
// caller supplied a `to_actor_id` so failure rows match the success-shape
|
|
118
|
+
// envelope of actor-targeted offers.
|
|
102
119
|
const emit_create_failure_audit = (ctx, auth, input) => {
|
|
103
120
|
audit.emit_role_grant_target(ctx, auth, {
|
|
104
121
|
event_type: 'role_grant_offer_create',
|
|
@@ -112,6 +129,46 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
112
129
|
}
|
|
113
130
|
});
|
|
114
131
|
};
|
|
132
|
+
// Answer (and audit) an accept the query refused as a builtin-role offer that
|
|
133
|
+
// must not mint. The refusal is a forensic event — someone holds an offer of a
|
|
134
|
+
// builtin role the spine won't confer — so it is audited as a failed
|
|
135
|
+
// `role_grant_offer_accept` (pool-routed, surviving the rollback), like
|
|
136
|
+
// offer-create refusals. Metadata is the success row's shape minus
|
|
137
|
+
// `role_grant_id` (no grant was minted), plus the wire `reason`. The offer
|
|
138
|
+
// stays pending. Returns the error the verb answers.
|
|
139
|
+
const refuse_builtin_accept = (ctx, auth, err) => {
|
|
140
|
+
let error;
|
|
141
|
+
switch (err.refusal) {
|
|
142
|
+
case 'bootstrap_only':
|
|
143
|
+
// `keeper`'s grant path is bootstrap-only — the offer-create answer
|
|
144
|
+
// for a role no offer may confer
|
|
145
|
+
error = jsonrpc_errors.forbidden('role not grantable', {
|
|
146
|
+
reason: ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE
|
|
147
|
+
});
|
|
148
|
+
break;
|
|
149
|
+
case 'scoped':
|
|
150
|
+
error = jsonrpc_errors.invalid_request({ reason: ERROR_ROLE_GRANT_BUILTIN_SCOPED });
|
|
151
|
+
break;
|
|
152
|
+
case 'grantor_not_authorized':
|
|
153
|
+
error = jsonrpc_errors.forbidden("the offer's grantor can no longer confer this role", {
|
|
154
|
+
reason: ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED
|
|
155
|
+
});
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
audit.emit_role_grant_target(ctx, auth, {
|
|
159
|
+
event_type: 'role_grant_offer_accept',
|
|
160
|
+
outcome: 'failure',
|
|
161
|
+
target_account_id: auth.account.id,
|
|
162
|
+
target_actor_id: auth.actor.id,
|
|
163
|
+
metadata: {
|
|
164
|
+
offer_id: err.offer.id,
|
|
165
|
+
role: err.offer.role,
|
|
166
|
+
scope_id: err.offer.scope_id,
|
|
167
|
+
reason: error.data.reason
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
return error;
|
|
171
|
+
};
|
|
115
172
|
// Returns {offer} only — no auto-accept. Recipient must call
|
|
116
173
|
// role_grant_offer_accept; admin tests drive the full consent flow over
|
|
117
174
|
// RPC (see testing/admin_integration.ts `offer_and_accept`), or seed
|
|
@@ -126,6 +183,23 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
126
183
|
reason: ERROR_ROLE_GRANT_OFFER_ROLE_NOT_GRANTABLE
|
|
127
184
|
});
|
|
128
185
|
}
|
|
186
|
+
// Builtin roles are global-only — refused before the injected callback,
|
|
187
|
+
// so no consumer authorizer can admit a scoped `admin` / `keeper` offer.
|
|
188
|
+
if (is_scoped_builtin(input.role, input.scope_id)) {
|
|
189
|
+
emit_create_failure_audit(ctx, auth, input);
|
|
190
|
+
throw builtin_scoped_error();
|
|
191
|
+
}
|
|
192
|
+
// Offering a builtin role is global-admin authority, decided here rather
|
|
193
|
+
// than by the injected callback: a consumer callback answering by holding
|
|
194
|
+
// the role (a scope-blind `has_role`) would otherwise let a scoped `admin`
|
|
195
|
+
// row offer a global `admin` to a second account it controls. Same denial
|
|
196
|
+
// the default callback gives.
|
|
197
|
+
if (is_builtin_role(input.role) && !has_scoped_role(auth, ROLE_ADMIN, null)) {
|
|
198
|
+
emit_create_failure_audit(ctx, auth, input);
|
|
199
|
+
throw jsonrpc_errors.forbidden('not authorized to offer this role', {
|
|
200
|
+
reason: ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED
|
|
201
|
+
});
|
|
202
|
+
}
|
|
129
203
|
const authorized = await authorize(auth, {
|
|
130
204
|
to_account_id: input.to_account_id,
|
|
131
205
|
role: input.role,
|
|
@@ -214,6 +288,9 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
214
288
|
reason: ERROR_ROLE_GRANT_OFFER_ACTOR_MISMATCH
|
|
215
289
|
});
|
|
216
290
|
}
|
|
291
|
+
if (err instanceof RoleGrantOfferBuiltinRefusedError) {
|
|
292
|
+
throw refuse_builtin_accept(ctx, auth, err);
|
|
293
|
+
}
|
|
217
294
|
throw err;
|
|
218
295
|
}
|
|
219
296
|
// Look up the grantor's account_id inside the transaction so the
|
|
@@ -448,25 +525,43 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
448
525
|
return { ok: true, revoked: true };
|
|
449
526
|
};
|
|
450
527
|
// The immediate admin-only conferral path — the consent-free sibling of
|
|
451
|
-
// `role_grant_offer_create`.
|
|
452
|
-
// ['admin']`), it re-checks admin-grantability
|
|
453
|
-
// can't be web-assigned),
|
|
454
|
-
//
|
|
455
|
-
//
|
|
456
|
-
//
|
|
528
|
+
// `role_grant_offer_create`. Gated at the dispatcher on a **global** admin
|
|
529
|
+
// (`roles: ['admin']`) and re-checked here, it re-checks admin-grantability
|
|
530
|
+
// (so keeper / CLI-only roles can't be web-assigned), refuses a scoped
|
|
531
|
+
// builtin role, resolves the target actor, writes the idempotent grant with
|
|
532
|
+
// the input's `(scope_kind, scope_id)` pair, and emits a `role_grant_create`
|
|
533
|
+
// audit row — the same event the offer-accept and self-service paths emit.
|
|
534
|
+
// No WS notification in v1 (the grantee picks the capability up on its next
|
|
535
|
+
// authenticated request).
|
|
457
536
|
const assign_handler = async (input, ctx) => {
|
|
458
537
|
const auth = ctx.auth;
|
|
538
|
+
// Defense in depth behind the dispatcher's role gate: conferring roles is
|
|
539
|
+
// global-admin authority. A scoped `admin` must never reach the write, or
|
|
540
|
+
// it could assign itself a global `admin`.
|
|
541
|
+
if (!has_scoped_role(auth, ROLE_ADMIN, null)) {
|
|
542
|
+
throw jsonrpc_errors.forbidden('forbidden', { reason: ERROR_INSUFFICIENT_PERMISSIONS });
|
|
543
|
+
}
|
|
459
544
|
// Admin-grant-path gate — the same registry check the offer/revoke gates
|
|
460
|
-
// use; keeper / daemon-scoped roles stay CLI-only.
|
|
461
|
-
//
|
|
462
|
-
|
|
545
|
+
// use; keeper / daemon-scoped roles stay CLI-only. A builtin role asked
|
|
546
|
+
// for with a scope is refused the same way, with its own reason: builtin
|
|
547
|
+
// roles are global-only. Denial is forensic-audited (`role_grant_id`
|
|
548
|
+
// omitted — no grant was created).
|
|
549
|
+
const admin_grantable = role_has_grant_path(role_specs, input.role, GRANT_PATH_ADMIN);
|
|
550
|
+
const builtin_scoped = is_scoped_builtin(input.role, input.scope_id);
|
|
551
|
+
if (builtin_scoped || !admin_grantable) {
|
|
463
552
|
audit.emit_role_grant_target(ctx, auth, {
|
|
464
553
|
event_type: 'role_grant_create',
|
|
465
554
|
outcome: 'failure',
|
|
466
555
|
target_account_id: input.to_account_id,
|
|
467
556
|
target_actor_id: input.to_actor_id ?? null,
|
|
468
|
-
metadata: {
|
|
557
|
+
metadata: {
|
|
558
|
+
role: input.role,
|
|
559
|
+
scope_kind: input.scope_kind ?? null,
|
|
560
|
+
scope_id: input.scope_id ?? null
|
|
561
|
+
}
|
|
469
562
|
});
|
|
563
|
+
if (builtin_scoped && admin_grantable)
|
|
564
|
+
throw builtin_scoped_error();
|
|
470
565
|
throw jsonrpc_errors.forbidden('role not web-grantable', {
|
|
471
566
|
reason: ERROR_ROLE_NOT_WEB_GRANTABLE
|
|
472
567
|
});
|
|
@@ -474,9 +569,7 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
474
569
|
// Resolve the grant's target actor. A supplied `to_actor_id` must be an
|
|
475
570
|
// active actor of `to_account_id`; otherwise the account's sole active
|
|
476
571
|
// actor is used (a multi-actor account must name one). A missing /
|
|
477
|
-
// actorless account 404-masks.
|
|
478
|
-
// actor-account-mismatch throw stays in the handler body the
|
|
479
|
-
// error_reasons drift scan reads.
|
|
572
|
+
// actorless account 404-masks.
|
|
480
573
|
const actors = await query_active_actors_by_account(ctx, input.to_account_id);
|
|
481
574
|
let target_actor_id;
|
|
482
575
|
if (input.to_actor_id != null) {
|
|
@@ -497,12 +590,12 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
497
590
|
throw jsonrpc_errors.invalid_params('to_actor_id is required for a multi-actor account');
|
|
498
591
|
}
|
|
499
592
|
// Idempotent write — re-assigning an already-held grant returns the
|
|
500
|
-
// existing row. `scope_kind`
|
|
501
|
-
//
|
|
593
|
+
// existing row. `scope_kind` + `scope_id` land as the pair the input
|
|
594
|
+
// carried. `granted_by` is the assigning admin.
|
|
502
595
|
const grant = await query_create_role_grant(ctx, {
|
|
503
596
|
actor_id: target_actor_id,
|
|
504
597
|
role: input.role,
|
|
505
|
-
scope_kind: null,
|
|
598
|
+
scope_kind: input.scope_kind ?? null,
|
|
506
599
|
scope_id: input.scope_id ?? null,
|
|
507
600
|
granted_by: auth.actor.id
|
|
508
601
|
});
|
|
@@ -513,6 +606,7 @@ export const create_role_grant_offer_actions = (deps, options = {}) => {
|
|
|
513
606
|
metadata: {
|
|
514
607
|
role: grant.role,
|
|
515
608
|
role_grant_id: grant.id,
|
|
609
|
+
scope_kind: input.scope_kind ?? null,
|
|
516
610
|
scope_id: grant.scope_id
|
|
517
611
|
}
|
|
518
612
|
});
|
|
@@ -66,6 +66,35 @@ export declare class RoleGrantOfferSelfTargetError extends Error {
|
|
|
66
66
|
export declare class RoleGrantOfferActorMismatchError extends Error {
|
|
67
67
|
constructor(offer_id: string);
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* Why `query_accept_offer` refused to mint a builtin-role grant.
|
|
71
|
+
*
|
|
72
|
+
* Each is a row no current grant path creates but a database can still hold
|
|
73
|
+
* (a direct `query_role_grant_offer_create` call, or an offer made under a
|
|
74
|
+
* since-closed path), so accept — the chokepoint where an offer becomes a
|
|
75
|
+
* `role_grant` — checks them itself.
|
|
76
|
+
*
|
|
77
|
+
* - `bootstrap_only` — the offer is of `keeper`, which is bootstrap-only:
|
|
78
|
+
* never conferred through an offer, whoever made it.
|
|
79
|
+
* - `scoped` — the offer binds a builtin role to a scope; builtin roles are
|
|
80
|
+
* global-only.
|
|
81
|
+
* - `grantor_not_authorized` — the grantor no longer holds an active
|
|
82
|
+
* **global** `admin` on an active actor of an active account. Conferring a
|
|
83
|
+
* builtin role is global-admin authority, so it is re-checked when the
|
|
84
|
+
* grant is minted, not only when the offer was made.
|
|
85
|
+
*/
|
|
86
|
+
export type BuiltinOfferRefusal = 'bootstrap_only' | 'scoped' | 'grantor_not_authorized';
|
|
87
|
+
/**
|
|
88
|
+
* Error thrown by `query_accept_offer` when the offer confers a builtin role
|
|
89
|
+
* (`admin` / `keeper`) that this accept must not mint — `refusal` names the
|
|
90
|
+
* rule. Carries the locked (still pending, untouched) offer so the caller can
|
|
91
|
+
* audit the refusal with the offer's role and scope.
|
|
92
|
+
*/
|
|
93
|
+
export declare class RoleGrantOfferBuiltinRefusedError extends Error {
|
|
94
|
+
readonly offer: RoleGrantOffer;
|
|
95
|
+
readonly refusal: BuiltinOfferRefusal;
|
|
96
|
+
constructor(offer: RoleGrantOffer, refusal: BuiltinOfferRefusal);
|
|
97
|
+
}
|
|
69
98
|
/**
|
|
70
99
|
* Error thrown when `query_role_grant_offer_create` is called with a
|
|
71
100
|
* `to_actor_id` that does not exist or does not belong to `to_account_id`.
|
|
@@ -224,6 +253,11 @@ export interface AcceptOfferResult {
|
|
|
224
253
|
* - `RoleGrantOfferAlreadyTerminalError` — offer is declined, retracted, or
|
|
225
254
|
* superseded.
|
|
226
255
|
* - `RoleGrantOfferExpiredError` — offer is pending but past `expires_at`.
|
|
256
|
+
* - `RoleGrantOfferActorMismatchError` — actor-targeted offer, different actor.
|
|
257
|
+
* - `RoleGrantOfferBuiltinRefusedError` — a builtin-role offer that must not
|
|
258
|
+
* mint: `keeper` at all, a builtin bound to a scope, or one whose grantor no
|
|
259
|
+
* longer holds an active global `admin` (read under `FOR SHARE`). The offer
|
|
260
|
+
* stays pending.
|
|
227
261
|
*
|
|
228
262
|
* Sibling supersede is what closes the "accept a pre-revoke sibling offer
|
|
229
263
|
* to bypass a revoke" path: once A is accepted, B/C/... can no longer be
|
|
@@ -236,6 +270,8 @@ export interface AcceptOfferResult {
|
|
|
236
270
|
* @throws RoleGrantOfferNotFoundError if the offer is missing or belongs to another recipient
|
|
237
271
|
* @throws RoleGrantOfferAlreadyTerminalError if the offer is declined, retracted, or superseded
|
|
238
272
|
* @throws RoleGrantOfferExpiredError if the offer is pending but past `expires_at`
|
|
273
|
+
* @throws RoleGrantOfferActorMismatchError if the offer is actor-targeted to a different actor
|
|
274
|
+
* @throws RoleGrantOfferBuiltinRefusedError if a builtin-role offer must not mint (see `BuiltinOfferRefusal`)
|
|
239
275
|
* @throws Error if the accepting `actor_id` does not belong to `to_account_id`, or invariant assertions fail
|
|
240
276
|
*/
|
|
241
277
|
export declare const query_accept_offer: (deps: QueryDeps, input: AcceptOfferInput) => Promise<AcceptOfferResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAC;AAEnD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AASrD,OAAO,KAAK,EACX,yBAAyB,EACzB,cAAc,EACd,eAAe,EACf,MAAM,8BAA8B,CAAC;AAEtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"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"}
|