@fuzdev/fuz_app 0.54.0 → 0.56.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 +214 -103
- package/dist/actions/action_bridge.d.ts +8 -5
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +1 -11
- package/dist/actions/action_codegen.d.ts +32 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +35 -15
- package/dist/actions/action_registry.d.ts.map +1 -1
- package/dist/actions/action_registry.js +5 -2
- package/dist/actions/action_rpc.d.ts +141 -22
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +106 -187
- package/dist/actions/action_spec.d.ts +55 -16
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +16 -11
- package/dist/actions/action_types.d.ts +28 -60
- package/dist/actions/action_types.d.ts.map +1 -1
- package/dist/actions/action_types.js +13 -5
- package/dist/actions/broadcast_api.d.ts +2 -2
- package/dist/actions/broadcast_api.js +2 -2
- package/dist/actions/compile_action_registry.d.ts +50 -0
- package/dist/actions/compile_action_registry.d.ts.map +1 -0
- package/dist/actions/compile_action_registry.js +69 -0
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -4
- package/dist/actions/perform_action.d.ts +145 -0
- package/dist/actions/perform_action.d.ts.map +1 -0
- package/dist/actions/perform_action.js +258 -0
- package/dist/actions/register_action_ws.d.ts +46 -40
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +101 -159
- package/dist/actions/register_ws_endpoint.d.ts +15 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +54 -7
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +0 -4
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +794 -410
- package/dist/auth/account_action_specs.d.ts +28 -7
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +7 -7
- package/dist/auth/account_actions.d.ts +7 -13
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -35
- package/dist/auth/account_queries.d.ts +52 -16
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +87 -38
- package/dist/auth/account_routes.d.ts +9 -11
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +118 -46
- package/dist/auth/account_schema.d.ts +46 -35
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -28
- package/dist/auth/admin_action_specs.d.ts +100 -32
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +64 -33
- package/dist/auth/admin_actions.d.ts +13 -19
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +37 -41
- package/dist/auth/audit_emitter.d.ts +160 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -0
- package/dist/auth/audit_emitter.js +83 -0
- package/dist/auth/audit_log_queries.d.ts +17 -48
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +20 -56
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +7 -3
- package/dist/auth/audit_log_schema.d.ts +92 -32
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +75 -46
- package/dist/auth/auth_guard_resolver.d.ts +44 -0
- package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
- package/dist/auth/auth_guard_resolver.js +56 -0
- 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/bootstrap_account.d.ts +7 -7
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +7 -7
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +11 -10
- package/dist/auth/cleanup.d.ts +20 -26
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +33 -42
- package/dist/auth/credential_type_schema.d.ts +115 -0
- package/dist/auth/credential_type_schema.d.ts.map +1 -0
- package/dist/auth/credential_type_schema.js +127 -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 +28 -22
- package/dist/auth/ddl.d.ts +2 -2
- package/dist/auth/ddl.d.ts.map +1 -1
- package/dist/auth/ddl.js +6 -6
- package/dist/auth/deps.d.ts +7 -18
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/grant_path_schema.d.ts +117 -0
- package/dist/auth/grant_path_schema.d.ts.map +1 -0
- package/dist/auth/grant_path_schema.js +137 -0
- package/dist/auth/invite_queries.d.ts +12 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +12 -1
- package/dist/auth/invite_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +9 -4
- package/dist/auth/migrations.d.ts +37 -14
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +79 -32
- package/dist/auth/request_context.d.ts +331 -61
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +378 -95
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +163 -94
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_action_specs.js +262 -0
- package/dist/auth/role_grant_offer_actions.d.ts +104 -0
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_actions.js +473 -0
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +90 -70
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_notifications.js +182 -0
- package/dist/auth/role_grant_offer_queries.d.ts +242 -0
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_queries.js +533 -0
- package/dist/auth/role_grant_offer_schema.d.ts +150 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
- package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +60 -36
- package/dist/auth/role_grant_queries.d.ts +231 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_queries.js +320 -0
- package/dist/auth/role_schema.d.ts +150 -40
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +144 -45
- package/dist/auth/scope_kind_schema.d.ts +96 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -0
- package/dist/auth/scope_kind_schema.js +94 -0
- package/dist/auth/self_service_role_action_specs.d.ts +6 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +3 -1
- package/dist/auth/self_service_role_actions.d.ts +34 -27
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +68 -48
- package/dist/auth/session_cookie.d.ts +43 -6
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +31 -5
- package/dist/auth/session_middleware.d.ts +37 -3
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +33 -7
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +48 -19
- package/dist/auth/standard_action_specs.d.ts +2 -2
- package/dist/auth/standard_action_specs.js +4 -4
- package/dist/auth/standard_rpc_actions.d.ts +23 -19
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +12 -12
- package/dist/db/migrate.d.ts +12 -8
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +10 -7
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +9 -7
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +64 -5
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +38 -2
- package/dist/http/CLAUDE.md +264 -87
- package/dist/http/auth_shape.d.ts +191 -0
- package/dist/http/auth_shape.d.ts.map +1 -0
- package/dist/http/auth_shape.js +237 -0
- package/dist/http/common_routes.js +3 -3
- package/dist/http/db_routes.d.ts +4 -0
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +44 -7
- package/dist/http/error_schemas.d.ts +132 -19
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +132 -40
- 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/pending_effects.d.ts +71 -18
- package/dist/http/pending_effects.d.ts.map +1 -1
- package/dist/http/pending_effects.js +87 -18
- package/dist/http/proxy.d.ts +52 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +92 -14
- package/dist/http/route_spec.d.ts +113 -41
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +130 -52
- package/dist/http/schema_helpers.d.ts +3 -2
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +9 -2
- package/dist/http/surface.d.ts +2 -1
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -2
- package/dist/http/surface_query.d.ts +39 -35
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +79 -36
- package/dist/primitive_schemas.d.ts +39 -0
- package/dist/primitive_schemas.d.ts.map +1 -0
- package/dist/primitive_schemas.js +40 -0
- package/dist/realtime/sse_auth_guard.d.ts +5 -5
- package/dist/realtime/sse_auth_guard.js +9 -9
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +14 -11
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +12 -8
- package/dist/server/app_server.d.ts +7 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +36 -31
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +73 -55
- package/dist/testing/admin_integration.d.ts +5 -6
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +100 -96
- package/dist/testing/adversarial_headers.js +1 -1
- package/dist/testing/app_server.d.ts +11 -14
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +18 -17
- package/dist/testing/assertions.d.ts.map +1 -1
- package/dist/testing/assertions.js +2 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +15 -9
- package/dist/testing/audit_completeness.d.ts +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +53 -39
- package/dist/testing/auth_apps.d.ts +5 -4
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +28 -22
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +5 -5
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +4 -4
- package/dist/testing/db_entities.d.ts +22 -0
- package/dist/testing/db_entities.d.ts.map +1 -0
- package/dist/testing/db_entities.js +28 -0
- package/dist/testing/entities.d.ts +10 -8
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +22 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +8 -6
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +29 -23
- package/dist/testing/middleware.d.ts +15 -11
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +75 -32
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +3 -1
- package/dist/testing/rpc_round_trip.d.ts +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +14 -13
- package/dist/testing/sse_round_trip.d.ts +3 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +7 -11
- package/dist/testing/standard.d.ts +1 -1
- package/dist/testing/stubs.d.ts +25 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +43 -2
- package/dist/testing/surface_invariants.d.ts +2 -2
- package/dist/testing/ws_round_trip.d.ts +12 -13
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +24 -12
- package/dist/ui/AdminAccounts.svelte +23 -20
- package/dist/ui/AdminOverview.svelte +15 -13
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +1 -1
- package/dist/ui/CLAUDE.md +65 -59
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +37 -22
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts +20 -0
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
- package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
- package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +35 -15
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +2 -3
- package/dist/ui/admin_accounts_state.svelte.d.ts +25 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +28 -17
- package/dist/ui/admin_rpc_adapters.d.ts +20 -20
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +17 -17
- package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
- package/dist/ui/admin_sessions_state.svelte.js +2 -2
- package/dist/ui/audit_log_state.svelte.d.ts +7 -7
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +6 -6
- package/dist/ui/auth_state.svelte.d.ts +3 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +6 -6
- package/dist/ui/format_scope.d.ts +2 -2
- package/dist/ui/format_scope.js +2 -2
- package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +39 -31
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +25 -19
- package/dist/ui/ui_format.js +2 -2
- package/package.json +3 -3
- package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
- package/dist/auth/permit_offer_action_specs.js +0 -227
- package/dist/auth/permit_offer_actions.d.ts +0 -110
- package/dist/auth/permit_offer_actions.d.ts.map +0 -1
- package/dist/auth/permit_offer_actions.js +0 -452
- package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.js +0 -182
- package/dist/auth/permit_offer_queries.d.ts +0 -183
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_queries.js +0 -408
- package/dist/auth/permit_offer_schema.d.ts +0 -103
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -210
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -294
- package/dist/auth/require_keeper.d.ts +0 -20
- package/dist/auth/require_keeper.d.ts.map +0 -1
- package/dist/auth/require_keeper.js +0 -35
- package/dist/auth/route_guards.d.ts +0 -21
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -32
- package/dist/auth/session_lifecycle.d.ts +0 -37
- package/dist/auth/session_lifecycle.d.ts.map +0 -1
- package/dist/auth/session_lifecycle.js +0 -29
- package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferForm.svelte.d.ts +0 -14
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
package/dist/testing/stubs.js
CHANGED
|
@@ -14,6 +14,8 @@ import { prefix_route_specs } from '../http/route_spec.js';
|
|
|
14
14
|
import { create_bootstrap_route_specs } from '../auth/bootstrap_routes.js';
|
|
15
15
|
import { create_rpc_endpoint } from '../actions/action_rpc.js';
|
|
16
16
|
import { create_app_surface_spec, } from '../http/surface.js';
|
|
17
|
+
import { AUDIT_LOG_SSE_MAX_PER_SCOPE } from '../realtime/sse_auth_guard.js';
|
|
18
|
+
import { SubscriberRegistry } from '../realtime/subscriber_registry.js';
|
|
17
19
|
import { BaseServerEnv } from '../server/env.js';
|
|
18
20
|
/* eslint-disable @typescript-eslint/require-await */
|
|
19
21
|
/**
|
|
@@ -88,6 +90,45 @@ export const stub_handler = () => new Response('stub');
|
|
|
88
90
|
/** Stub middleware that passes through. */
|
|
89
91
|
export const stub_mw = async (_c, next) => next();
|
|
90
92
|
const stub_db = create_noop_stub('stub_db');
|
|
93
|
+
/**
|
|
94
|
+
* Build a no-op `AuditEmitter` for tests that don't assert on audit fan-out.
|
|
95
|
+
*
|
|
96
|
+
* `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves
|
|
97
|
+
* immediately; `notify` is a no-op; `on_event_chain` is a frozen empty
|
|
98
|
+
* array — pushing onto it throws at runtime, so a test that wires a
|
|
99
|
+
* listener fails loudly instead of silently never firing. Tests asserting
|
|
100
|
+
* on real audit-row persistence (or on listener fan-out) build a real
|
|
101
|
+
* emitter via `create_audit_emitter` against a stub or real DB —
|
|
102
|
+
* `create_test_app` already does this on the test backend.
|
|
103
|
+
*/
|
|
104
|
+
export const create_test_audit_emitter = () => ({
|
|
105
|
+
emit: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
106
|
+
emit_role_grant_target: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
107
|
+
emit_pool: async () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
108
|
+
notify: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
109
|
+
on_event_chain: Object.freeze([]),
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
113
|
+
* surface helper but don't assert on SSE fan-out or subscriber state.
|
|
114
|
+
*
|
|
115
|
+
* `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the
|
|
116
|
+
* `registry` is a fresh `SubscriberRegistry` instance (call sites that
|
|
117
|
+
* inspect `.size` or call `.close_*` see a real registry, so writes are
|
|
118
|
+
* isolated per test). Tests that need real SSE plumbing build it via
|
|
119
|
+
* `create_audit_log_sse` against `create_test_app`.
|
|
120
|
+
*/
|
|
121
|
+
export const create_stub_audit_sse = () => {
|
|
122
|
+
const registry = new SubscriberRegistry({
|
|
123
|
+
max_per_scope: AUDIT_LOG_SSE_MAX_PER_SCOPE,
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
subscribe: () => () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
127
|
+
log: new Logger('test:audit_sse', { level: 'off' }),
|
|
128
|
+
on_audit_event: () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
129
|
+
registry,
|
|
130
|
+
};
|
|
131
|
+
};
|
|
91
132
|
/** Stub `AppDeps` for auth surface tests — throws on any method access. */
|
|
92
133
|
export const stub_app_deps = {
|
|
93
134
|
stat: create_throwing_stub('stat'),
|
|
@@ -97,7 +138,7 @@ export const stub_app_deps = {
|
|
|
97
138
|
password: create_throwing_stub('password'),
|
|
98
139
|
db: create_throwing_stub('db'),
|
|
99
140
|
log: create_throwing_stub('log'),
|
|
100
|
-
|
|
141
|
+
audit: create_test_audit_emitter(),
|
|
101
142
|
};
|
|
102
143
|
/**
|
|
103
144
|
* Create no-op `AppDeps` for auth surface testing.
|
|
@@ -110,7 +151,7 @@ export const create_stub_app_deps = () => ({
|
|
|
110
151
|
password: create_noop_stub('password'),
|
|
111
152
|
db: stub_db,
|
|
112
153
|
log: new Logger('test', { level: 'off' }),
|
|
113
|
-
|
|
154
|
+
audit: create_test_audit_emitter(),
|
|
114
155
|
});
|
|
115
156
|
/** Create the API middleware stub array matching `create_auth_middleware_specs` output. */
|
|
116
157
|
export const create_stub_api_middleware = (options) => {
|
|
@@ -97,8 +97,8 @@ export interface SurfaceSecurityPolicyOptions {
|
|
|
97
97
|
/**
|
|
98
98
|
* Path patterns for routes that should be rate-limited.
|
|
99
99
|
* Default: common sensitive REST patterns (login, password, bootstrap).
|
|
100
|
-
* `account_token_create`
|
|
101
|
-
*
|
|
100
|
+
* `account_token_create` lives on the RPC surface; per-method RPC rate
|
|
101
|
+
* limiting is a separate invariant if consumers want it.
|
|
102
102
|
*/
|
|
103
103
|
sensitive_route_patterns?: Array<string | RegExp>;
|
|
104
104
|
/**
|
|
@@ -43,7 +43,7 @@ import { type Uuid } from '@fuzdev/fuz_util/id.js';
|
|
|
43
43
|
import type { ActionSpecUnion } from '../actions/action_spec.js';
|
|
44
44
|
import type { Action } from '../actions/action_types.js';
|
|
45
45
|
import type { ActionEventEnvironment } from '../actions/action_event_types.js';
|
|
46
|
-
import { type
|
|
46
|
+
import { type RegisterActionWsOptions } from '../actions/register_action_ws.js';
|
|
47
47
|
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
48
48
|
import { type RequestContext } from '../auth/request_context.js';
|
|
49
49
|
import { type CredentialType } from '../hono_context.js';
|
|
@@ -124,7 +124,7 @@ export interface WsConnectIdentity {
|
|
|
124
124
|
session_id?: string;
|
|
125
125
|
/** Api token id; set for bearer connections, null otherwise. */
|
|
126
126
|
api_token_id?: string | null;
|
|
127
|
-
/** Roles to grant via active
|
|
127
|
+
/** Roles to grant via active role_grants. Pass `[ROLE_KEEPER]` for keeper actions. */
|
|
128
128
|
roles?: Array<string>;
|
|
129
129
|
}
|
|
130
130
|
/** A mock WS client: send requests, inspect/await incoming messages. */
|
|
@@ -214,15 +214,14 @@ export declare const is_notification_with: <P>(method: string, match: (params: P
|
|
|
214
214
|
/** Predicate matching a JSON-RPC response frame (success or error) for the given request id. */
|
|
215
215
|
export declare const is_response_for: (id: number | string) => (msg: unknown) => boolean;
|
|
216
216
|
/** Options for `create_ws_test_harness`. */
|
|
217
|
-
export interface CreateWsTestHarnessOptions
|
|
217
|
+
export interface CreateWsTestHarnessOptions {
|
|
218
218
|
/**
|
|
219
219
|
* The actions registered on this endpoint — matches the shape
|
|
220
220
|
* `register_action_ws` accepts. Each entry is a `{spec, handler?}` tuple;
|
|
221
221
|
* shared fuz_app primitives (like `heartbeat_action`) can be spread in
|
|
222
222
|
* alongside consumer-specific actions.
|
|
223
223
|
*/
|
|
224
|
-
actions: ReadonlyArray<Action
|
|
225
|
-
extend_context?: RegisterActionWsOptions<TCtx>['extend_context'];
|
|
224
|
+
actions: ReadonlyArray<Action>;
|
|
226
225
|
/** Pass a pre-created transport to share with a broadcast API. */
|
|
227
226
|
transport?: BackendWebsocketTransport;
|
|
228
227
|
/**
|
|
@@ -230,13 +229,13 @@ export interface CreateWsTestHarnessOptions<TCtx extends BaseHandlerContext> {
|
|
|
230
229
|
* fake timers + receive-silence detection need explicit opt-in and per-
|
|
231
230
|
* test tuning to avoid spurious closes.
|
|
232
231
|
*/
|
|
233
|
-
heartbeat?: RegisterActionWsOptions
|
|
232
|
+
heartbeat?: RegisterActionWsOptions['heartbeat'];
|
|
234
233
|
/** Optional logger. Defaults to a silent `[ws-test]` logger. */
|
|
235
234
|
log?: Logger;
|
|
236
235
|
/** Threaded straight through to `register_action_ws`. */
|
|
237
|
-
on_socket_open?: RegisterActionWsOptions
|
|
236
|
+
on_socket_open?: RegisterActionWsOptions['on_socket_open'];
|
|
238
237
|
/** Threaded straight through to `register_action_ws`. */
|
|
239
|
-
on_socket_close?: RegisterActionWsOptions
|
|
238
|
+
on_socket_close?: RegisterActionWsOptions['on_socket_close'];
|
|
240
239
|
}
|
|
241
240
|
/** A harness instance — transport handle + connection factory. */
|
|
242
241
|
export interface WsTestHarness {
|
|
@@ -259,24 +258,24 @@ export interface WsTestHarness {
|
|
|
259
258
|
* auth identity. Returned clients drive the real
|
|
260
259
|
* `onOpen`/`onMessage`/`onClose` path against a real `WSContext`.
|
|
261
260
|
*/
|
|
262
|
-
export declare const create_ws_test_harness:
|
|
261
|
+
export declare const create_ws_test_harness: (options: CreateWsTestHarnessOptions) => WsTestHarness;
|
|
263
262
|
/** Convenience: default identity for keeper-authenticated connections. */
|
|
264
263
|
export declare const keeper_identity: () => WsConnectIdentity;
|
|
265
264
|
/**
|
|
266
265
|
* Wire a typed broadcast API against the harness's transport, matching
|
|
267
266
|
* how a consumer's real backend composes the stack. Returns the typed
|
|
268
|
-
* API so tests can call `.
|
|
267
|
+
* API so tests can call `.zap_run_created(...)` / `.workspace_changed(...)`
|
|
269
268
|
* etc. directly.
|
|
270
269
|
*
|
|
271
270
|
* ```ts
|
|
272
|
-
* const harness = create_ws_test_harness
|
|
271
|
+
* const harness = create_ws_test_harness({actions});
|
|
273
272
|
* const broadcast = build_broadcast_api<MyBackendActionsApi>({
|
|
274
273
|
* harness,
|
|
275
274
|
* specs: my_broadcast_action_specs,
|
|
276
275
|
* });
|
|
277
276
|
* const client = await harness.connect(keeper_identity());
|
|
278
|
-
* await broadcast.
|
|
279
|
-
* await client.wait_for(is_notification('
|
|
277
|
+
* await broadcast.zap_run_created({run_id: '...', ...});
|
|
278
|
+
* await client.wait_for(is_notification('zap_run_created'));
|
|
280
279
|
* ```
|
|
281
280
|
*/
|
|
282
281
|
export declare const build_broadcast_api: <TApi extends object>(options: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAO,MAAM,MAAM,CAAC;AACxC,OAAO,EACN,SAAS,EAET,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAC,MAAM,EAAC,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAc,KAAK,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAE9D,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,4BAA4B,CAAC;AAEvD,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,kCAAkC,CAAC;AAE7E,OAAO,
|
|
1
|
+
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/ws_round_trip.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,14 +41,15 @@ import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
|
41
41
|
import { create_uuid } from '@fuzdev/fuz_util/id.js';
|
|
42
42
|
import { ActionPeer } from '../actions/action_peer.js';
|
|
43
43
|
import { create_broadcast_api } from '../actions/broadcast_api.js';
|
|
44
|
-
import { register_action_ws
|
|
44
|
+
import { register_action_ws } from '../actions/register_action_ws.js';
|
|
45
|
+
import { create_stub_db } from './stubs.js';
|
|
45
46
|
import { BackendWebsocketTransport } from '../actions/transports_ws_backend.js';
|
|
46
47
|
import { REQUEST_CONTEXT_KEY } from '../auth/request_context.js';
|
|
47
48
|
import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
48
|
-
import { AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY } from '../hono_context.js';
|
|
49
|
+
import { ACCOUNT_ID_KEY, AUTH_API_TOKEN_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
|
|
49
50
|
import { JSONRPC_VERSION } from '../http/jsonrpc.js';
|
|
50
51
|
import { create_jsonrpc_request, is_jsonrpc_error_response, is_jsonrpc_notification, is_jsonrpc_response, } from '../http/jsonrpc_helpers.js';
|
|
51
|
-
import { create_test_account, create_test_actor,
|
|
52
|
+
import { create_test_account, create_test_actor, create_test_role_grant } from './entities.js';
|
|
52
53
|
/**
|
|
53
54
|
* Build a real `WSContext` backed by in-memory `send`/`close` capture.
|
|
54
55
|
* Parsing of outgoing frames is left to the caller — `sends` holds the
|
|
@@ -76,10 +77,12 @@ export const create_fake_ws = () => {
|
|
|
76
77
|
export const create_fake_hono_context = (opts) => {
|
|
77
78
|
const request_context = opts.request_context ?? build_simple_request_context(opts.role);
|
|
78
79
|
const vars = {
|
|
80
|
+
[ACCOUNT_ID_KEY]: request_context.account.id,
|
|
79
81
|
[REQUEST_CONTEXT_KEY]: request_context,
|
|
80
82
|
[CREDENTIAL_TYPE_KEY]: opts.credential_type,
|
|
81
83
|
auth_session_id: opts.auth_session_id ?? (opts.credential_type === 'session' ? 's1' : null),
|
|
82
84
|
[AUTH_API_TOKEN_ID_KEY]: opts.api_token_id ?? null,
|
|
85
|
+
[TEST_CONTEXT_PRESET_KEY]: true,
|
|
83
86
|
};
|
|
84
87
|
return {
|
|
85
88
|
get: (key) => vars[key],
|
|
@@ -158,7 +161,7 @@ export const is_notification_with = (method, match) => (msg) => is_jsonrpc_notif
|
|
|
158
161
|
export const is_response_for = (id) => (msg) => (is_jsonrpc_response(msg) || is_jsonrpc_error_response(msg)) && msg.id === id;
|
|
159
162
|
const DEFAULT_TIMEOUT_MS = 1000;
|
|
160
163
|
/**
|
|
161
|
-
* Build a `RequestContext` with a fresh UUID account/actor and
|
|
164
|
+
* Build a `RequestContext` with a fresh UUID account/actor and role_grants
|
|
162
165
|
* for the supplied roles. Used by the high-level harness so callers can
|
|
163
166
|
* pass `roles: [ROLE_KEEPER, 'admin']`.
|
|
164
167
|
*/
|
|
@@ -185,10 +188,11 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
185
188
|
updated_at: null,
|
|
186
189
|
updated_by: null,
|
|
187
190
|
},
|
|
188
|
-
|
|
191
|
+
role_grants: roles.map((role) => ({
|
|
189
192
|
id: create_uuid(),
|
|
190
193
|
actor_id,
|
|
191
194
|
role,
|
|
195
|
+
scope_kind: null,
|
|
192
196
|
scope_id: null,
|
|
193
197
|
created_at: now,
|
|
194
198
|
expires_at: null,
|
|
@@ -208,7 +212,7 @@ const build_multi_role_request_context = (account_id, roles) => {
|
|
|
208
212
|
const build_simple_request_context = (role) => ({
|
|
209
213
|
account: create_test_account({ id: 'acc_1', username: 'testuser' }),
|
|
210
214
|
actor: create_test_actor({ id: 'act_1', account_id: 'acc_1', name: 'testuser' }),
|
|
211
|
-
|
|
215
|
+
role_grants: role ? [create_test_role_grant({ id: 'perm_1', actor_id: 'act_1', role })] : [],
|
|
212
216
|
});
|
|
213
217
|
/**
|
|
214
218
|
* Create a WebSocket test harness for the given specs + handlers.
|
|
@@ -220,16 +224,22 @@ const build_simple_request_context = (role) => ({
|
|
|
220
224
|
* `onOpen`/`onMessage`/`onClose` path against a real `WSContext`.
|
|
221
225
|
*/
|
|
222
226
|
export const create_ws_test_harness = (options) => {
|
|
223
|
-
const { actions,
|
|
227
|
+
const { actions, transport = new BackendWebsocketTransport(), heartbeat = false, log = new Logger('[ws-test]', { level: 'off' }), on_socket_open, on_socket_close, } = options;
|
|
224
228
|
const stub = create_stub_upgrade();
|
|
225
229
|
// Minimal Hono stub — `register_action_ws` only needs `.get(path, handler)`.
|
|
226
230
|
const stub_app = { get: () => stub_app };
|
|
231
|
+
// Stub DB — the harness pre-bakes `RequestContext` via the test-preset
|
|
232
|
+
// escape hatch so `perform_action` skips the live authorization phase.
|
|
233
|
+
// `db.transaction(fn)` synchronously calls `fn(stub_db)` so handlers
|
|
234
|
+
// declaring `side_effects: true` execute under the same shape they
|
|
235
|
+
// would in production.
|
|
236
|
+
const stub_db = create_stub_db();
|
|
227
237
|
register_action_ws({
|
|
228
238
|
path: '/test/ws',
|
|
229
239
|
app: stub_app,
|
|
230
240
|
upgradeWebSocket: stub.upgradeWebSocket,
|
|
231
241
|
actions,
|
|
232
|
-
|
|
242
|
+
db: stub_db,
|
|
233
243
|
transport,
|
|
234
244
|
heartbeat,
|
|
235
245
|
log,
|
|
@@ -244,10 +254,12 @@ export const create_ws_test_harness = (options) => {
|
|
|
244
254
|
const api_token_id = identity.api_token_id ?? null;
|
|
245
255
|
const roles = identity.roles ?? [];
|
|
246
256
|
const ctx_store = new Map([
|
|
257
|
+
[ACCOUNT_ID_KEY, account_id],
|
|
247
258
|
[REQUEST_CONTEXT_KEY, build_multi_role_request_context(account_id, roles)],
|
|
248
259
|
[CREDENTIAL_TYPE_KEY, credential_type],
|
|
249
260
|
['auth_session_id', session_id],
|
|
250
261
|
[AUTH_API_TOKEN_ID_KEY, api_token_id],
|
|
262
|
+
[TEST_CONTEXT_PRESET_KEY, true],
|
|
251
263
|
]);
|
|
252
264
|
const fake_c = {
|
|
253
265
|
get: (key) => ctx_store.get(key),
|
|
@@ -379,18 +391,18 @@ const make_peer = () => new ActionPeer({ environment: new MinimalActionEnvironme
|
|
|
379
391
|
/**
|
|
380
392
|
* Wire a typed broadcast API against the harness's transport, matching
|
|
381
393
|
* how a consumer's real backend composes the stack. Returns the typed
|
|
382
|
-
* API so tests can call `.
|
|
394
|
+
* API so tests can call `.zap_run_created(...)` / `.workspace_changed(...)`
|
|
383
395
|
* etc. directly.
|
|
384
396
|
*
|
|
385
397
|
* ```ts
|
|
386
|
-
* const harness = create_ws_test_harness
|
|
398
|
+
* const harness = create_ws_test_harness({actions});
|
|
387
399
|
* const broadcast = build_broadcast_api<MyBackendActionsApi>({
|
|
388
400
|
* harness,
|
|
389
401
|
* specs: my_broadcast_action_specs,
|
|
390
402
|
* });
|
|
391
403
|
* const client = await harness.connect(keeper_identity());
|
|
392
|
-
* await broadcast.
|
|
393
|
-
* await client.wait_for(is_notification('
|
|
404
|
+
* await broadcast.zap_run_created({run_id: '...', ...});
|
|
405
|
+
* await client.wait_for(is_notification('zap_run_created'));
|
|
394
406
|
* ```
|
|
395
407
|
*/
|
|
396
408
|
export const build_broadcast_api = (options) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
* Admin accounts table — users with their
|
|
3
|
+
* Admin accounts table — users with their role_grants and pending offers.
|
|
4
4
|
* Consumes `admin_accounts_rpc_context` (read via `AdminAccountsState`)
|
|
5
5
|
* and `format_scope_context` for label rendering. Per-row actions:
|
|
6
|
-
* grant role (`
|
|
7
|
-
* keyed by `actor_id`), retract pending offer (`
|
|
6
|
+
* grant role (`role_grant_offer_create`), revoke role_grant (`role_grant_revoke`,
|
|
7
|
+
* keyed by `actor_id`), retract pending offer (`role_grant_offer_retract`).
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
10
10
|
*/
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
const get_format_scope = format_scope_context.get();
|
|
23
23
|
const format_scope = $derived(get_format_scope());
|
|
24
24
|
|
|
25
|
-
// `null` global label: global
|
|
25
|
+
// `null` global label: global role_grants render no scope chip — the implicit default in admin tables.
|
|
26
26
|
const scope_label = (scope_id: string | null, role: string): string | null =>
|
|
27
27
|
resolve_scope_label(scope_id, role, format_scope, null);
|
|
28
28
|
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
const columns: Array<DatatableColumn<AdminAccountEntryJson>> = [
|
|
32
32
|
{key: 'account', label: 'username', width: 180},
|
|
33
|
-
{key: '
|
|
33
|
+
{key: 'role_grants', label: 'role_grants', width: 240},
|
|
34
34
|
{key: 'actor', label: 'grant', width: 200},
|
|
35
35
|
];
|
|
36
36
|
</script>
|
|
@@ -72,31 +72,34 @@
|
|
|
72
72
|
updated {format_relative_time(row.account.updated_at)}
|
|
73
73
|
</div>
|
|
74
74
|
{/if}
|
|
75
|
-
{:else if column.key === '
|
|
76
|
-
{#each row.
|
|
77
|
-
{@const scope = scope_label(
|
|
75
|
+
{:else if column.key === 'role_grants'}
|
|
76
|
+
{#each row.role_grants as role_grant (role_grant.id)}
|
|
77
|
+
{@const scope = scope_label(role_grant.scope_id, role_grant.role)}
|
|
78
78
|
<div class="row">
|
|
79
|
-
<span class="chip color_b">{
|
|
79
|
+
<span class="chip color_b">{role_grant.role}</span>
|
|
80
80
|
{#if scope !== null}
|
|
81
|
-
<span class="text_50 font_size_sm" title={
|
|
81
|
+
<span class="text_50 font_size_sm" title={role_grant.scope_id ?? undefined}>
|
|
82
82
|
{scope}
|
|
83
83
|
</span>
|
|
84
84
|
{/if}
|
|
85
|
-
{#if
|
|
86
|
-
<span
|
|
87
|
-
|
|
85
|
+
{#if role_grant.expires_at}
|
|
86
|
+
<span
|
|
87
|
+
class="text_50 font_size_sm"
|
|
88
|
+
title={format_datetime_local(role_grant.expires_at)}
|
|
89
|
+
>
|
|
90
|
+
expires {format_relative_time(role_grant.expires_at)}
|
|
88
91
|
</span>
|
|
89
92
|
{/if}
|
|
90
93
|
{#if admin_accounts.has_rpc && row.actor}
|
|
91
94
|
{@const actor_id = row.actor.id}
|
|
92
95
|
<ConfirmButton
|
|
93
|
-
onconfirm={() => admin_accounts.
|
|
94
|
-
title="revoke {
|
|
96
|
+
onconfirm={() => admin_accounts.revoke_role_grant(actor_id, role_grant.id)}
|
|
97
|
+
title="revoke {role_grant.role}"
|
|
95
98
|
class="sm"
|
|
96
|
-
disabled={admin_accounts.revoking_ids.has(
|
|
99
|
+
disabled={admin_accounts.revoking_ids.has(role_grant.id)}
|
|
97
100
|
>
|
|
98
101
|
{#snippet children(_popover, _confirm)}
|
|
99
|
-
{admin_accounts.revoking_ids.has(
|
|
102
|
+
{admin_accounts.revoking_ids.has(role_grant.id) ? 'revoking…' : 'revoke'}
|
|
100
103
|
{/snippet}
|
|
101
104
|
</ConfirmButton>
|
|
102
105
|
{/if}
|
|
@@ -130,15 +133,15 @@
|
|
|
130
133
|
{/if}
|
|
131
134
|
</div>
|
|
132
135
|
{/each}
|
|
133
|
-
{#if row.
|
|
136
|
+
{#if row.role_grants.length === 0 && row.pending_offers.length === 0}
|
|
134
137
|
<span class="text_50">none</span>
|
|
135
138
|
{/if}
|
|
136
139
|
{:else if column.key === 'actor'}
|
|
137
140
|
{#if admin_accounts.has_rpc}
|
|
138
141
|
{#each admin_accounts.grantable_roles as role (role)}
|
|
139
|
-
{#if !row.
|
|
142
|
+
{#if !row.role_grants.some((p) => p.role === role) && !row.pending_offers.some((o) => o.role === role)}
|
|
140
143
|
<ConfirmButton
|
|
141
|
-
onconfirm={() => admin_accounts.
|
|
144
|
+
onconfirm={() => admin_accounts.create_role_grant(row.account.id, role)}
|
|
142
145
|
title="offer {role}"
|
|
143
146
|
class="sm"
|
|
144
147
|
disabled={admin_accounts.granting_keys.has(`${row.account.id}:${role}`)}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Admin dashboard — six summary panels (accounts, sessions, invites,
|
|
4
4
|
* recent activity, security, system) fed by parallel `fetch()` calls
|
|
5
5
|
* on mount. Consumes all four admin RPC contexts plus `auth_state_context`;
|
|
6
|
-
* derives `role_counts`, `failed_logins`, and `
|
|
6
|
+
* derives `role_counts`, `failed_logins`, and `role_grant_changes` from the
|
|
7
7
|
* audit log slice.
|
|
8
8
|
*
|
|
9
9
|
* @module
|
|
@@ -39,14 +39,16 @@
|
|
|
39
39
|
// eslint-disable-next-line svelte/prefer-svelte-reactivity
|
|
40
40
|
const counts = new Map<string, number>();
|
|
41
41
|
for (const entry of accounts.accounts) {
|
|
42
|
-
const roles = new Set(entry.
|
|
42
|
+
const roles = new Set(entry.role_grants.map((p) => p.role));
|
|
43
43
|
for (const role of roles) {
|
|
44
44
|
counts.set(role, (counts.get(role) || 0) + 1);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
return Array.from(counts.entries());
|
|
48
48
|
});
|
|
49
|
-
const unroled_count = $derived(
|
|
49
|
+
const unroled_count = $derived(
|
|
50
|
+
accounts.accounts.filter((a) => a.role_grants.length === 0).length,
|
|
51
|
+
);
|
|
50
52
|
|
|
51
53
|
// sessions
|
|
52
54
|
const unique_users = $derived(new Set(sessions.sessions.map((s) => s.username)).size);
|
|
@@ -63,9 +65,9 @@
|
|
|
63
65
|
const failed_logins = $derived(
|
|
64
66
|
audit_log.events.filter((e) => e.event_type === 'login' && e.outcome === 'failure'),
|
|
65
67
|
);
|
|
66
|
-
const
|
|
68
|
+
const role_grant_changes = $derived(
|
|
67
69
|
audit_log.events.filter(
|
|
68
|
-
(e) => e.event_type === '
|
|
70
|
+
(e) => e.event_type === 'role_grant_create' || e.event_type === 'role_grant_revoke',
|
|
69
71
|
),
|
|
70
72
|
);
|
|
71
73
|
|
|
@@ -108,10 +110,10 @@
|
|
|
108
110
|
{#each accounts.accounts.slice(0, 6) as entry (entry)}
|
|
109
111
|
<li>
|
|
110
112
|
<strong>{entry.account.username}</strong>
|
|
111
|
-
{#each entry.
|
|
112
|
-
<span class="chip font_size_sm">{
|
|
113
|
+
{#each entry.role_grants as role_grant (role_grant.id)}
|
|
114
|
+
<span class="chip font_size_sm">{role_grant.role}</span>
|
|
113
115
|
{/each}
|
|
114
|
-
{#if entry.
|
|
116
|
+
{#if entry.role_grants.length === 0}
|
|
115
117
|
<span class="text_50 font_size_sm">no roles</span>
|
|
116
118
|
{/if}
|
|
117
119
|
</li>
|
|
@@ -244,17 +246,17 @@
|
|
|
244
246
|
<span class="text_50">failed logins</span>
|
|
245
247
|
</div>
|
|
246
248
|
<div class="baseline-row gap_xs">
|
|
247
|
-
<strong class="font_size_lg">{
|
|
248
|
-
<span class="text_50">
|
|
249
|
+
<strong class="font_size_lg">{role_grant_changes.length}</strong>
|
|
250
|
+
<span class="text_50">role_grant changes</span>
|
|
249
251
|
</div>
|
|
250
|
-
{#if
|
|
252
|
+
{#if role_grant_changes.length > 0}
|
|
251
253
|
<ul class="compact-list">
|
|
252
|
-
{#each
|
|
254
|
+
{#each role_grant_changes.slice(0, 4) as event (event.id)}
|
|
253
255
|
<li class="font_size_sm">
|
|
254
256
|
<span class="text_50" title={format_datetime_local(event.created_at)}
|
|
255
257
|
>{format_relative_time(event.created_at)}</span
|
|
256
258
|
>
|
|
257
|
-
<code>{event.event_type === '
|
|
259
|
+
<code>{event.event_type === 'role_grant_create' ? 'grant' : 'revoke'}</code>
|
|
258
260
|
{#if event.metadata?.role}
|
|
259
261
|
<span class="chip font_size_sm">{event.metadata.role}</span>
|
|
260
262
|
{/if}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"AdminOverview.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminOverview.svelte"],"names":[],"mappings":"AA4UA,QAAA,MAAM,aAAa,2DAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* calls `audit_log.
|
|
5
|
-
* `
|
|
3
|
+
* Role grant create/revoke history table. Consumes `audit_log_rpc_context`,
|
|
4
|
+
* calls `audit_log.fetch_role_grant_history()` once on mount (the
|
|
5
|
+
* `audit_log_role_grant_history` RPC). Uses `format_scope_context` to render
|
|
6
6
|
* scope ids as human labels.
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import {format_relative_time, format_datetime_local, truncate_uuid} from './ui_format.js';
|
|
13
13
|
import Datatable from './Datatable.svelte';
|
|
14
14
|
import type {DatatableColumn} from './datatable.js';
|
|
15
|
-
import type {
|
|
15
|
+
import type {RoleGrantHistoryEventJson} from '../auth/audit_log_schema.js';
|
|
16
16
|
import {format_scope_context, resolve_scope_label} from './format_scope.js';
|
|
17
17
|
|
|
18
18
|
const get_rpc = audit_log_rpc_context.get();
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
const get_format_scope = format_scope_context.get();
|
|
21
21
|
const format_scope = $derived(get_format_scope());
|
|
22
22
|
|
|
23
|
-
void audit_log.
|
|
23
|
+
void audit_log.fetch_role_grant_history();
|
|
24
24
|
|
|
25
|
-
const columns: Array<DatatableColumn<
|
|
25
|
+
const columns: Array<DatatableColumn<RoleGrantHistoryEventJson>> = [
|
|
26
26
|
{key: 'event_type', label: 'action', width: 100},
|
|
27
27
|
{key: 'metadata', label: 'role', width: 160},
|
|
28
28
|
{key: 'username', label: 'by', width: 140},
|
|
@@ -38,22 +38,22 @@
|
|
|
38
38
|
</script>
|
|
39
39
|
|
|
40
40
|
<section>
|
|
41
|
-
<h1>
|
|
41
|
+
<h1>role_grant history</h1>
|
|
42
42
|
|
|
43
43
|
{#if audit_log.loading}
|
|
44
|
-
<p class="text_50">loading
|
|
44
|
+
<p class="text_50">loading role_grant history...</p>
|
|
45
45
|
{:else if audit_log.error}
|
|
46
46
|
<p class="color_c_50">{audit_log.error}</p>
|
|
47
47
|
{:else}
|
|
48
|
-
<Datatable {columns} rows={audit_log.
|
|
48
|
+
<Datatable {columns} rows={audit_log.role_grant_history_events} height="400px" row_key="id">
|
|
49
49
|
{#snippet cell(column, row)}
|
|
50
50
|
{#if column.key === 'event_type'}
|
|
51
51
|
<span
|
|
52
52
|
class="chip"
|
|
53
|
-
class:color_b={row.event_type === '
|
|
54
|
-
class:color_c={row.event_type === '
|
|
53
|
+
class:color_b={row.event_type === 'role_grant_create'}
|
|
54
|
+
class:color_c={row.event_type === 'role_grant_revoke'}
|
|
55
55
|
>
|
|
56
|
-
{row.event_type === '
|
|
56
|
+
{row.event_type === 'role_grant_create' ? 'grant' : 'revoke'}
|
|
57
57
|
</span>
|
|
58
58
|
{:else if column.key === 'metadata'}
|
|
59
59
|
{#if row.metadata}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AdminRoleGrantHistory.svelte.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/ui/AdminRoleGrantHistory.svelte"],"names":[],"mappings":"AAiGA,QAAA,MAAM,qBAAqB,2DAAwC,CAAC;AACpE,KAAK,qBAAqB,GAAG,UAAU,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACtE,eAAe,qBAAqB,CAAC"}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import PendingButton from '@fuzdev/fuz_ui/PendingButton.svelte';
|
|
18
18
|
import {autofocus} from '@fuzdev/fuz_ui/autofocus.svelte.js';
|
|
19
19
|
|
|
20
|
-
import {Username} from '../
|
|
20
|
+
import {Username} from '../primitive_schemas.js';
|
|
21
21
|
import {PASSWORD_LENGTH_MIN} from '../auth/password.js';
|
|
22
22
|
import {auth_state_context} from './auth_state.svelte.js';
|
|
23
23
|
import {FormState} from './form_state.svelte.js';
|