@fuzdev/fuz_app 0.53.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 +230 -63
- 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 +106 -95
- 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 +360 -32
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +442 -60
- 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 +32 -19
- 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/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +12 -0
- 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
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auth guard resolver for the route spec system.
|
|
3
3
|
*
|
|
4
|
-
* Maps `RouteAuth` discriminants to auth middleware
|
|
4
|
+
* Maps `RouteAuth` discriminants to two-phase auth middleware sets.
|
|
5
|
+
* `pre_validation` carries the 401 check (`require_auth`) so
|
|
6
|
+
* unauthenticated callers never see route-shape information from input
|
|
7
|
+
* parse failures. `post_authorization` carries the 403 role / keeper
|
|
8
|
+
* checks because they read the `RequestContext` populated by the
|
|
9
|
+
* dispatcher's authorization phase.
|
|
10
|
+
*
|
|
5
11
|
* Injected into `apply_route_specs` to decouple the generic HTTP
|
|
6
12
|
* framework (`http/route_spec.ts`) from auth-specific middleware.
|
|
7
13
|
*
|
|
@@ -14,19 +20,19 @@ import { require_keeper } from './require_keeper.js';
|
|
|
14
20
|
*
|
|
15
21
|
* Maps `RouteAuth` to middleware:
|
|
16
22
|
* - `none` → no guards
|
|
17
|
-
* - `authenticated` → `require_auth`
|
|
18
|
-
* - `role` → `require_role(role)`
|
|
19
|
-
* - `keeper` → `require_keeper`
|
|
23
|
+
* - `authenticated` → pre-validation `require_auth`
|
|
24
|
+
* - `role` → pre-validation `require_auth` + post-authorization `require_role(role)`
|
|
25
|
+
* - `keeper` → pre-validation `require_auth` + post-authorization `require_keeper`
|
|
20
26
|
*/
|
|
21
27
|
export const fuz_auth_guard_resolver = (auth) => {
|
|
22
28
|
switch (auth.type) {
|
|
23
29
|
case 'none':
|
|
24
|
-
return [];
|
|
30
|
+
return { pre_validation: [], post_authorization: [] };
|
|
25
31
|
case 'authenticated':
|
|
26
|
-
return [require_auth];
|
|
32
|
+
return { pre_validation: [require_auth], post_authorization: [] };
|
|
27
33
|
case 'role':
|
|
28
|
-
return [require_role(auth.role)];
|
|
34
|
+
return { pre_validation: [require_auth], post_authorization: [require_role(auth.role)] };
|
|
29
35
|
case 'keeper':
|
|
30
|
-
return [require_keeper];
|
|
36
|
+
return { pre_validation: [require_auth], post_authorization: [require_keeper] };
|
|
31
37
|
}
|
|
32
38
|
};
|
|
@@ -15,6 +15,7 @@ export declare const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE: "role_not_self_servic
|
|
|
15
15
|
export declare const SelfServiceRoleSetInput: z.ZodObject<{
|
|
16
16
|
role: z.ZodString;
|
|
17
17
|
enabled: z.ZodBoolean;
|
|
18
|
+
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
18
19
|
}, z.core.$strict>;
|
|
19
20
|
export type SelfServiceRoleSetInput = z.infer<typeof SelfServiceRoleSetInput>;
|
|
20
21
|
/**
|
|
@@ -37,6 +38,7 @@ export declare const self_service_role_set_action_spec: {
|
|
|
37
38
|
input: z.ZodObject<{
|
|
38
39
|
role: z.ZodString;
|
|
39
40
|
enabled: z.ZodBoolean;
|
|
41
|
+
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
40
42
|
}, z.core.$strict>;
|
|
41
43
|
output: z.ZodObject<{
|
|
42
44
|
ok: z.ZodLiteral<true>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;CAWT,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,aAAa,CAAC,yBAAyB,CAEvF,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { RoleName } from './role_schema.js';
|
|
12
|
+
import { ActingActor } from './account_schema.js';
|
|
12
13
|
/** Error reason — caller asked to self-toggle a role outside the configured allowlist. */
|
|
13
14
|
export const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE = 'role_not_self_service_eligible';
|
|
14
15
|
/** Input for `self_service_role_set`. */
|
|
@@ -17,6 +18,7 @@ export const SelfServiceRoleSetInput = z.strictObject({
|
|
|
17
18
|
enabled: z.boolean().meta({
|
|
18
19
|
description: 'Desired post-call state. `true` grants if not held; `false` revokes if held. Idempotent in both directions.',
|
|
19
20
|
}),
|
|
21
|
+
acting: ActingActor,
|
|
20
22
|
});
|
|
21
23
|
/**
|
|
22
24
|
* Output for `self_service_role_set`. `enabled` echoes the post-call state
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
*/
|
|
34
34
|
import { type RpcAction } from '../actions/action_rpc.js';
|
|
35
35
|
import type { RoleSchemaResult } from './role_schema.js';
|
|
36
|
-
import type {
|
|
36
|
+
import type { AuditEmitDeps } from './deps.js';
|
|
37
37
|
/** Options for `create_self_service_role_actions`. */
|
|
38
38
|
export interface SelfServiceRoleActionsOptions {
|
|
39
39
|
/**
|
|
@@ -50,12 +50,13 @@ export interface SelfServiceRoleActionsOptions {
|
|
|
50
50
|
roles?: RoleSchemaResult;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Dependencies for `create_self_service_role_actions`.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* Dependencies for `create_self_service_role_actions`.
|
|
54
|
+
*
|
|
55
|
+
* Aliases the shared `AuditEmitDeps` so consumers thread one deps object
|
|
56
|
+
* through every action factory. `audit_log_config` is consumed by
|
|
56
57
|
* `audit_log_fire_and_forget`.
|
|
57
58
|
*/
|
|
58
|
-
export type SelfServiceRoleActionDeps =
|
|
59
|
+
export type SelfServiceRoleActionDeps = AuditEmitDeps;
|
|
59
60
|
/**
|
|
60
61
|
* Build the unified self-service role toggle RPC action.
|
|
61
62
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAA4C,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAEnG,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,WAAW,CAAC;AAY7C,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC7C;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,aAAa,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,yBAAyB,EAC/B,SAAS,6BAA6B,KACpC,KAAK,CAAC,SAAS,CA8HjB,CAAC"}
|
|
@@ -31,16 +31,13 @@
|
|
|
31
31
|
*
|
|
32
32
|
* @module
|
|
33
33
|
*/
|
|
34
|
-
import {
|
|
34
|
+
import { rpc_actor_action } from '../actions/action_rpc.js';
|
|
35
35
|
import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
|
|
36
|
-
import { query_grant_permit,
|
|
36
|
+
import { query_grant_permit, query_revoke_permit } from './permit_queries.js';
|
|
37
37
|
import { audit_log_fire_and_forget } from './audit_log_queries.js';
|
|
38
|
+
import { is_permit_active } from './account_schema.js';
|
|
39
|
+
import { has_scoped_role } from './request_context.js';
|
|
38
40
|
import { ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE, self_service_role_set_action_spec, } from './self_service_role_action_specs.js';
|
|
39
|
-
const require_request_auth = (auth) => {
|
|
40
|
-
if (!auth)
|
|
41
|
-
throw new Error('unreachable: action auth guard did not enforce authentication');
|
|
42
|
-
return auth;
|
|
43
|
-
};
|
|
44
41
|
/**
|
|
45
42
|
* Build the unified self-service role toggle RPC action.
|
|
46
43
|
*
|
|
@@ -67,19 +64,19 @@ export const create_self_service_role_actions = (deps, options) => {
|
|
|
67
64
|
}
|
|
68
65
|
};
|
|
69
66
|
const handler = async (input, ctx) => {
|
|
70
|
-
const auth =
|
|
67
|
+
const auth = ctx.auth;
|
|
71
68
|
reject_if_ineligible(input.role);
|
|
72
69
|
if (input.enabled) {
|
|
73
70
|
// Pre-check for idempotent re-grant. `query_grant_permit` is itself
|
|
74
71
|
// idempotent (returns the existing permit instead of inserting), but
|
|
75
72
|
// it doesn't signal "already existed" vs "newly inserted" — so we
|
|
76
|
-
// peek first.
|
|
77
|
-
//
|
|
73
|
+
// peek first. Reads from the in-memory `auth.permits` snapshot
|
|
74
|
+
// (no DB roundtrip). The TOCTOU window is benign for self-service:
|
|
75
|
+
// two concurrent grants both observe "no permit", both call
|
|
78
76
|
// `query_grant_permit`, and one collapses onto the other inside the
|
|
79
77
|
// query's `ON CONFLICT DO NOTHING`. Worst case both responses report
|
|
80
78
|
// `changed: true`; the DB still ends up with exactly one permit.
|
|
81
|
-
|
|
82
|
-
if (already) {
|
|
79
|
+
if (has_scoped_role(auth, input.role, null)) {
|
|
83
80
|
return { ok: true, enabled: true, changed: false };
|
|
84
81
|
}
|
|
85
82
|
const permit = await query_grant_permit(ctx, {
|
|
@@ -89,10 +86,19 @@ export const create_self_service_role_actions = (deps, options) => {
|
|
|
89
86
|
expires_at: null,
|
|
90
87
|
granted_by: auth.actor.id,
|
|
91
88
|
});
|
|
89
|
+
// `permit_grant` is the canonical actor-bound-subject event —
|
|
90
|
+
// populate both target columns even on self-service so the
|
|
91
|
+
// "always populated for permit_grant" rule holds uniformly
|
|
92
|
+
// regardless of who initiated the grant. On self-service the
|
|
93
|
+
// grantor and grantee are the same identity; admin direct-grant
|
|
94
|
+
// (separate code path) populates the same columns with the
|
|
95
|
+
// grantee actor.
|
|
92
96
|
void audit_log_fire_and_forget(ctx, {
|
|
93
97
|
event_type: 'permit_grant',
|
|
94
98
|
actor_id: auth.actor.id,
|
|
95
99
|
account_id: auth.account.id,
|
|
100
|
+
target_account_id: auth.account.id,
|
|
101
|
+
target_actor_id: auth.actor.id,
|
|
96
102
|
ip: ctx.client_ip,
|
|
97
103
|
metadata: {
|
|
98
104
|
role: permit.role,
|
|
@@ -103,12 +109,13 @@ export const create_self_service_role_actions = (deps, options) => {
|
|
|
103
109
|
}, deps);
|
|
104
110
|
return { ok: true, enabled: true, changed: true };
|
|
105
111
|
}
|
|
106
|
-
// Find an active global permit for this (actor, role)
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
+
// Find an active global permit for this (actor, role) in the in-memory
|
|
113
|
+
// `auth.permits` snapshot. No DB roundtrip — same correctness-equivalent
|
|
114
|
+
// pattern as `has_scoped_role` above (race window is between predicate
|
|
115
|
+
// and `query_revoke_permit`'s actual UPDATE, not between predicate and
|
|
116
|
+
// middleware load).
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const target = auth.permits.find((p) => p.role === input.role && p.scope_id === null && is_permit_active(p, now));
|
|
112
119
|
if (!target) {
|
|
113
120
|
return { ok: true, enabled: false, changed: false };
|
|
114
121
|
}
|
|
@@ -117,10 +124,16 @@ export const create_self_service_role_actions = (deps, options) => {
|
|
|
117
124
|
// Raced with another revoker — treat as already revoked.
|
|
118
125
|
return { ok: true, enabled: false, changed: false };
|
|
119
126
|
}
|
|
127
|
+
// Same actor-bound rule as the grant branch — `permit_revoke`
|
|
128
|
+
// always populates both target columns even on self-service so
|
|
129
|
+
// forensic queries that filter on `target_actor_id IS NOT NULL`
|
|
130
|
+
// don't silently miss self-toggled permits.
|
|
120
131
|
void audit_log_fire_and_forget(ctx, {
|
|
121
132
|
event_type: 'permit_revoke',
|
|
122
133
|
actor_id: auth.actor.id,
|
|
123
134
|
account_id: auth.account.id,
|
|
135
|
+
target_account_id: auth.account.id,
|
|
136
|
+
target_actor_id: auth.actor.id,
|
|
124
137
|
ip: ctx.client_ip,
|
|
125
138
|
metadata: {
|
|
126
139
|
role: result.role,
|
|
@@ -131,5 +144,5 @@ export const create_self_service_role_actions = (deps, options) => {
|
|
|
131
144
|
}, deps);
|
|
132
145
|
return { ok: true, enabled: false, changed: true };
|
|
133
146
|
};
|
|
134
|
-
return [
|
|
147
|
+
return [rpc_actor_action(self_service_role_set_action_spec, handler)];
|
|
135
148
|
};
|
package/dist/db/migrate.d.ts
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* `(namespace, name, sequence, applied_at)` — and the runner verifies the
|
|
7
7
|
* applied list is a name-prefix of the code's migration array at boot.
|
|
8
8
|
*
|
|
9
|
-
* **
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* **Schema is not stabilized yet — append-only is NOT the rule.** While
|
|
10
|
+
* fuz_app is pre-stable, migration bodies, names, and positions can change
|
|
11
|
+
* freely between versions; consumers upgrading across a schema change are
|
|
12
|
+
* expected to drop and re-bootstrap their dev/test databases (production
|
|
13
|
+
* deployments are not yet a supported use case). Once the schema is
|
|
14
|
+
* declared stable a hard append-only-after-publish rule will apply and the
|
|
15
|
+
* cliff will be called out in that release's notes; until then, body edits
|
|
16
|
+
* to a published migration slip past the runner (no content hashing) by
|
|
17
|
+
* design — they're the recommended way to evolve the schema.
|
|
15
18
|
*
|
|
16
19
|
* **Chain-level transactions**: All pending migrations in a namespace run in
|
|
17
20
|
* a single transaction. Any failure rolls back every migration in that run —
|
|
@@ -53,7 +56,8 @@ export interface Migration {
|
|
|
53
56
|
/**
|
|
54
57
|
* A named group of ordered migrations.
|
|
55
58
|
*
|
|
56
|
-
* Array index = position in the chain.
|
|
59
|
+
* Array index = position in the chain. Pre-stable: bodies, names, and
|
|
60
|
+
* positions can change between versions (consumers re-bootstrap on upgrade).
|
|
57
61
|
*/
|
|
58
62
|
export interface MigrationNamespace {
|
|
59
63
|
namespace: string;
|
package/dist/db/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/db/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,SAAS,CAAC;AAEhC;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAC7B;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAC3B,sBAAsB,GACtB,sBAAsB,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,2BAA2B,GAC3B,4BAA4B,GAC5B,sCAAsC,CAAC;AAE1C,8DAA8D;AAC9D,MAAM,WAAW,qBAAqB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,aAAa,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBAEnC,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,qBAAqB;CAQtF;AA6ED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,cAAc,GAC1B,IAAI,EAAE,EACN,YAAY,KAAK,CAAC,kBAAkB,CAAC,KACnC,OAAO,CAAC,KAAK,CAAC,eAAe,CAAC,CAuFhC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,QAAQ,GACpB,IAAI,EAAE,EACN,IAAI,kBAAkB,EACtB,OAAO,aAAa,CAAC,MAAM,CAAC,KAC1B,OAAO,CAAC,IAAI,CA+Dd,CAAC"}
|
package/dist/db/migrate.js
CHANGED
|
@@ -6,12 +6,15 @@
|
|
|
6
6
|
* `(namespace, name, sequence, applied_at)` — and the runner verifies the
|
|
7
7
|
* applied list is a name-prefix of the code's migration array at boot.
|
|
8
8
|
*
|
|
9
|
-
* **
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* **Schema is not stabilized yet — append-only is NOT the rule.** While
|
|
10
|
+
* fuz_app is pre-stable, migration bodies, names, and positions can change
|
|
11
|
+
* freely between versions; consumers upgrading across a schema change are
|
|
12
|
+
* expected to drop and re-bootstrap their dev/test databases (production
|
|
13
|
+
* deployments are not yet a supported use case). Once the schema is
|
|
14
|
+
* declared stable a hard append-only-after-publish rule will apply and the
|
|
15
|
+
* cliff will be called out in that release's notes; until then, body edits
|
|
16
|
+
* to a published migration slip past the runner (no content hashing) by
|
|
17
|
+
* design — they're the recommended way to evolve the schema.
|
|
15
18
|
*
|
|
16
19
|
* **Chain-level transactions**: All pending migrations in a namespace run in
|
|
17
20
|
* a single transaction. Any failure rolls back every migration in that run —
|
package/dist/dev/setup.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/dev/setup.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACX,WAAW,EACX,aAAa,EACb,OAAO,EACP,UAAU,EACV,YAAY,EACZ,WAAW,EACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAQnD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,EAAE,WAIlC,CAAC;AAEF,kCAAkC;AAClC,MAAM,WAAW,cAAc;IAC9B,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAC;IACjB,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,yBAAyB;IACzB,IAAI,EAAE,MAAM,CAAC;CACb;AAED,yCAAyC;AACzC,MAAM,WAAW,gBAAgB;IAChC,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB,2BAA2B;IAC3B,UAAU,EAAE,MAAM,CAAC;CACnB;AAED,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC7B,+CAA+C;IAC/C,KAAK,EAAE,OAAO,CAAC;IACf,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;CACxC;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC/B;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACrD,qEAAqE;IACrE,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,WAAW,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,0BAA0B;IAC1C,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2DAA2D;IAC3D,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,WAAW,CAAC;CAClB;AAED,qCAAqC;AACrC,MAAM,WAAW,qBAAqB;IACrC,GAAG,CAAC,EAAE,WAAW,CAAC;CAClB;AAED,oCAAoC;AACpC,MAAM,WAAW,oBAAoB;IACpC,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,EAAE,WAAW,CAAC;CAClB;AAID;;;;GAIG;AACH,eAAO,MAAM,aAAa,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,IAQpD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAAU,MAAM,WAAW,KAAG,OAAO,CAAC,MAAM,CAI3E,CAAC;AAIF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GACxB,MAAM,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,gBAAgB,CAAC,EACjD,UAAU,MAAM,EAChB,MAAM,MAAM,KACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAU5B,CAAC;AAIF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAM,UAAU,GAAG,WAAW,GAAG,WAAW,EAC5C,UAAU,MAAM,EAChB,cAAc,MAAM,EACpB,UAAU,eAAe,KACvB,OAAO,CAAC,cAAc,CAiDxB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,EACtD,UAAU,MAAM,EAChB,UAAU,0BAA0B,KAClC,OAAO,CAAC,gBAAgB,CA0B1B,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,UAAU,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,EACrE,UAAU,MAAM,EAChB,UAAU,0BAA0B,KAClC,OAAO,CAAC,gBAAgB,CAoB1B,CAAC;AAIF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,GAC3B,MAAM,WAAW,EACjB,SAAS,MAAM,EACf,UAAU,qBAAqB,KAC7B,OAAO,CAAC,aAAa,CAgBvB,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,cAAc,GAC1B,MAAM,WAAW,GAAG,UAAU,GAAG,YAAY,EAC7C,cAAc,MAAM,EACpB,UAAU,oBAAoB,KAC5B,OAAO,CAAC,aAAa,CA8CvB,CAAC;AAIF,mCAAmC;AACnC,MAAM,WAAW,mBAAmB;IACnC,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9B;AAED,oCAAoC;AACpC,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;CACjB;AAED,2CAA2C;AAC3C,MAAM,WAAW,kBAAmB,SAAQ,SAAS;IACpD,oEAAoE;IACpE,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACrD;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,GAC5B,MAAM,kBAAkB,EACxB,OAAO,mBAAmB,EAC1B,UAAU;IAAC,GAAG,CAAC,EAAE,WAAW,CAAA;CAAC,KAC3B,OAAO,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/dev/setup.ts"],"names":[],"mappings":"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"}
|
package/dist/dev/setup.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
11
|
-
import { query_account_by_username,
|
|
11
|
+
import { query_account_by_username, query_actors_by_account, query_create_account_with_actor, } from '../auth/account_queries.js';
|
|
12
12
|
import { query_grant_permit } from '../auth/permit_queries.js';
|
|
13
13
|
/** Default logger using bracket format. */
|
|
14
14
|
export const default_setup_logger = {
|
|
@@ -292,11 +292,13 @@ export const seed_dev_account = async (deps, input, options) => {
|
|
|
292
292
|
const query_deps = { db: deps.db };
|
|
293
293
|
const existing = await query_account_by_username(query_deps, input.username);
|
|
294
294
|
if (existing) {
|
|
295
|
-
const
|
|
296
|
-
if (
|
|
295
|
+
const actors = await query_actors_by_account(query_deps, existing.id);
|
|
296
|
+
if (actors.length === 0) {
|
|
297
297
|
log.error(`dev account '${input.username}' exists but has no actor`);
|
|
298
298
|
throw new Error(`dev account '${input.username}' has no actor`);
|
|
299
299
|
}
|
|
300
|
+
// Dev seed is single-actor by construction; pick the first.
|
|
301
|
+
const actor = actors[0];
|
|
300
302
|
for (const role of input.roles ?? []) {
|
|
301
303
|
await query_grant_permit(query_deps, {
|
|
302
304
|
actor_id: actor.id,
|
package/dist/hono_context.d.ts
CHANGED
|
@@ -25,6 +25,63 @@ export type CredentialType = z.infer<typeof CredentialType>;
|
|
|
25
25
|
export declare const CREDENTIAL_TYPE_KEY = "credential_type";
|
|
26
26
|
/** Hono context variable name for the authenticated API token id. */
|
|
27
27
|
export declare const AUTH_API_TOKEN_ID_KEY = "auth_api_token_id";
|
|
28
|
+
/**
|
|
29
|
+
* Hono context variable name for the authenticated account id.
|
|
30
|
+
*
|
|
31
|
+
* Set by the auth middleware (session, bearer, or daemon token) on a valid
|
|
32
|
+
* credential. `null` for unauthenticated requests. The route-spec wrapper /
|
|
33
|
+
* RPC dispatcher's authorization phase reads this when resolving the acting
|
|
34
|
+
* actor; account-grain auth guards (`require_auth`) and account-grain handlers
|
|
35
|
+
* read it directly.
|
|
36
|
+
*/
|
|
37
|
+
export declare const ACCOUNT_ID_KEY = "auth_account_id";
|
|
38
|
+
/**
|
|
39
|
+
* Hono context variable name for the test-harness pre-baked context flag.
|
|
40
|
+
*
|
|
41
|
+
* Test harnesses (`create_test_app_from_specs`, `create_fake_hono_context`,
|
|
42
|
+
* the WS round-trip `connect()` helper, plus per-test middleware that
|
|
43
|
+
* pre-populates `REQUEST_CONTEXT_KEY`) set this to `true` so
|
|
44
|
+
* `apply_authorization_phase` skips its DB-backed actor resolution and
|
|
45
|
+
* trusts the supplied `RequestContext`. Production middleware never sets
|
|
46
|
+
* this key — only test code does. The flag is the explicit escape hatch
|
|
47
|
+
* that replaced the implicit "is `REQUEST_CONTEXT_KEY` already set?" probe,
|
|
48
|
+
* so that future production code consulting `REQUEST_CONTEXT_KEY` cannot
|
|
49
|
+
* silently bypass the live build.
|
|
50
|
+
*/
|
|
51
|
+
export declare const TEST_CONTEXT_PRESET_KEY = "test_context_preset";
|
|
52
|
+
/**
|
|
53
|
+
* Cached parsed JSON request body, keyed by `'cached_request_body'`.
|
|
54
|
+
*
|
|
55
|
+
* Written by `read_raw_acting` (in the dispatcher's authorization
|
|
56
|
+
* phase) when it pre-parses the body to extract the `acting` field;
|
|
57
|
+
* read by `create_input_validation` so the input-validation step does
|
|
58
|
+
* not pay for a second `JSON.parse` on the same Hono-cached body text.
|
|
59
|
+
*
|
|
60
|
+
* Decouples our pipeline from Hono's internal `bodyCache` shape: Hono
|
|
61
|
+
* caches the body *text* (so a second `c.req.json()` call doesn't
|
|
62
|
+
* re-read the request stream), but each call still re-runs
|
|
63
|
+
* `JSON.parse(text)`. Storing the parsed value here saves the second
|
|
64
|
+
* parse and keeps fuz_app from depending on undocumented Hono
|
|
65
|
+
* implementation details.
|
|
66
|
+
*
|
|
67
|
+
* Three states:
|
|
68
|
+
*
|
|
69
|
+
* - Key absent — body has not been pre-parsed yet (the route had no
|
|
70
|
+
* `acting` to extract, or the request is GET).
|
|
71
|
+
* - `{ok: true, body: unknown}` — pre-parse succeeded; the parsed
|
|
72
|
+
* value (object, primitive, or array) is in `body`.
|
|
73
|
+
* - `{ok: false}` — pre-parse threw (malformed JSON). The downstream
|
|
74
|
+
* input-validation step short-circuits with `ERROR_INVALID_JSON_BODY`
|
|
75
|
+
* instead of re-parsing.
|
|
76
|
+
*/
|
|
77
|
+
export declare const CACHED_REQUEST_BODY_KEY = "cached_request_body";
|
|
78
|
+
/** The shape stored under `CACHED_REQUEST_BODY_KEY`. */
|
|
79
|
+
export type CachedRequestBody = {
|
|
80
|
+
ok: true;
|
|
81
|
+
body: unknown;
|
|
82
|
+
} | {
|
|
83
|
+
ok: false;
|
|
84
|
+
};
|
|
28
85
|
declare module 'hono' {
|
|
29
86
|
interface ContextVariableMap {
|
|
30
87
|
/** Resolved client IP, set by the trusted proxy middleware. */
|
|
@@ -36,6 +93,13 @@ declare module 'hono' {
|
|
|
36
93
|
validated_query: unknown;
|
|
37
94
|
/** How the request was authenticated (`'session'`, `'api_token'`, or `'daemon_token'`). */
|
|
38
95
|
credential_type: CredentialType | null;
|
|
96
|
+
/**
|
|
97
|
+
* Authenticated account id. Set by the session / bearer / daemon-token
|
|
98
|
+
* middleware on a valid credential; `null` for unauthenticated requests.
|
|
99
|
+
* The dispatcher's authorization phase resolves the acting actor against
|
|
100
|
+
* this id; `require_auth` 401s when it is `null`.
|
|
101
|
+
*/
|
|
102
|
+
auth_account_id: string | null;
|
|
39
103
|
/**
|
|
40
104
|
* blake3 hash of the authenticated session token, or `null` for non-session
|
|
41
105
|
* credentials. Set by `create_request_context_middleware`. Used to scope
|
|
@@ -57,6 +121,19 @@ declare module 'hono' {
|
|
|
57
121
|
* all effects are awaited before the response returns.
|
|
58
122
|
*/
|
|
59
123
|
pending_effects: Array<Promise<void>>;
|
|
124
|
+
/**
|
|
125
|
+
* Set to `true` by test harnesses that pre-populate `request_context`
|
|
126
|
+
* to bypass the dispatcher's DB-backed actor resolution. Read by
|
|
127
|
+
* `apply_authorization_phase`. Production middleware never sets this.
|
|
128
|
+
*/
|
|
129
|
+
test_context_preset: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Pre-parsed JSON request body cache. Written by `read_raw_acting`
|
|
132
|
+
* (the dispatcher's `acting` extractor) and read by
|
|
133
|
+
* `create_input_validation` so the same body is not parsed twice.
|
|
134
|
+
* See `CACHED_REQUEST_BODY_KEY` for state semantics.
|
|
135
|
+
*/
|
|
136
|
+
cached_request_body: CachedRequestBody;
|
|
60
137
|
}
|
|
61
138
|
}
|
|
62
139
|
//# sourceMappingURL=hono_context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,mDAAoD,CAAC;AAElF,yDAAyD;AACzD,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAC3B,+DAA+D;QAC/D,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC,eAAe,EAAE,OAAO,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,2FAA2F;QAC3F,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC;;;;WAIG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"hono_context.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/hono_context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,4DAA4D;AAC5D,eAAO,MAAM,gBAAgB,mDAAoD,CAAC;AAElF,yDAAyD;AACzD,eAAO,MAAM,cAAc;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE5D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,oBAAoB,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,uBAAuB,wBAAwB,CAAC;AAE7D,wDAAwD;AACxD,MAAM,MAAM,iBAAiB,GAAG;IAAC,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAC,GAAG;IAAC,EAAE,EAAE,KAAK,CAAA;CAAC,CAAC;AAExE,OAAO,QAAQ,MAAM,CAAC;IACrB,UAAU,kBAAkB;QAC3B,+DAA+D;QAC/D,SAAS,EAAE,MAAM,CAAC;QAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC,eAAe,EAAE,OAAO,CAAC;QACzB,gBAAgB,EAAE,OAAO,CAAC;QAC1B,eAAe,EAAE,OAAO,CAAC;QACzB,2FAA2F;QAC3F,eAAe,EAAE,cAAc,GAAG,IAAI,CAAC;QACvC;;;;;WAKG;QACH,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;QAC/B;;;;;WAKG;QACH,uBAAuB,EAAE,MAAM,GAAG,IAAI,CAAC;QACvC;;;;;;WAMG;QACH,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;QACjC;;;;WAIG;QACH,eAAe,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACtC;;;;WAIG;QACH,mBAAmB,EAAE,OAAO,CAAC;QAC7B;;;;;WAKG;QACH,mBAAmB,EAAE,iBAAiB,CAAC;KACvC;CACD"}
|
package/dist/hono_context.js
CHANGED
|
@@ -19,3 +19,53 @@ export const CredentialType = z.enum(CREDENTIAL_TYPES);
|
|
|
19
19
|
export const CREDENTIAL_TYPE_KEY = 'credential_type';
|
|
20
20
|
/** Hono context variable name for the authenticated API token id. */
|
|
21
21
|
export const AUTH_API_TOKEN_ID_KEY = 'auth_api_token_id';
|
|
22
|
+
/**
|
|
23
|
+
* Hono context variable name for the authenticated account id.
|
|
24
|
+
*
|
|
25
|
+
* Set by the auth middleware (session, bearer, or daemon token) on a valid
|
|
26
|
+
* credential. `null` for unauthenticated requests. The route-spec wrapper /
|
|
27
|
+
* RPC dispatcher's authorization phase reads this when resolving the acting
|
|
28
|
+
* actor; account-grain auth guards (`require_auth`) and account-grain handlers
|
|
29
|
+
* read it directly.
|
|
30
|
+
*/
|
|
31
|
+
export const ACCOUNT_ID_KEY = 'auth_account_id';
|
|
32
|
+
/**
|
|
33
|
+
* Hono context variable name for the test-harness pre-baked context flag.
|
|
34
|
+
*
|
|
35
|
+
* Test harnesses (`create_test_app_from_specs`, `create_fake_hono_context`,
|
|
36
|
+
* the WS round-trip `connect()` helper, plus per-test middleware that
|
|
37
|
+
* pre-populates `REQUEST_CONTEXT_KEY`) set this to `true` so
|
|
38
|
+
* `apply_authorization_phase` skips its DB-backed actor resolution and
|
|
39
|
+
* trusts the supplied `RequestContext`. Production middleware never sets
|
|
40
|
+
* this key — only test code does. The flag is the explicit escape hatch
|
|
41
|
+
* that replaced the implicit "is `REQUEST_CONTEXT_KEY` already set?" probe,
|
|
42
|
+
* so that future production code consulting `REQUEST_CONTEXT_KEY` cannot
|
|
43
|
+
* silently bypass the live build.
|
|
44
|
+
*/
|
|
45
|
+
export const TEST_CONTEXT_PRESET_KEY = 'test_context_preset';
|
|
46
|
+
/**
|
|
47
|
+
* Cached parsed JSON request body, keyed by `'cached_request_body'`.
|
|
48
|
+
*
|
|
49
|
+
* Written by `read_raw_acting` (in the dispatcher's authorization
|
|
50
|
+
* phase) when it pre-parses the body to extract the `acting` field;
|
|
51
|
+
* read by `create_input_validation` so the input-validation step does
|
|
52
|
+
* not pay for a second `JSON.parse` on the same Hono-cached body text.
|
|
53
|
+
*
|
|
54
|
+
* Decouples our pipeline from Hono's internal `bodyCache` shape: Hono
|
|
55
|
+
* caches the body *text* (so a second `c.req.json()` call doesn't
|
|
56
|
+
* re-read the request stream), but each call still re-runs
|
|
57
|
+
* `JSON.parse(text)`. Storing the parsed value here saves the second
|
|
58
|
+
* parse and keeps fuz_app from depending on undocumented Hono
|
|
59
|
+
* implementation details.
|
|
60
|
+
*
|
|
61
|
+
* Three states:
|
|
62
|
+
*
|
|
63
|
+
* - Key absent — body has not been pre-parsed yet (the route had no
|
|
64
|
+
* `acting` to extract, or the request is GET).
|
|
65
|
+
* - `{ok: true, body: unknown}` — pre-parse succeeded; the parsed
|
|
66
|
+
* value (object, primitive, or array) is in `body`.
|
|
67
|
+
* - `{ok: false}` — pre-parse threw (malformed JSON). The downstream
|
|
68
|
+
* input-validation step short-circuits with `ERROR_INVALID_JSON_BODY`
|
|
69
|
+
* instead of re-parsing.
|
|
70
|
+
*/
|
|
71
|
+
export const CACHED_REQUEST_BODY_KEY = 'cached_request_body';
|
package/dist/http/CLAUDE.md
CHANGED
|
@@ -91,21 +91,66 @@ wrapper). See `../auth/signup_routes.ts`.
|
|
|
91
91
|
|
|
92
92
|
`apply_route_specs` assembles the following middleware chain per spec:
|
|
93
93
|
|
|
94
|
-
1. **
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
1. **Params validation** — `spec.params` → `validated_params` context
|
|
95
|
+
var; mismatch returns 400 `ERROR_INVALID_ROUTE_PARAMS` with Zod
|
|
96
|
+
`issues`
|
|
97
|
+
2. **Query validation** — `spec.query` → `validated_query`; mismatch
|
|
98
|
+
returns 400 `ERROR_INVALID_QUERY_PARAMS`
|
|
99
|
+
3. **Pre-validation auth guards** — `require_auth` (401
|
|
100
|
+
`ERROR_AUTHENTICATION_REQUIRED`) for any non-public route. Fires
|
|
101
|
+
before any body parsing so unauthenticated callers never see
|
|
102
|
+
route-shape information from input parse failures. The
|
|
103
|
+
`AuthGuardResolver` (e.g. `fuz_auth_guard_resolver` from
|
|
104
|
+
`../auth/route_guards.ts`) returns this set as
|
|
105
|
+
`pre_validation: Array<MiddlewareHandler>`.
|
|
106
|
+
4. **Authorization phase** — when the route's input schema declares
|
|
107
|
+
`acting?: ActingActor` or `spec.auth.type` is `'role'` / `'keeper'`,
|
|
108
|
+
resolves the acting actor against `c.var.account_id` (set by the
|
|
109
|
+
auth middleware) plus the raw `acting` value extracted from query
|
|
110
|
+
(GET) or pre-parsed JSON body (mutating methods), builds
|
|
111
|
+
`RequestContext` via `build_request_context`, and sets
|
|
112
|
+
`REQUEST_CONTEXT_KEY`. Resolution failures return 400
|
|
113
|
+
`ERROR_ACTOR_REQUIRED` (with `available[]`) or
|
|
114
|
+
`ERROR_ACTOR_NOT_ON_ACCOUNT` (or 500 `ERROR_NO_ACTORS_ON_ACCOUNT`
|
|
115
|
+
when the actor enumeration came back empty, 500 `ERROR_ACCOUNT_VANISHED`
|
|
116
|
+
on torn account/actor reads after a successful resolve) before the
|
|
117
|
+
handler runs. Account-grain
|
|
118
|
+
routes skip this phase; their handlers see no `RequestContext` (or
|
|
119
|
+
one with `actor: null`, depending on the helper). The pre-parsed body
|
|
120
|
+
lands on `c.var.cached_request_body` (see step 6) so the subsequent
|
|
121
|
+
input-validation step reads from there instead of re-parsing.
|
|
122
|
+
5. **Post-authorization auth guards** — `require_role(role)` /
|
|
123
|
+
`require_keeper` (403 `ERROR_INSUFFICIENT_PERMISSIONS` /
|
|
124
|
+
`ERROR_KEEPER_REQUIRES_DAEMON_TOKEN`). Reads `REQUEST_CONTEXT_KEY`
|
|
125
|
+
populated by step 4. The resolver returns this set as
|
|
126
|
+
`post_authorization: Array<MiddlewareHandler>`.
|
|
127
|
+
6. **Input validation** — JSON body parsed + validated; mismatch returns
|
|
128
|
+
400 `ERROR_INVALID_JSON_BODY` (not JSON) or `ERROR_INVALID_REQUEST_BODY`
|
|
129
|
+
(schema failure with `issues`). Skipped on GET and `z.null()` inputs.
|
|
130
|
+
On mutating methods, the parse result is shared with the authorization
|
|
131
|
+
phase's pre-parse via `c.var.cached_request_body`
|
|
132
|
+
(`CACHED_REQUEST_BODY_KEY` from `hono_context.ts`) — the cache is
|
|
133
|
+
fuz_app-owned, not Hono's internal `bodyCache`, so a future Hono
|
|
134
|
+
internals refactor can't break our second-parse-avoidance contract.
|
|
135
|
+
7. **Handler** — wrapped in transaction when `use_transaction` (see
|
|
136
|
+
above), receives `RouteContext`
|
|
137
|
+
8. **DEV-only output + error validation** — wraps the handler (see below)
|
|
138
|
+
9. **Error catch** — catches `ThrownJsonrpcError` → maps to HTTP status +
|
|
139
|
+
the flat REST `ApiError` body (`{error: <reason>, message?, ...rest_data}`);
|
|
140
|
+
catches generic `Error` → 500 `{error: 'internal_error', message?}`
|
|
141
|
+
(message only included in DEV). The reason string comes from
|
|
142
|
+
`err.data.reason` when set (consumer-supplied canonical reason
|
|
143
|
+
override) or from `jsonrpc_error_code_to_name(err.code)` (e.g.
|
|
144
|
+
`-32003 → 'not_found'`). The flat shape matches what middleware
|
|
145
|
+
and direct handlers emit (`c.json({error: ERROR_FOO}, status)`,
|
|
146
|
+
`c.json(failure.body, status)` from the dispatcher's authorization
|
|
147
|
+
phase) — REST callers see one envelope across every emit site, while
|
|
148
|
+
the JSON-RPC dispatcher keeps its own `{jsonrpc, id, error: {code,
|
|
149
|
+
message, data}}` envelope on the RPC mount
|
|
150
|
+
|
|
151
|
+
The auth-before-validation order matches the RPC dispatcher
|
|
152
|
+
(`actions/action_rpc.ts`) so HTTP RPC and REST surface failures with
|
|
153
|
+
the same priority: 401 → 403 → 400 → handler.
|
|
109
154
|
|
|
110
155
|
Duplicate `method path` pairs throw at registration.
|
|
111
156
|
|
|
@@ -162,21 +207,39 @@ Pair every schema with the `z.infer` type export (`export type ApiError = z.infe
|
|
|
162
207
|
|
|
163
208
|
### Three-layer error-schema merge
|
|
164
209
|
|
|
165
|
-
`merge_error_schemas(spec, middleware_errors?)` (in `schema_helpers.ts`)
|
|
210
|
+
`merge_error_schemas(spec, middleware_errors?, acting_aware?)` (in `schema_helpers.ts`)
|
|
166
211
|
merges three layers, later overrides earlier at the same status code:
|
|
167
212
|
|
|
168
|
-
1. **Derived** — from `derive_error_schemas(auth, has_input
|
|
213
|
+
1. **Derived** — from `derive_error_schemas({auth, has_input?, has_params?, has_query?, rate_limit?, acting_aware?})`:
|
|
169
214
|
- `has_input || has_params || has_query` → 400 `ValidationError`
|
|
170
215
|
- `auth.type === 'authenticated'` → 401 `ApiError`
|
|
171
216
|
- `auth.type === 'role'` → 401 `ApiError` + 403 `PermissionError`
|
|
172
217
|
- `auth.type === 'keeper'` → 401 `ApiError` + 403 `KeeperError`
|
|
173
218
|
- `rate_limit` → 429 `RateLimitError`
|
|
219
|
+
- `acting_aware` → widens 400 to a union with `ActorRequiredError` /
|
|
220
|
+
`ActorNotOnAccountError` and adds 500 union of `NoActorsOnAccountError`
|
|
221
|
+
/ `AccountVanishedError`. Mirrors what the dispatcher's authorization
|
|
222
|
+
phase actually emits on routes whose input declares `acting?: ActingActor`
|
|
223
|
+
or whose auth requires permits — so DEV-mode error-schema validation in
|
|
224
|
+
`wrap_output_validation` doesn't reject the auth phase's body.
|
|
174
225
|
2. **Middleware** — from `MiddlewareSpec.errors` that apply to the route's
|
|
175
226
|
path (via `middleware_applies`)
|
|
176
227
|
3. **Explicit** — `RouteSpec.errors` — always wins
|
|
177
228
|
|
|
178
229
|
Routes typically only need `errors` for handler-specific codes (404, 409, 422).
|
|
179
230
|
|
|
231
|
+
`acting_aware` is computed at the call site (`apply_route_specs` /
|
|
232
|
+
`generate_app_surface`) via the optional `is_acting_aware?: (spec) => boolean`
|
|
233
|
+
callback. Computation lives in the consumer because the canonical
|
|
234
|
+
"input declares `acting?: ActingActor`" check uses reference equality with
|
|
235
|
+
the canonical `ActingActor` Zod schema in `auth/account_schema.ts`, and
|
|
236
|
+
`http/` stays auth-agnostic. fuz_app's `create_app_server` wires
|
|
237
|
+
`(spec) => is_actor_implying_auth(spec.auth) || input_schema_declares_acting(spec.input)`
|
|
238
|
+
— consumers building on raw `apply_route_specs` opt in by passing the
|
|
239
|
+
same predicate (or a narrower one). When the callback is omitted the
|
|
240
|
+
flag defaults to false so frameworks not using fuz_app's auth phase don't
|
|
241
|
+
get fuz_app-specific shapes on their derived surface.
|
|
242
|
+
|
|
180
243
|
### `ERROR_*` constants by category
|
|
181
244
|
|
|
182
245
|
- **Validation**: `ERROR_INVALID_REQUEST_BODY`, `ERROR_INVALID_JSON_BODY`,
|