@fuzdev/fuz_app 0.54.0 → 0.55.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/actions/CLAUDE.md +68 -13
- package/dist/actions/action_codegen.d.ts +13 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +15 -1
- package/dist/actions/action_rpc.d.ts +60 -7
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +158 -44
- package/dist/actions/register_action_ws.d.ts +4 -4
- package/dist/actions/register_action_ws.js +6 -6
- package/dist/actions/register_ws_endpoint.d.ts +20 -7
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +30 -5
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +0 -4
- package/dist/auth/CLAUDE.md +219 -66
- package/dist/auth/account_actions.d.ts +6 -6
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +8 -11
- package/dist/auth/account_queries.d.ts +6 -3
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +14 -5
- package/dist/auth/account_routes.d.ts +7 -10
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +70 -23
- package/dist/auth/account_schema.d.ts +19 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +20 -0
- package/dist/auth/admin_action_specs.d.ts +45 -11
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +23 -8
- package/dist/auth/admin_actions.d.ts +8 -7
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +11 -18
- package/dist/auth/audit_log_queries.d.ts +53 -14
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +45 -2
- package/dist/auth/audit_log_schema.d.ts +55 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +19 -3
- package/dist/auth/bearer_auth.d.ts +9 -7
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +13 -21
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +5 -0
- package/dist/auth/daemon_token_middleware.d.ts +23 -11
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +26 -20
- package/dist/auth/deps.d.ts +14 -0
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +4 -2
- package/dist/auth/migrations.d.ts +15 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +15 -7
- package/dist/auth/permit_offer_action_specs.d.ts +45 -6
- package/dist/auth/permit_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/permit_offer_action_specs.js +38 -7
- package/dist/auth/permit_offer_actions.d.ts +2 -2
- package/dist/auth/permit_offer_actions.d.ts.map +1 -1
- package/dist/auth/permit_offer_actions.js +98 -90
- package/dist/auth/permit_offer_notifications.d.ts +10 -0
- package/dist/auth/permit_offer_notifications.d.ts.map +1 -1
- package/dist/auth/permit_offer_queries.d.ts +68 -9
- package/dist/auth/permit_offer_queries.d.ts.map +1 -1
- package/dist/auth/permit_offer_queries.js +147 -35
- package/dist/auth/permit_offer_schema.d.ts +23 -1
- package/dist/auth/permit_offer_schema.d.ts.map +1 -1
- package/dist/auth/permit_offer_schema.js +5 -0
- package/dist/auth/permit_queries.d.ts +17 -5
- package/dist/auth/permit_queries.d.ts.map +1 -1
- package/dist/auth/permit_queries.js +19 -8
- package/dist/auth/request_context.d.ts +321 -38
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +393 -66
- package/dist/auth/route_guards.d.ts +10 -4
- package/dist/auth/route_guards.d.ts.map +1 -1
- package/dist/auth/route_guards.js +14 -8
- package/dist/auth/self_service_role_action_specs.d.ts +2 -0
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +2 -0
- package/dist/auth/self_service_role_actions.d.ts +6 -5
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +18 -8
- package/dist/db/migrate.d.ts +11 -7
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +9 -6
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +5 -3
- package/dist/hono_context.d.ts +77 -0
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +50 -0
- package/dist/http/CLAUDE.md +80 -17
- package/dist/http/error_schemas.d.ts +92 -1
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +73 -16
- package/dist/http/jsonrpc_errors.d.ts +27 -2
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +26 -2
- package/dist/http/route_spec.d.ts +62 -4
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +117 -21
- package/dist/http/schema_helpers.d.ts +13 -1
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +21 -2
- package/dist/http/surface.d.ts +10 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +2 -2
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +11 -1
- package/dist/testing/CLAUDE.md +23 -17
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +15 -13
- package/dist/testing/adversarial_headers.js +1 -1
- package/dist/testing/app_server.js +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +21 -7
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +6 -3
- package/dist/testing/entities.d.ts +2 -1
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +1 -0
- package/dist/testing/integration_helpers.d.ts +4 -2
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +9 -5
- package/dist/testing/middleware.d.ts +12 -8
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +67 -25
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +3 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +5 -1
- package/dist/ui/CLAUDE.md +16 -10
- package/dist/ui/PermitOfferForm.svelte +14 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts +6 -0
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts +8 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +14 -3
- package/dist/ui/permit_offers_state.svelte.d.ts +9 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +1 -1
- package/dist/ui/permit_offers_state.svelte.js +7 -1
- package/package.json +1 -1
package/dist/auth/CLAUDE.md
CHANGED
|
@@ -84,8 +84,10 @@ Design notes:
|
|
|
84
84
|
### Identity entities (`account_schema.ts`)
|
|
85
85
|
|
|
86
86
|
- `Account` (primary identity, holds `password_hash`), `Actor` (the entity
|
|
87
|
-
that acts — owns cells, holds permits, appears in audit trails;
|
|
88
|
-
|
|
87
|
+
that acts — owns cells, holds permits, appears in audit trails; an account
|
|
88
|
+
may host one or more actors, with the dispatcher's authorization phase
|
|
89
|
+
resolving the acting actor per-request via `acting?: ActingActor` on
|
|
90
|
+
inputs), `Permit` (time-bounded, revocable grant of a role to an
|
|
89
91
|
actor — carries `scope_id`, `source_offer_id`, `revoked_reason`),
|
|
90
92
|
`AuthSession` (server-side, keyed by blake3), `ApiToken`.
|
|
91
93
|
- Every `id` / `*_id` field on entity interfaces, `*Json` schemas, and
|
|
@@ -209,6 +211,50 @@ Zod enum; `AuditOutcome` is `'success' | 'failure'`.
|
|
|
209
211
|
`AuditLogListOptions` (supports `since_seq` for SSE reconnection gap fill);
|
|
210
212
|
`AUDIT_LOG_DEFAULT_LIMIT = 50` (default page size, lives on the schema
|
|
211
213
|
side so client codegen can import it without dragging in the query layer).
|
|
214
|
+
`target_actor_id` lives parallel to `target_account_id` on both row
|
|
215
|
+
and input. **Rule** — `target_actor_id` is populated when the event
|
|
216
|
+
subject is bound to a specific actor. Concretely: `permit_revoke`
|
|
217
|
+
and `permit_grant` (admin direct-grant, self-service toggle, and
|
|
218
|
+
in-tx accept all populate both target columns — the grantee is the
|
|
219
|
+
subject regardless of initiator), in-tx `permit_offer_accept` on
|
|
220
|
+
accept, and `permit_offer_decline` always populate both target
|
|
221
|
+
columns (decline joins `from_account_id` into the RETURNING so the
|
|
222
|
+
"both populated → same account" invariant holds uniformly).
|
|
223
|
+
Offer-shape events (`permit_offer_create`, `_expire`, `_retract`,
|
|
224
|
+
`_supersede`) populate `target_actor_id` when the offer was
|
|
225
|
+
actor-targeted at create time (`permit_offer.to_actor_id` set),
|
|
226
|
+
null when the offer was account-grain (any actor on
|
|
227
|
+
`to_account_id` may accept). Account-shape events (login, logout,
|
|
228
|
+
signup, bootstrap, password change, session/token revoke,
|
|
229
|
+
app_settings update, invite events) stay account-grain on both
|
|
230
|
+
`target_actor_id` **and** `actor_id` — the operation is performed
|
|
231
|
+
by the account, and a multi-actor user must be able to log out
|
|
232
|
+
(or change password, or revoke sessions) without first picking an
|
|
233
|
+
acting actor. Permit/admin/offer events keep recording the
|
|
234
|
+
initiator's actor in `actor_id`.
|
|
235
|
+
SSE/WS socket-close keys on `target_account_id ?? account_id`
|
|
236
|
+
(sessions stay account-grain at the routing layer even though
|
|
237
|
+
they bind to a specific actor at request-context resolution time —
|
|
238
|
+
see request_context.ts).
|
|
239
|
+
- **Actor-targetable offers** — `permit_offer.to_actor_id` is the
|
|
240
|
+
optional column that flips an offer from account-grain (null,
|
|
241
|
+
default) to actor-grain (non-null). `query_permit_offer_create`
|
|
242
|
+
validates the actor↔account binding in one SELECT and rejects with
|
|
243
|
+
`PermitOfferActorAccountMismatchError` when the supplied actor isn't
|
|
244
|
+
on `to_account_id`. `query_accept_offer` rejects wrong-actor accepts
|
|
245
|
+
on actor-targeted offers with `PermitOfferActorMismatchError` —
|
|
246
|
+
surfaced to RPC callers as `permit_offer_actor_mismatch`. Closes the
|
|
247
|
+
audit hole where offer-shape events left `target_actor_id` null even
|
|
248
|
+
when the recipient binding was known at offer time.
|
|
249
|
+
- **`emit_permit_target_event` helper** — the canonical entry point
|
|
250
|
+
for permit-shape audit emissions. Takes `(ctx, auth, deps, {event_type,
|
|
251
|
+
target_account_id, target_actor_id, metadata, outcome?})` and lifts
|
|
252
|
+
the `actor_id` / `account_id` / `ip` boilerplate that every
|
|
253
|
+
`permit_*` audit emit site repeats. Use this instead of
|
|
254
|
+
`audit_log_fire_and_forget` for any event populating one of the
|
|
255
|
+
`target_*_id` columns; reach for the lower-level helper only when
|
|
256
|
+
the event is non-permit-shape (e.g., `app_settings_update`,
|
|
257
|
+
bootstrap, signup).
|
|
212
258
|
- Client-safe: `AuditLogEventJson`, `AuditLogEventWithUsernamesJson`,
|
|
213
259
|
`PermitHistoryEventJson`, `AdminSessionJson`.
|
|
214
260
|
- `get_audit_metadata(event)` type-narrows after checking `event_type`.
|
|
@@ -334,7 +380,12 @@ CRUD + listing:
|
|
|
334
380
|
- `query_update_account_password`, `query_delete_account` (cascades to
|
|
335
381
|
actors, permits, sessions, tokens).
|
|
336
382
|
- `query_account_has_any` — used by bootstrap for belt-and-suspenders check.
|
|
337
|
-
- `
|
|
383
|
+
- `query_actors_by_account` — list every actor on an account, ordered
|
|
384
|
+
by `created_at`. Used by `resolve_acting_actor` to pick the unique
|
|
385
|
+
actor on single-actor accounts or surface `actor_required` when the
|
|
386
|
+
account has multiple actors.
|
|
387
|
+
- `query_actor_by_id` — direct lookup by id; preferred when the caller
|
|
388
|
+
already has an actor id in scope.
|
|
338
389
|
- `query_admin_account_list` — composes accounts + actors + active permits +
|
|
339
390
|
pending inbound offers with **four flat queries** instead of N+1. Pending
|
|
340
391
|
offers exclude `message` on purpose (cross-admin visibility). Returns
|
|
@@ -347,8 +398,14 @@ CRUD + listing:
|
|
|
347
398
|
uses `IS NOT DISTINCT FROM` (plain `=` would miss the NULL-scope conflict
|
|
348
399
|
case).
|
|
349
400
|
- `query_permit_find_active_role_for_actor(deps, permit_id, actor_id)` —
|
|
350
|
-
actor-scoped read, so IDOR protection is consistent with revoke.
|
|
351
|
-
`{role}`
|
|
401
|
+
actor-scoped read, so IDOR protection is consistent with revoke.
|
|
402
|
+
Returns `{role, account_id}` (the actor's `account_id` joined in) or
|
|
403
|
+
`null`. The `account_id` flows into the audit envelope's
|
|
404
|
+
`target_account_id` and the SSE/WS socket-close fan-out target —
|
|
405
|
+
collapsing what used to be a second `query_actor_by_id` round-trip in
|
|
406
|
+
the revoke handler into one read closes the small TOCTOU window
|
|
407
|
+
where the actor row could be deleted between the IDOR check and the
|
|
408
|
+
actor lookup.
|
|
352
409
|
- **`query_revoke_permit(deps, permit_id, actor_id, revoked_by, reason?)`** —
|
|
353
410
|
actor-scoped IDOR guard (returns `null` if the permit belongs to a
|
|
354
411
|
different actor). Supersedes pending offers for the revoked permit's
|
|
@@ -375,8 +432,9 @@ CRUD + listing:
|
|
|
375
432
|
active permit at `scope_id` (role-agnostic) and supersedes every pending
|
|
376
433
|
offer at `scope_id` (tuple-matched and orphan, undifferentiated) in the
|
|
377
434
|
caller's transaction. Returns `RevokeForScopeResult = {revoked, superseded_offers}`
|
|
378
|
-
— `revoked` carries `
|
|
379
|
-
`
|
|
435
|
+
— `revoked` carries both `actor_id` (drives `target_actor_id` audit
|
|
436
|
+
envelopes) and `account_id` (drives `target_account_id` for socket-close
|
|
437
|
+
fan-out); `superseded_offers` carries `from_account_id`. Caller emits
|
|
380
438
|
`permit_offer_supersede` audits with `reason: 'scope_destroyed'` and
|
|
381
439
|
`cause_id: <destroyed scope row id>` per superseded offer (the cause is
|
|
382
440
|
the scope deletion, not any individual permit revoke). Use from a
|
|
@@ -389,9 +447,12 @@ CRUD + listing:
|
|
|
389
447
|
Error classes (all extend `Error` with stable `.name` — never use
|
|
390
448
|
`instanceof` against plain messages):
|
|
391
449
|
|
|
392
|
-
- `PermitOfferSelfTargetError` — grantor offered themselves. Enforced
|
|
393
|
-
|
|
394
|
-
|
|
450
|
+
- `PermitOfferSelfTargetError` — grantor offered themselves. Enforced
|
|
451
|
+
via a single SELECT on the grantor's `actor.account_id` in
|
|
452
|
+
`query_permit_offer_create` (resolving from the grantor side keeps
|
|
453
|
+
the check multi-actor-correct — the grantor → account binding stays
|
|
454
|
+
1:1 by definition of `actor`, while the recipient account may host
|
|
455
|
+
many actors under multi-actor).
|
|
395
456
|
- `PermitOfferAlreadyTerminalError` — offer exists for the caller but is
|
|
396
457
|
accepted / declined / retracted / superseded.
|
|
397
458
|
- `PermitOfferExpiredError` — pending but past `expires_at` (distinct from
|
|
@@ -515,19 +576,24 @@ run'` if the seed somehow missed (defensive — migrations always seed).
|
|
|
515
576
|
- `query_audit_log_list(deps, options?)` — supports `event_type`,
|
|
516
577
|
`event_type_in`, `account_id` (matches `account_id` OR
|
|
517
578
|
`target_account_id`), `outcome`, `since_seq`, `limit`, `offset`.
|
|
518
|
-
|
|
579
|
+
`target_actor_id` filtering is not yet exposed; will land alongside
|
|
580
|
+
the admin-viewer's actor-grain forensics pass.
|
|
581
|
+
- `query_audit_log_list_with_usernames` — joins twice to `account`
|
|
582
|
+
(chains `target_account_id` for the `target_username` field).
|
|
583
|
+
`target_actor_id` is on the row but not currently joined to actor
|
|
584
|
+
for a name; the admin viewer will resolve via `actor_lookup` /
|
|
585
|
+
`actor.name` when the actor-grain forensics pass lands.
|
|
519
586
|
- `query_audit_log_list_for_account`, `query_audit_log_list_permit_history`
|
|
520
587
|
(filters to `permit_grant` / `permit_revoke`).
|
|
521
588
|
- `query_audit_log_cleanup_before`.
|
|
522
589
|
- **`audit_log_fire_and_forget(route, input, deps)`** —
|
|
523
590
|
writes to `route.background_db` (pool-level), so audit entries persist
|
|
524
|
-
even when the request transaction rolls back. `deps` is
|
|
525
|
-
`
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
`BUILTIN_AUDIT_LOG_CONFIG`. Write and `on_audit_event` callback
|
|
591
|
+
even when the request transaction rolls back. `deps` is the shared
|
|
592
|
+
`AuditEmitDeps` bundle (`{log, on_audit_event, audit_log_config?}`)
|
|
593
|
+
from `auth/deps.ts`, so call sites pass the surrounding deps object
|
|
594
|
+
directly. Bundling replaces the prior 5-arg positional signature;
|
|
595
|
+
consumers that forgot the trailing `config` would silently fall back
|
|
596
|
+
to `BUILTIN_AUDIT_LOG_CONFIG`. Write and `on_audit_event` callback
|
|
531
597
|
failures are logged separately. Pushes onto `route.pending_effects`
|
|
532
598
|
for test flushing.
|
|
533
599
|
|
|
@@ -566,11 +632,14 @@ by `sequence`, then enforces:
|
|
|
566
632
|
3. **Run the pending tail** (`code[applied.length..]`) inside a single
|
|
567
633
|
chain transaction; each `INSERT` uses `sequence = max(sequence) + 1`.
|
|
568
634
|
|
|
569
|
-
**
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
635
|
+
**Schema is not stabilized yet — append-only is NOT the rule today.**
|
|
636
|
+
While fuz_app is pre-stable, migration bodies, names, and positions can
|
|
637
|
+
change freely between versions and consumers upgrading across a schema
|
|
638
|
+
change are expected to drop and re-bootstrap their dev/test databases.
|
|
639
|
+
Once the schema is declared stable, a hard append-only-after-publish rule
|
|
640
|
+
will apply (with the cliff called out in that release's notes). Until
|
|
641
|
+
then bias toward editing the existing migration entries rather than
|
|
642
|
+
appending patch migrations.
|
|
574
643
|
|
|
575
644
|
`MigrationError` is the only error class thrown from `run_migrations` /
|
|
576
645
|
`baseline`; branch on `.kind` (never on message text). Kinds:
|
|
@@ -637,47 +706,109 @@ consciously violate the contract.
|
|
|
637
706
|
|
|
638
707
|
## Middleware
|
|
639
708
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
**
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
709
|
+
See the root `../../../CLAUDE.md` §Middleware Ordering for the canonical
|
|
710
|
+
assembly order. Two-phase identity:
|
|
711
|
+
|
|
712
|
+
- **Authentication** runs in middleware (session / bearer / daemon
|
|
713
|
+
token). Sets `c.var.account_id` + `CREDENTIAL_TYPE_KEY` on a valid
|
|
714
|
+
credential. Account-only — never loads actor or permits, never
|
|
715
|
+
populates `REQUEST_CONTEXT_KEY`. **Production-middleware invariant**:
|
|
716
|
+
no production middleware on the auth path (session / bearer / daemon
|
|
717
|
+
token) populates `REQUEST_CONTEXT_KEY`; identity-related context vars
|
|
718
|
+
it does set are `ACCOUNT_ID_KEY`, `CREDENTIAL_TYPE_KEY`, and (for
|
|
719
|
+
sessions / bearer) `AUTH_SESSION_TOKEN_HASH_KEY` /
|
|
720
|
+
`AUTH_API_TOKEN_ID_KEY`. Other middleware (proxy, app server,
|
|
721
|
+
session-cookie parser) sets unrelated vars like `client_ip`,
|
|
722
|
+
`pending_effects`, and the session-token slot keyed by
|
|
723
|
+
`session_options.context_key` (default `auth_session_id`) — those
|
|
724
|
+
are out of scope for this invariant. Test harnesses pre-populate
|
|
725
|
+
`REQUEST_CONTEXT_KEY` + `TEST_CONTEXT_PRESET_KEY` to bypass DB-backed
|
|
726
|
+
actor resolution; production code that consults
|
|
727
|
+
`REQUEST_CONTEXT_KEY` is reading test escape-hatch state, never live
|
|
728
|
+
middleware output.
|
|
729
|
+
- **Authorization** runs in the route-spec wrapper / RPC dispatcher
|
|
730
|
+
before input validation (matches the RPC dispatcher's order so 401 /
|
|
731
|
+
403 surface ahead of `invalid_params`). When the route's input
|
|
732
|
+
declares `acting?: ActingActor` or its auth requires permits
|
|
733
|
+
(`role` / `keeper`), the authorization phase calls
|
|
734
|
+
`resolve_acting_actor` over the raw `acting` value extracted from
|
|
735
|
+
query (GET) or pre-parsed body (mutating methods), builds the
|
|
736
|
+
actor-bound `RequestContext`, and sets `REQUEST_CONTEXT_KEY` before
|
|
737
|
+
the role / keeper guards fire. Account-grain routes skip resolution
|
|
738
|
+
and run with `RequestContext.actor: null`. Resolution failures come
|
|
739
|
+
back as `AuthorizationFailure` (`{status, body}`) — the auth domain
|
|
740
|
+
stops short of constructing a `Response` so each transport binds the
|
|
741
|
+
same failure to its wire shape: REST emits `c.json(body, status)`;
|
|
742
|
+
the WS upgrade does the same; the RPC dispatcher folds it into a
|
|
743
|
+
JSON-RPC envelope (`{jsonrpc, id, error: {code, message, data}}`)
|
|
744
|
+
with `error.message` carrying the reason string and
|
|
745
|
+
`error.data: {reason, ...rest}` flattening any diagnostic fields
|
|
746
|
+
(e.g. `available[]` for `actor_required`). The two 500 reasons the
|
|
747
|
+
phase emits are kept distinct: `no_actors_on_account` names a signup
|
|
748
|
+
invariant violation (`resolve_acting_actor` enumerated zero actors);
|
|
749
|
+
`account_vanished` names a torn-read race (`build_request_context` /
|
|
750
|
+
`build_account_context` returned null after a successful resolve —
|
|
751
|
+
the account or actor row was deleted between credential validation
|
|
752
|
+
and the dispatcher's follow-up read). See the root
|
|
753
|
+
`../../../CLAUDE.md` § Cleanest architecture takes priority for the
|
|
754
|
+
rationale.
|
|
755
|
+
|
|
756
|
+
Session parsing is separate from auth enforcement — login / bootstrap
|
|
757
|
+
participate in cookie refresh without being blocked. `require_auth` /
|
|
758
|
+
`require_role` / `require_keeper` are the gates.
|
|
648
759
|
|
|
649
760
|
### `request_context.ts`
|
|
650
761
|
|
|
651
|
-
- `RequestContext = {account, actor, permits}`.
|
|
762
|
+
- `RequestContext = {account, actor: Actor | null, permits}`. `actor`
|
|
763
|
+
is null on account-grain routes (no `acting`, no permit-requiring
|
|
764
|
+
auth); `permits` is empty in that case.
|
|
652
765
|
- `REQUEST_CONTEXT_KEY` — Hono context variable name.
|
|
653
766
|
- **`AUTH_SESSION_TOKEN_HASH_KEY`** — holds the blake3 session hash. Set on
|
|
654
767
|
successful session lookup; `null` for unauthenticated or non-session
|
|
655
768
|
credentials. Exposed so SSE endpoints can scope per-session resource
|
|
656
769
|
identity (the audit-log SSE uses this to close only the revoked session's
|
|
657
770
|
stream on `session_revoke`).
|
|
658
|
-
- `get_request_context(c)`, `require_request_context(c)` (throws on
|
|
659
|
-
—
|
|
771
|
+
- `get_request_context(c)`, `require_request_context(c)` (throws on
|
|
772
|
+
misuse — handler ran without authorization phase wiring).
|
|
660
773
|
- **In-memory permit predicates** — `has_role(ctx, role, now?)`,
|
|
661
774
|
`has_scoped_role(ctx, role, scope_id, now?)`,
|
|
662
775
|
`has_any_scoped_role(ctx, roles, scope_id, now?)`. All three take
|
|
663
|
-
`RequestContext | null`
|
|
664
|
-
`
|
|
665
|
-
|
|
666
|
-
`
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
`account
|
|
776
|
+
`RequestContext | null` and return `false` for null ctx and for
|
|
777
|
+
account-grain ctx (`actor: null`, empty `permits`); they drop into
|
|
778
|
+
`auth: 'public'` and account-grain handlers without a manual narrow.
|
|
779
|
+
`scope_id === null` matches global permits only; UUID matches that
|
|
780
|
+
exact scope. Empty `roles` short-circuits `has_any_scoped_role` to
|
|
781
|
+
`false`. Decide-time predicates only — the predicate / mutation
|
|
782
|
+
race window is the same as the SQL `query_permit_has_role` style and
|
|
783
|
+
only a transactional re-check inside the UPDATE/INSERT closes it.
|
|
784
|
+
- `build_request_context(deps, account_id, actor_id)` — loads
|
|
785
|
+
`account` + the named `actor` + active permits. Verifies
|
|
786
|
+
`actor.account_id === account.id`; returns `null` when the account
|
|
787
|
+
or actor is missing, or when they don't bind to each other. Called
|
|
788
|
+
by the authorization phase after `resolve_acting_actor` succeeds —
|
|
789
|
+
a null return there is a torn read (account/actor deleted mid-request)
|
|
790
|
+
rather than the missing-actor invariant `resolve_acting_actor` would
|
|
791
|
+
have caught upstream, so the phase surfaces `ERROR_ACCOUNT_VANISHED`
|
|
792
|
+
on null. Not called from middleware.
|
|
793
|
+
- `resolve_acting_actor(deps, account_id, acting_actor_id)` — uniform
|
|
794
|
+
resolver. Resolves to `{ok: true, actor_id}` for 1 actor (any
|
|
795
|
+
`acting`) or matching supplied id; `actor_required` with the
|
|
796
|
+
available list when multi-actor and `acting` is missing;
|
|
797
|
+
`actor_not_on_account` when supplied id doesn't belong; `no_actors`
|
|
798
|
+
defensively.
|
|
673
799
|
- `refresh_permits(ctx, deps)` — reloads permits without mutating the
|
|
674
|
-
original (concurrent-safe). Useful for long-lived WebSocket
|
|
800
|
+
original (concurrent-safe). Useful for long-lived WebSocket
|
|
801
|
+
connections that have an acting actor.
|
|
675
802
|
- `create_request_context_middleware(deps, log, session_context_key?)` —
|
|
676
|
-
|
|
677
|
-
`CREDENTIAL_TYPE_KEY = 'session'
|
|
678
|
-
-
|
|
679
|
-
- `
|
|
680
|
-
|
|
803
|
+
validates the session and sets `c.var.account_id` +
|
|
804
|
+
`CREDENTIAL_TYPE_KEY = 'session'` + `AUTH_SESSION_TOKEN_HASH_KEY`.
|
|
805
|
+
Touches the session fire-and-forget. Does not load actor / permits.
|
|
806
|
+
- `require_auth` — 401 (`ERROR_AUTHENTICATION_REQUIRED`) when
|
|
807
|
+
`account_id` is null. Does not require an acting actor.
|
|
808
|
+
- `require_role(role)` — 401 on no auth, 403
|
|
809
|
+
(`ERROR_INSUFFICIENT_PERMISSIONS` + `required_role`) when permits
|
|
810
|
+
don't carry the role. Implies the authorization phase ran (a
|
|
811
|
+
role-gated route always resolves an actor).
|
|
681
812
|
|
|
682
813
|
### `bearer_auth.ts`
|
|
683
814
|
|
|
@@ -951,7 +1082,7 @@ Closure state:
|
|
|
951
1082
|
`all_admin_action_specs: Array<RequestResponseActionSpec>` — codegen-ready
|
|
952
1083
|
registry of all eleven specs (always includes the two app-settings specs).
|
|
953
1084
|
|
|
954
|
-
Deps: `AdminActionDeps = Pick<
|
|
1085
|
+
Deps: `AdminActionDeps = AuditEmitDeps` — the shared `Pick<AppDeps, 'log' | 'on_audit_event' | 'audit_log_config'>` slice every audit-emitting site picks (defined in `auth/deps.ts`). The `audit_log_config` slot flows through to `audit_log_fire_and_forget` so consumer-extended event-type metadata gets validated.
|
|
955
1086
|
|
|
956
1087
|
### `permit_offer_action_specs.ts` + `permit_offer_actions.ts` — seven RPC actions
|
|
957
1088
|
|
|
@@ -986,15 +1117,19 @@ Six offer-lifecycle methods plus `permit_revoke`. Authorization is a mix:
|
|
|
986
1117
|
**`actor_id`, not `account_id`** — permits are actor-scoped and deriving
|
|
987
1118
|
actor from account collapses under multi-actor accounts.
|
|
988
1119
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
|
994
|
-
|
|
|
995
|
-
| `
|
|
996
|
-
| `
|
|
997
|
-
| `
|
|
1120
|
+
Every input row below also carries the shared `acting?: ActingActor`
|
|
1121
|
+
field that the dispatcher's authorization phase reads off the raw
|
|
1122
|
+
params (omitted from the table for brevity).
|
|
1123
|
+
|
|
1124
|
+
| Spec | Input | Output |
|
|
1125
|
+
| ---------------------------------- | ---------------------------------------------------------- | ------------------------------------------ |
|
|
1126
|
+
| `permit_offer_create_action_spec` | `{to_account_id, to_actor_id?, role, scope_id?, message?}` | `{offer}` |
|
|
1127
|
+
| `permit_offer_accept_action_spec` | `{offer_id}` | `{permit_id, offer, superseded_offer_ids}` |
|
|
1128
|
+
| `permit_offer_decline_action_spec` | `{offer_id, reason?}` | `{ok}` |
|
|
1129
|
+
| `permit_offer_retract_action_spec` | `{offer_id}` | `{ok}` |
|
|
1130
|
+
| `permit_offer_list_action_spec` | `{account_id?}` | `{offers}` |
|
|
1131
|
+
| `permit_offer_history_action_spec` | `{account_id?, limit?, offset?}` | `{offers}` |
|
|
1132
|
+
| `permit_revoke_action_spec` | `{actor_id, permit_id, reason?}` | `{ok, revoked}` |
|
|
998
1133
|
|
|
999
1134
|
Error reason constants (exported as `as const` literals):
|
|
1000
1135
|
|
|
@@ -1004,6 +1139,11 @@ Error reason constants (exported as `as const` literals):
|
|
|
1004
1139
|
- `ERROR_OFFER_NOT_FOUND` (`'offer_not_found'` — 404-over-403 IDOR mask)
|
|
1005
1140
|
- `ERROR_OFFER_ROLE_NOT_GRANTABLE` (`'offer_role_not_grantable'`)
|
|
1006
1141
|
- `ERROR_OFFER_NOT_AUTHORIZED` (`'offer_not_authorized'`)
|
|
1142
|
+
- `ERROR_OFFER_ACTOR_ACCOUNT_MISMATCH` (`'offer_actor_account_mismatch'` —
|
|
1143
|
+
`permit_offer_create` was called with a `to_actor_id` that does not
|
|
1144
|
+
belong to `to_account_id`)
|
|
1145
|
+
- `ERROR_OFFER_ACTOR_MISMATCH` (`'offer_actor_mismatch'` —
|
|
1146
|
+
actor-targeted offer was accepted by an actor other than `to_actor_id`)
|
|
1007
1147
|
|
|
1008
1148
|
Plus re-uses from `../http/error_schemas.ts`: `ERROR_PERMIT_NOT_FOUND`,
|
|
1009
1149
|
`ERROR_ROLE_NOT_WEB_GRANTABLE`, `ERROR_INSUFFICIENT_PERMISSIONS`,
|
|
@@ -1020,11 +1160,18 @@ Failure-outcome audit events emitted (success and failure rows both carry
|
|
|
1020
1160
|
`ip: ctx.client_ip` — uniform with the admin and self-service surfaces):
|
|
1021
1161
|
|
|
1022
1162
|
- `permit_offer_create` failure — `web_grantable` denial, `authorize`
|
|
1023
|
-
denial, self-target rejection
|
|
1024
|
-
audit row
|
|
1163
|
+
denial, self-target rejection, and actor-account mismatch all emit
|
|
1164
|
+
the same audit row via `emit_create_failure_audit`. `target_account_id`
|
|
1165
|
+
carries `input.to_account_id`; `target_actor_id` echoes
|
|
1166
|
+
`input.to_actor_id` when supplied so failure rows match the
|
|
1167
|
+
success-shape envelope of actor-targeted offers (null on
|
|
1168
|
+
account-grain offers — see audit_log_schema rule).
|
|
1025
1169
|
- `permit_revoke` failure — `web_grantable` denial after IDOR / role
|
|
1026
1170
|
lookup succeeded. The admin-role-denied path (pre-IDOR) emits no audit,
|
|
1027
|
-
matching the middleware auth-guard precedent.
|
|
1171
|
+
matching the middleware auth-guard precedent. `target_account_id` +
|
|
1172
|
+
`target_actor_id` both populated (the IDOR-passing branch resolves
|
|
1173
|
+
the target actor before the gate; the subject is an actor-bound
|
|
1174
|
+
permit).
|
|
1028
1175
|
|
|
1029
1176
|
WS notifications (post-commit via `emit_after_commit` from
|
|
1030
1177
|
`../http/pending_effects.js` — swallows exceptions so one failed send
|
|
@@ -1038,7 +1185,7 @@ can't starve others; see `../http/CLAUDE.md` §Pending Effects):
|
|
|
1038
1185
|
- Revoke → `permit_revoke` to revokee + one `permit_offer_supersede` per
|
|
1039
1186
|
superseded sibling.
|
|
1040
1187
|
|
|
1041
|
-
Deps: `PermitOfferActionDeps extends
|
|
1188
|
+
Deps: `PermitOfferActionDeps extends AuditEmitDeps & {notification_sender?: NotificationSender | null}`.
|
|
1042
1189
|
Notification sender is optional — when absent, WS fan-out is silently
|
|
1043
1190
|
skipped (DB-only side effects still happen).
|
|
1044
1191
|
|
|
@@ -1141,7 +1288,7 @@ Audit events emitted (via `audit_log_fire_and_forget` with `ip: ctx.client_ip`):
|
|
|
1141
1288
|
IP is the resolved trusted-proxy value from `ActionContext.client_ip`,
|
|
1142
1289
|
matching the REST handler convention.
|
|
1143
1290
|
|
|
1144
|
-
Deps: `AccountActionDeps =
|
|
1291
|
+
Deps: `AccountActionDeps = AuditEmitDeps`.
|
|
1145
1292
|
Options: `{max_tokens?: number | null}` — defaults to `DEFAULT_MAX_TOKENS`
|
|
1146
1293
|
from `account_routes.ts`; `null` disables the cap.
|
|
1147
1294
|
|
|
@@ -1195,7 +1342,7 @@ roundtrip — then `query_grant_permit` for the actual insert. Revoke branch fil
|
|
|
1195
1342
|
`create_standard_rpc_actions` — `eligible_roles` is app-specific, opt-in,
|
|
1196
1343
|
spread alongside the standard bundle when needed.
|
|
1197
1344
|
|
|
1198
|
-
Deps: `SelfServiceRoleActionDeps =
|
|
1345
|
+
Deps: `SelfServiceRoleActionDeps = AuditEmitDeps`.
|
|
1199
1346
|
|
|
1200
1347
|
`all_self_service_role_action_specs: ReadonlyArray<RequestResponseActionSpec>` —
|
|
1201
1348
|
codegen-ready registry of the single unified spec.
|
|
@@ -1245,6 +1392,12 @@ resulting permit.
|
|
|
1245
1392
|
- **`RouteFactoryDeps = Omit<AppDeps, 'db'>`** — for route factories. Route
|
|
1246
1393
|
handlers receive DB access via `RouteContext`, so factories don't capture
|
|
1247
1394
|
a pool-level `Db`.
|
|
1395
|
+
- **`AuditEmitDeps = Pick<AppDeps, 'log' | 'on_audit_event' | 'audit_log_config'>`**
|
|
1396
|
+
— the slice every audit-emitting site needs. Used by `audit_log_fire_and_forget`
|
|
1397
|
+
/ `emit_permit_target_event` (the primitives) and aliased by every
|
|
1398
|
+
action-factory deps type (`AdminActionDeps`, `AccountActionDeps`,
|
|
1399
|
+
`PermitOfferActionDeps`, `SelfServiceRoleActionDeps`) so the five
|
|
1400
|
+
factories stop spelling the same `Pick` independently.
|
|
1248
1401
|
|
|
1249
1402
|
See root `../../../CLAUDE.md` §AppDeps Vocabulary for the
|
|
1250
1403
|
capability / options / runtime-state split across the whole project.
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* @module
|
|
23
23
|
*/
|
|
24
24
|
import { type RpcAction } from '../actions/action_rpc.js';
|
|
25
|
-
import type {
|
|
25
|
+
import type { AuditEmitDeps } from './deps.js';
|
|
26
26
|
/** Options for `create_account_actions`. */
|
|
27
27
|
export interface AccountActionOptions {
|
|
28
28
|
/**
|
|
@@ -36,12 +36,12 @@ export interface AccountActionOptions {
|
|
|
36
36
|
/**
|
|
37
37
|
* Dependencies for `create_account_actions`.
|
|
38
38
|
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* Aliases the shared `AuditEmitDeps` (the `log` / `on_audit_event` /
|
|
40
|
+
* optional `audit_log_config` slice every audit-emitting site picks).
|
|
41
|
+
* `audit_log_config` is consumed by `audit_log_fire_and_forget`; absent →
|
|
42
|
+
* defaults to `BUILTIN_AUDIT_LOG_CONFIG`.
|
|
43
43
|
*/
|
|
44
|
-
export type AccountActionDeps =
|
|
44
|
+
export type AccountActionDeps = AuditEmitDeps;
|
|
45
45
|
/**
|
|
46
46
|
* Create the self-service account RPC actions.
|
|
47
47
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAgBxF,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"account_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAgBxF,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAyB7C,4CAA4C;AAC5C,MAAM,WAAW,oBAAoB;IACpC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,iBAAiB,GAAG,aAAa,CAAC;AAE9C;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,iBAAiB,EACvB,UAAS,oBAAyB,KAChC,KAAK,CAAC,SAAS,CAqHjB,CAAC"}
|
|
@@ -28,6 +28,7 @@ import { query_api_token_enforce_limit, query_api_token_list_for_account, query_
|
|
|
28
28
|
import { generate_api_token } from './api_token.js';
|
|
29
29
|
import { audit_log_fire_and_forget } from './audit_log_queries.js';
|
|
30
30
|
import { DEFAULT_MAX_TOKENS } from './account_routes.js';
|
|
31
|
+
import { require_request_auth } from './request_context.js';
|
|
31
32
|
import { account_verify_action_spec, account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from './account_action_specs.js';
|
|
32
33
|
/**
|
|
33
34
|
* Create the self-service account RPC actions.
|
|
@@ -39,21 +40,20 @@ import { account_verify_action_spec, account_session_list_action_spec, account_s
|
|
|
39
40
|
export const create_account_actions = (deps, options = {}) => {
|
|
40
41
|
const { max_tokens = DEFAULT_MAX_TOKENS } = options;
|
|
41
42
|
const verify_handler = (_input, ctx) => {
|
|
42
|
-
const auth = ctx.auth;
|
|
43
|
+
const auth = require_request_auth(ctx.auth);
|
|
43
44
|
return to_session_account(auth.account);
|
|
44
45
|
};
|
|
45
46
|
const session_list_handler = async (_input, ctx) => {
|
|
46
|
-
const auth = ctx.auth;
|
|
47
|
+
const auth = require_request_auth(ctx.auth);
|
|
47
48
|
const sessions = await query_session_list_for_account(ctx, auth.account.id);
|
|
48
49
|
return { sessions };
|
|
49
50
|
};
|
|
50
51
|
const session_revoke_handler = async (input, ctx) => {
|
|
51
|
-
const auth = ctx.auth;
|
|
52
|
+
const auth = require_request_auth(ctx.auth);
|
|
52
53
|
const revoked = await query_session_revoke_for_account(ctx, input.session_id, auth.account.id);
|
|
53
54
|
void audit_log_fire_and_forget(ctx, {
|
|
54
55
|
event_type: 'session_revoke',
|
|
55
56
|
outcome: revoked ? 'success' : 'failure',
|
|
56
|
-
actor_id: auth.actor.id,
|
|
57
57
|
account_id: auth.account.id,
|
|
58
58
|
ip: ctx.client_ip,
|
|
59
59
|
metadata: { session_id: input.session_id },
|
|
@@ -61,11 +61,10 @@ export const create_account_actions = (deps, options = {}) => {
|
|
|
61
61
|
return { ok: true, revoked };
|
|
62
62
|
};
|
|
63
63
|
const session_revoke_all_handler = async (_input, ctx) => {
|
|
64
|
-
const auth = ctx.auth;
|
|
64
|
+
const auth = require_request_auth(ctx.auth);
|
|
65
65
|
const count = await query_session_revoke_all_for_account(ctx, auth.account.id);
|
|
66
66
|
void audit_log_fire_and_forget(ctx, {
|
|
67
67
|
event_type: 'session_revoke_all',
|
|
68
|
-
actor_id: auth.actor.id,
|
|
69
68
|
account_id: auth.account.id,
|
|
70
69
|
ip: ctx.client_ip,
|
|
71
70
|
metadata: { count },
|
|
@@ -73,7 +72,7 @@ export const create_account_actions = (deps, options = {}) => {
|
|
|
73
72
|
return { ok: true, count };
|
|
74
73
|
};
|
|
75
74
|
const token_create_handler = async (input, ctx) => {
|
|
76
|
-
const auth = ctx.auth;
|
|
75
|
+
const auth = require_request_auth(ctx.auth);
|
|
77
76
|
const { token, id, token_hash } = generate_api_token();
|
|
78
77
|
await query_create_api_token(ctx, id, auth.account.id, input.name, token_hash);
|
|
79
78
|
if (max_tokens != null) {
|
|
@@ -81,7 +80,6 @@ export const create_account_actions = (deps, options = {}) => {
|
|
|
81
80
|
}
|
|
82
81
|
void audit_log_fire_and_forget(ctx, {
|
|
83
82
|
event_type: 'token_create',
|
|
84
|
-
actor_id: auth.actor.id,
|
|
85
83
|
account_id: auth.account.id,
|
|
86
84
|
ip: ctx.client_ip,
|
|
87
85
|
metadata: { token_id: id, name: input.name },
|
|
@@ -89,17 +87,16 @@ export const create_account_actions = (deps, options = {}) => {
|
|
|
89
87
|
return { ok: true, token, id, name: input.name };
|
|
90
88
|
};
|
|
91
89
|
const token_list_handler = async (_input, ctx) => {
|
|
92
|
-
const auth = ctx.auth;
|
|
90
|
+
const auth = require_request_auth(ctx.auth);
|
|
93
91
|
const tokens = await query_api_token_list_for_account(ctx, auth.account.id);
|
|
94
92
|
return { tokens };
|
|
95
93
|
};
|
|
96
94
|
const token_revoke_handler = async (input, ctx) => {
|
|
97
|
-
const auth = ctx.auth;
|
|
95
|
+
const auth = require_request_auth(ctx.auth);
|
|
98
96
|
const revoked = await query_revoke_api_token_for_account(ctx, input.token_id, auth.account.id);
|
|
99
97
|
void audit_log_fire_and_forget(ctx, {
|
|
100
98
|
event_type: 'token_revoke',
|
|
101
99
|
outcome: revoked ? 'success' : 'failure',
|
|
102
|
-
actor_id: auth.actor.id,
|
|
103
100
|
account_id: auth.account.id,
|
|
104
101
|
ip: ctx.client_ip,
|
|
105
102
|
metadata: { token_id: input.token_id },
|
|
@@ -68,11 +68,14 @@ export declare const query_account_has_any: (deps: QueryDeps) => Promise<boolean
|
|
|
68
68
|
*/
|
|
69
69
|
export declare const query_create_actor: (deps: QueryDeps, account_id: string, name: string) => Promise<Actor>;
|
|
70
70
|
/**
|
|
71
|
-
*
|
|
71
|
+
* List every actor on an account, ordered by `created_at`.
|
|
72
72
|
*
|
|
73
|
-
*
|
|
73
|
+
* Used by `resolve_acting_actor` to resolve the acting actor for a
|
|
74
|
+
* request: 1 actor picks transparently, multiple require an explicit
|
|
75
|
+
* `acting` field on the request payload. For lookups by id, use
|
|
76
|
+
* `query_actor_by_id` instead.
|
|
74
77
|
*/
|
|
75
|
-
export declare const
|
|
78
|
+
export declare const query_actors_by_account: (deps: QueryDeps, account_id: string) => Promise<Array<Actor>>;
|
|
76
79
|
/**
|
|
77
80
|
* Find an actor by id.
|
|
78
81
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,OAAO,kBAAkB,KACvB,OAAO,CAAC,OAAO,CAQjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,SAAS,EACf,IAAI,MAAM,KACR,OAAO,CAAC,OAAO,GAAG,SAAS,CAE7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,OAAO,GAAG,SAAS,CAI7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,OAAO,MAAM,KACX,OAAO,CAAC,OAAO,GAAG,SAAS,CAI7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,SAAS,EACf,OAAO,MAAM,KACX,OAAO,CAAC,OAAO,GAAG,SAAS,CAS7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,IAAI,MAAM,EACV,eAAe,MAAM,EACrB,YAAY,MAAM,GAAG,IAAI,KACvB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAU,MAAM,SAAS,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,OAAO,CAKvF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,OAAO,CAK5E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,MAAM,MAAM,KACV,OAAO,CAAC,KAAK,CAMf,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"account_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/account_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,EAEN,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,GAChC,MAAM,SAAS,EACf,OAAO,kBAAkB,KACvB,OAAO,CAAC,OAAO,CAQjB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,SAAS,EACf,IAAI,MAAM,KACR,OAAO,CAAC,OAAO,GAAG,SAAS,CAE7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,OAAO,GAAG,SAAS,CAI7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,SAAS,EACf,OAAO,MAAM,KACX,OAAO,CAAC,OAAO,GAAG,SAAS,CAI7B,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,SAAS,EACf,OAAO,MAAM,KACX,OAAO,CAAC,OAAO,GAAG,SAAS,CAS7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,6BAA6B,GACzC,MAAM,SAAS,EACf,IAAI,MAAM,EACV,eAAe,MAAM,EACrB,YAAY,MAAM,GAAG,IAAI,KACvB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAU,MAAM,SAAS,EAAE,IAAI,MAAM,KAAG,OAAO,CAAC,OAAO,CAKvF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,MAAM,SAAS,KAAG,OAAO,CAAC,OAAO,CAK5E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,MAAM,MAAM,KACV,OAAO,CAAC,KAAK,CAMf,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,YAAY,MAAM,KAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAKtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,IAAI,MAAM,KACR,OAAO,CAAC,KAAK,GAAG,SAAS,CAE3B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,SAAS,EACf,OAAO,kBAAkB,KACvB,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,KAAK,CAAA;CAAC,CAI1C,CAAC;AAyBF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,SAAS,KACb,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAqFtC,CAAC"}
|
|
@@ -101,12 +101,15 @@ export const query_create_actor = async (deps, account_id, name) => {
|
|
|
101
101
|
return assert_row(row, 'INSERT INTO actor');
|
|
102
102
|
};
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* List every actor on an account, ordered by `created_at`.
|
|
105
105
|
*
|
|
106
|
-
*
|
|
106
|
+
* Used by `resolve_acting_actor` to resolve the acting actor for a
|
|
107
|
+
* request: 1 actor picks transparently, multiple require an explicit
|
|
108
|
+
* `acting` field on the request payload. For lookups by id, use
|
|
109
|
+
* `query_actor_by_id` instead.
|
|
107
110
|
*/
|
|
108
|
-
export const
|
|
109
|
-
return deps.db.
|
|
111
|
+
export const query_actors_by_account = async (deps, account_id) => {
|
|
112
|
+
return deps.db.query(`SELECT * FROM actor WHERE account_id = $1 ORDER BY created_at ASC, id ASC`, [account_id]);
|
|
110
113
|
};
|
|
111
114
|
/**
|
|
112
115
|
* Find an actor by id.
|
|
@@ -161,7 +164,13 @@ export const query_admin_account_list = async (deps) => {
|
|
|
161
164
|
AND po.expires_at > NOW()
|
|
162
165
|
ORDER BY po.expires_at ASC`),
|
|
163
166
|
]);
|
|
164
|
-
// Index actors by account_id
|
|
167
|
+
// Index actors by account_id. Multi-actor TODO: this Map keyed by
|
|
168
|
+
// account_id silently overwrites earlier actors when an account
|
|
169
|
+
// hosts more than one — when multi-actor lands, the admin row shape
|
|
170
|
+
// must change from "account → one actor" to "account → Array<Actor>"
|
|
171
|
+
// (or split into a separate per-actor row). The JSON shape change
|
|
172
|
+
// will ripple into the admin UI; bundle that with the multi-actor
|
|
173
|
+
// session-actor-selector work.
|
|
165
174
|
const actor_by_account = new Map();
|
|
166
175
|
for (const actor of actors) {
|
|
167
176
|
actor_by_account.set(actor.account_id, actor);
|