@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
|
@@ -5,12 +5,13 @@ import { ROLE_KEEPER } from '../auth/role_schema.js';
|
|
|
5
5
|
import { create_validated_keyring } from '../auth/keyring.js';
|
|
6
6
|
import { generate_api_token } from '../auth/api_token.js';
|
|
7
7
|
import { query_create_account_with_actor } from '../auth/account_queries.js';
|
|
8
|
-
import {
|
|
8
|
+
import { query_create_role_grant } from '../auth/role_grant_queries.js';
|
|
9
9
|
import { generate_session_token, hash_session_token, AUTH_SESSION_LIFETIME_MS, query_create_session, } from '../auth/session_queries.js';
|
|
10
10
|
import { query_create_api_token } from '../auth/api_token_queries.js';
|
|
11
11
|
import { create_session_cookie_value } from '../auth/session_cookie.js';
|
|
12
12
|
import { run_migrations } from '../db/migrate.js';
|
|
13
13
|
import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
|
|
14
|
+
import { create_audit_emitter } from '../auth/audit_emitter.js';
|
|
14
15
|
import { create_app_server, } from '../server/app_server.js';
|
|
15
16
|
import { generate_daemon_token, DAEMON_TOKEN_HEADER, } from '../auth/daemon_token.js';
|
|
16
17
|
import { create_pglite_factory } from './db.js';
|
|
@@ -42,7 +43,7 @@ const fallback_pglite_factory = create_pglite_factory(async (db) => {
|
|
|
42
43
|
* `create_test_app_server` and `TestApp.create_account`.
|
|
43
44
|
*
|
|
44
45
|
* @mutates the underlying `options.db` — inserts rows into `account`, `actor`,
|
|
45
|
-
* `
|
|
46
|
+
* `role_grant` (one per role), `api_token`, and `auth_session`.
|
|
46
47
|
*/
|
|
47
48
|
export const bootstrap_test_account = async (options) => {
|
|
48
49
|
const { db, keyring, session_options, password, username = 'keeper', password_value = 'test-password-123', roles = [], } = options;
|
|
@@ -54,12 +55,12 @@ export const bootstrap_test_account = async (options) => {
|
|
|
54
55
|
});
|
|
55
56
|
// Grant roles
|
|
56
57
|
for (const role of roles) {
|
|
57
|
-
await
|
|
58
|
+
await query_create_role_grant(deps, { actor_id: actor.id, role, granted_by: null });
|
|
58
59
|
}
|
|
59
|
-
// Create API token
|
|
60
|
+
// Create API token (account-scoped — acting actor is per-request)
|
|
60
61
|
const { token: api_token, id: token_id, token_hash } = generate_api_token();
|
|
61
62
|
await query_create_api_token(deps, token_id, account.id, 'test-cli', token_hash);
|
|
62
|
-
// Create session
|
|
63
|
+
// Create session (account-scoped — acting actor is per-request)
|
|
63
64
|
const session_token = generate_session_token();
|
|
64
65
|
const session_hash = hash_session_token(session_token);
|
|
65
66
|
const expires_at = new Date(Date.now() + AUTH_SESSION_LIFETIME_MS);
|
|
@@ -80,7 +81,7 @@ const test_log = new Logger('test', { level: 'off' });
|
|
|
80
81
|
* Sets up:
|
|
81
82
|
* - Auth tables (via cached PGlite factory, or reuses existing `db`)
|
|
82
83
|
* - A keeper account with hashed password
|
|
83
|
-
* - Role
|
|
84
|
+
* - Role role_grants for each role in `options.roles`
|
|
84
85
|
* - An API token for Bearer auth
|
|
85
86
|
* - A session with a signed cookie value
|
|
86
87
|
*
|
|
@@ -91,10 +92,8 @@ const test_log = new Logger('test', { level: 'off' });
|
|
|
91
92
|
* @returns a `TestAppServer` ready for HTTP testing
|
|
92
93
|
* @mutates the underlying database — when `db` is supplied, resets singleton
|
|
93
94
|
* state (`bootstrap_lock.bootstrapped`, `app_settings.open_signup`) before
|
|
94
|
-
* bootstrapping; in either branch inserts an account, actor, role
|
|
95
|
-
* API token, and session row.
|
|
96
|
-
* sets `backend.deps.audit_log_config` so `create_app_server`'s shallow
|
|
97
|
-
* spread picks it up.
|
|
95
|
+
* bootstrapping; in either branch inserts an account, actor, role role_grants,
|
|
96
|
+
* API token, and session row.
|
|
98
97
|
*/
|
|
99
98
|
export const create_test_app_server = async (options) => {
|
|
100
99
|
const { session_options, db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, username = 'keeper', password_value = 'test-password-123', roles = [ROLE_KEEPER], on_audit_event = () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
@@ -117,6 +116,12 @@ export const create_test_app_server = async (options) => {
|
|
|
117
116
|
await existing_db.query('UPDATE app_settings SET open_signup = false, updated_at = NULL, updated_by = NULL WHERE open_signup = true OR updated_at IS NOT NULL');
|
|
118
117
|
// Use the caller's database — tables already created by the factory's init_schema.
|
|
119
118
|
// Caller owns the DB lifecycle — close is a no-op.
|
|
119
|
+
const audit = create_audit_emitter({
|
|
120
|
+
db: existing_db,
|
|
121
|
+
log: test_log,
|
|
122
|
+
on_audit_event,
|
|
123
|
+
audit_log_config,
|
|
124
|
+
});
|
|
120
125
|
backend = {
|
|
121
126
|
db_type,
|
|
122
127
|
db_name: 'test',
|
|
@@ -127,7 +132,7 @@ export const create_test_app_server = async (options) => {
|
|
|
127
132
|
password,
|
|
128
133
|
db: existing_db,
|
|
129
134
|
log: test_log,
|
|
130
|
-
|
|
135
|
+
audit,
|
|
131
136
|
...fs_stubs,
|
|
132
137
|
},
|
|
133
138
|
};
|
|
@@ -137,6 +142,7 @@ export const create_test_app_server = async (options) => {
|
|
|
137
142
|
// instead of creating a new PGlite each time. Schema is reset and migrations re-run
|
|
138
143
|
// on each call, but the expensive WASM cold start only happens once per worker thread.
|
|
139
144
|
const db = await fallback_pglite_factory.create();
|
|
145
|
+
const audit = create_audit_emitter({ db, log: test_log, on_audit_event, audit_log_config });
|
|
140
146
|
backend = {
|
|
141
147
|
db_type: 'pglite-memory',
|
|
142
148
|
db_name: '(memory)',
|
|
@@ -147,7 +153,7 @@ export const create_test_app_server = async (options) => {
|
|
|
147
153
|
password,
|
|
148
154
|
db,
|
|
149
155
|
log: test_log,
|
|
150
|
-
|
|
156
|
+
audit,
|
|
151
157
|
...fs_stubs,
|
|
152
158
|
},
|
|
153
159
|
};
|
|
@@ -161,11 +167,6 @@ export const create_test_app_server = async (options) => {
|
|
|
161
167
|
password_value,
|
|
162
168
|
roles,
|
|
163
169
|
});
|
|
164
|
-
// Land before `create_app_server`'s shallow-spread of `backend.deps` so
|
|
165
|
-
// the SSE branch's snapshot picks it up alongside the no-SSE alias branch.
|
|
166
|
-
if (audit_log_config !== undefined) {
|
|
167
|
-
backend.deps.audit_log_config = audit_log_config;
|
|
168
|
-
}
|
|
169
170
|
return {
|
|
170
171
|
...backend,
|
|
171
172
|
...bootstrapped,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assertions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,UAAU,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"assertions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/assertions.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,UAAU,EAAE,eAAe,EAAC,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,0BAA0B,CAAC;AAGhE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,UAAU,MAAM,EAAE,iBAAiB,MAAM,KAAG,MACtB,CAAC;AAE5D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,+BAA+B,GAC3C,SAAS,UAAU,EACnB,eAAe,MAAM,KACnB,IAOF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,4BAA4B,GAAI,eAAe,MAAM,UAAU,KAAG,IAE9E,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,kCAAkC,GAC9C,SAAS,UAAU,EACnB,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,IAWF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,GAClC,QAAQ,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACtC,OAAO,eAAe,EACtB,QAAQ,MAAM,KACZ,CAAC,CAAC,OAAO,GAAG,SAGd,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB,GACrC,QAAQ,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACtC,OAAO,eAAe,EACtB,QAAQ,MAAM,EACd,MAAM,OAAO,KACX,IAIF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,UAAU,EACnB,aAAa,MAAM,EACnB,qBAAqB,KAAK,CAAC,MAAM,CAAC,KAChC,IAUF,CAAC"}
|
|
@@ -11,6 +11,7 @@ import { readFileSync } from 'node:fs';
|
|
|
11
11
|
import { resolve, dirname } from 'node:path';
|
|
12
12
|
import { fileURLToPath } from 'node:url';
|
|
13
13
|
import { assert } from 'vitest';
|
|
14
|
+
import { is_public_auth } from '../http/auth_shape.js';
|
|
14
15
|
/**
|
|
15
16
|
* Resolve an absolute path relative to the caller's module.
|
|
16
17
|
*
|
|
@@ -52,7 +53,7 @@ export const assert_surface_deterministic = (build_surface) => {
|
|
|
52
53
|
export const assert_only_expected_public_routes = (surface, expected_public) => {
|
|
53
54
|
const expected = new Set(expected_public);
|
|
54
55
|
const actual_public = surface.routes
|
|
55
|
-
.filter((r) => r.auth
|
|
56
|
+
.filter((r) => is_public_auth(r.auth))
|
|
56
57
|
.map((r) => `${r.method} ${r.path}`);
|
|
57
58
|
const unexpected = actual_public.filter((r) => !expected.has(r));
|
|
58
59
|
const missing = expected_public.filter((r) => !actual_public.includes(r));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAON,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAON,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAoBjC,OAAO,EAA4B,KAAK,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAsClF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IA4H3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,gHAAgH;IAChH,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;CAC5D;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IAuEF,CAAC"}
|
|
@@ -23,7 +23,7 @@ import { assert_surface_matches_snapshot, assert_surface_deterministic, assert_o
|
|
|
23
23
|
import { merge_error_schemas } from '../http/schema_helpers.js';
|
|
24
24
|
import { collect_middleware_errors } from '../http/surface.js';
|
|
25
25
|
import { filter_protected_routes, filter_role_routes, filter_keeper_routes, } from '../http/surface_query.js';
|
|
26
|
-
import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INSUFFICIENT_PERMISSIONS,
|
|
26
|
+
import { ERROR_AUTHENTICATION_REQUIRED, ERROR_INSUFFICIENT_PERMISSIONS, ERROR_CREDENTIAL_TYPE_REQUIRED, } from '../http/error_schemas.js';
|
|
27
27
|
// --- Adversarial test runner ---
|
|
28
28
|
/**
|
|
29
29
|
* Build a lookup from `"METHOD /path"` to merged error schemas (auto-derived + middleware + explicit).
|
|
@@ -79,12 +79,17 @@ export const describe_adversarial_auth = (options) => {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
|
-
|
|
82
|
+
// Role-only routes (no credential gate). Keeper routes have a credential
|
|
83
|
+
// gate that fires before the role gate, so they get tested separately
|
|
84
|
+
// in the keeper block below.
|
|
85
|
+
const role_only_routes = role_routes.filter((r) => !(r.auth.credential_types?.length ?? 0));
|
|
86
|
+
if (role_only_routes.length > 0) {
|
|
83
87
|
describe('wrong role → 403', () => {
|
|
84
|
-
for (const route of
|
|
85
|
-
const
|
|
88
|
+
for (const route of role_only_routes) {
|
|
89
|
+
const required_roles = route.auth.roles ?? [];
|
|
90
|
+
const wrong_roles = roles.filter((r) => !required_roles.includes(r));
|
|
86
91
|
for (const wrong_role of wrong_roles) {
|
|
87
|
-
test(`${route.method} ${route.path} (${wrong_role} instead of ${
|
|
92
|
+
test(`${route.method} ${route.path} (${wrong_role} instead of ${required_roles.join('|')})`, async () => {
|
|
88
93
|
const app = apps.by_role.get(wrong_role);
|
|
89
94
|
if (!app)
|
|
90
95
|
throw new Error(`No test app for role '${wrong_role}'`);
|
|
@@ -94,14 +99,14 @@ export const describe_adversarial_auth = (options) => {
|
|
|
94
99
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
95
100
|
const body = await res.json();
|
|
96
101
|
assert.strictEqual(body.error, ERROR_INSUFFICIENT_PERMISSIONS);
|
|
97
|
-
assert.
|
|
102
|
+
assert.deepStrictEqual(body.required_roles, required_roles);
|
|
98
103
|
assert_error_schema_valid(error_schema_lookup, route, 403, body);
|
|
99
104
|
});
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
});
|
|
103
108
|
describe('authenticated without role → 403', () => {
|
|
104
|
-
for (const route of
|
|
109
|
+
for (const route of role_only_routes) {
|
|
105
110
|
test(`${route.method} ${route.path}`, async () => {
|
|
106
111
|
const res = await apps.authed.request(resolve_test_path(route.path), {
|
|
107
112
|
method: route.method,
|
|
@@ -109,7 +114,7 @@ export const describe_adversarial_auth = (options) => {
|
|
|
109
114
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
110
115
|
const body = await res.json();
|
|
111
116
|
assert.strictEqual(body.error, ERROR_INSUFFICIENT_PERMISSIONS);
|
|
112
|
-
assert.
|
|
117
|
+
assert.deepStrictEqual(body.required_roles, route.auth.roles ?? []);
|
|
113
118
|
assert_error_schema_valid(error_schema_lookup, route, 403, body);
|
|
114
119
|
});
|
|
115
120
|
}
|
|
@@ -126,7 +131,8 @@ export const describe_adversarial_auth = (options) => {
|
|
|
126
131
|
});
|
|
127
132
|
assert.strictEqual(res.status, 403, `${route.method} ${route.path}`);
|
|
128
133
|
const body = await res.json();
|
|
129
|
-
assert.strictEqual(body.error,
|
|
134
|
+
assert.strictEqual(body.error, ERROR_CREDENTIAL_TYPE_REQUIRED);
|
|
135
|
+
assert.deepStrictEqual(body.required_credential_types, route.auth.credential_types ?? []);
|
|
130
136
|
assert_error_schema_valid(error_schema_lookup, route, 403, body);
|
|
131
137
|
});
|
|
132
138
|
}
|
|
@@ -15,7 +15,7 @@ export interface AuditCompletenessTestOptions {
|
|
|
15
15
|
create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
|
|
16
16
|
/**
|
|
17
17
|
* RPC endpoint specs — the source `RpcAction` arrays. Required; the
|
|
18
|
-
* admin
|
|
18
|
+
* admin role_grant flow is RPC-only and the suite hard-fails without it.
|
|
19
19
|
*
|
|
20
20
|
* Accepts either an array (eager) or a factory
|
|
21
21
|
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
|
|
@@ -39,7 +39,7 @@ export interface AuditCompletenessTestOptions {
|
|
|
39
39
|
* database, then queries the `audit_log` table to verify events.
|
|
40
40
|
*
|
|
41
41
|
* @throws Error at setup time when `options.rpc_endpoints` is empty — the
|
|
42
|
-
* mutation-audit tests drive
|
|
42
|
+
* mutation-audit tests drive role_grant flow, session/token revoke-all, and
|
|
43
43
|
* invite create/delete through their RPC action specs. Hard-fails via
|
|
44
44
|
* `require_rpc_endpoint_path`.
|
|
45
45
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAoDD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAkgBzF,CAAC"}
|
|
@@ -20,12 +20,11 @@ import { create_test_app, } from './app_server.js';
|
|
|
20
20
|
import { create_pglite_factory, create_describe_db, AUTH_INTEGRATION_TRUNCATE_TABLES, } from './db.js';
|
|
21
21
|
import { find_auth_route } from './integration_helpers.js';
|
|
22
22
|
import { run_migrations } from '../db/migrate.js';
|
|
23
|
-
import { query_accept_offer } from '../auth/
|
|
23
|
+
import { query_accept_offer } from '../auth/role_grant_offer_queries.js';
|
|
24
24
|
import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
25
|
-
import {
|
|
25
|
+
import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec, } from '../auth/role_grant_offer_action_specs.js';
|
|
26
26
|
import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
|
|
27
27
|
import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
|
|
28
|
-
import { query_actor_by_account } from '../auth/account_queries.js';
|
|
29
28
|
/** Query audit log events from the database. */
|
|
30
29
|
const query_audit_events = async (db) => {
|
|
31
30
|
return db.query('SELECT event_type, seq FROM audit_log ORDER BY seq');
|
|
@@ -64,7 +63,7 @@ const json_session_headers = (test_app, extra) => test_app.create_session_header
|
|
|
64
63
|
* database, then queries the `audit_log` table to verify events.
|
|
65
64
|
*
|
|
66
65
|
* @throws Error at setup time when `options.rpc_endpoints` is empty — the
|
|
67
|
-
* mutation-audit tests drive
|
|
66
|
+
* mutation-audit tests drive role_grant flow, session/token revoke-all, and
|
|
68
67
|
* invite create/delete through their RPC action specs. Hard-fails via
|
|
69
68
|
* `require_rpc_endpoint_path`.
|
|
70
69
|
*/
|
|
@@ -232,58 +231,73 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
232
231
|
});
|
|
233
232
|
// --- Admin routes ---
|
|
234
233
|
describe('admin mutation audit events', () => {
|
|
235
|
-
test('admin offer (RPC) + accept produces
|
|
234
|
+
test('admin offer (RPC) + accept produces role_grant_offer_create and role_grant_create events', async () => {
|
|
236
235
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
237
236
|
const target = await test_app.create_account({ username: 'audit_target' });
|
|
238
237
|
const offer_res = await rpc_call_for_spec({
|
|
239
238
|
app: test_app.app,
|
|
240
239
|
path: rpc_path,
|
|
241
|
-
spec:
|
|
240
|
+
spec: role_grant_offer_create_action_spec,
|
|
242
241
|
params: { to_account_id: target.account.id, role: ROLE_ADMIN },
|
|
243
242
|
headers: test_app.create_session_headers(),
|
|
244
243
|
});
|
|
245
|
-
assert.ok(offer_res.ok, `
|
|
244
|
+
assert.ok(offer_res.ok, `role_grant_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
|
|
246
245
|
const { offer } = offer_res.result;
|
|
247
|
-
// Admin offer emits `
|
|
248
|
-
// exist yet. Drive the accept to confirm `
|
|
246
|
+
// Admin offer emits `role_grant_offer_create` only — the role_grant doesn't
|
|
247
|
+
// exist yet. Drive the accept to confirm `role_grant_create` fires on the
|
|
249
248
|
// downstream consent transition.
|
|
250
249
|
const events_after_offer = await query_audit_events(test_app.backend.deps.db);
|
|
251
|
-
assert_has_event(events_after_offer, '
|
|
250
|
+
assert_has_event(events_after_offer, 'role_grant_offer_create', 'role_grant_offer_create RPC');
|
|
252
251
|
await get_db().transaction(async (tx) => {
|
|
253
|
-
await query_accept_offer({ db: tx }, {
|
|
252
|
+
await query_accept_offer({ db: tx }, {
|
|
253
|
+
offer_id: offer.id,
|
|
254
|
+
to_account_id: target.account.id,
|
|
255
|
+
actor_id: target.actor.id,
|
|
256
|
+
ip: null,
|
|
257
|
+
});
|
|
254
258
|
});
|
|
255
259
|
const events_after_accept = await query_audit_events(test_app.backend.deps.db);
|
|
256
|
-
assert_has_event(events_after_accept, '
|
|
260
|
+
assert_has_event(events_after_accept, 'role_grant_create', 'offer accept');
|
|
257
261
|
});
|
|
258
|
-
test('
|
|
262
|
+
test('role_grant revoke (RPC) produces role_grant_revoke event with both target columns', async () => {
|
|
259
263
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
260
264
|
const target = await test_app.create_account({ username: 'audit_revoke_target' });
|
|
261
|
-
|
|
262
|
-
assert.ok(target_actor);
|
|
263
|
-
// Offer + accept to materialize a permit we can revoke.
|
|
265
|
+
// Offer + accept to materialize a role_grant we can revoke.
|
|
264
266
|
const offer_res = await rpc_call_for_spec({
|
|
265
267
|
app: test_app.app,
|
|
266
268
|
path: rpc_path,
|
|
267
|
-
spec:
|
|
269
|
+
spec: role_grant_offer_create_action_spec,
|
|
268
270
|
params: { to_account_id: target.account.id, role: ROLE_ADMIN },
|
|
269
271
|
headers: test_app.create_session_headers(),
|
|
270
272
|
});
|
|
271
|
-
assert.ok(offer_res.ok, `
|
|
273
|
+
assert.ok(offer_res.ok, `role_grant_offer_create failed: ${offer_res.ok ? '' : JSON.stringify(offer_res.error)}`);
|
|
272
274
|
const { offer } = offer_res.result;
|
|
273
275
|
const accept_result = await get_db().transaction(async (tx) => {
|
|
274
|
-
return query_accept_offer({ db: tx }, {
|
|
276
|
+
return query_accept_offer({ db: tx }, {
|
|
277
|
+
offer_id: offer.id,
|
|
278
|
+
to_account_id: target.account.id,
|
|
279
|
+
actor_id: target.actor.id,
|
|
280
|
+
ip: null,
|
|
281
|
+
});
|
|
275
282
|
});
|
|
276
283
|
// Revoke via RPC.
|
|
277
284
|
const revoke_res = await rpc_call_for_spec({
|
|
278
285
|
app: test_app.app,
|
|
279
286
|
path: rpc_path,
|
|
280
|
-
spec:
|
|
281
|
-
params: { actor_id:
|
|
287
|
+
spec: role_grant_revoke_action_spec,
|
|
288
|
+
params: { actor_id: target.actor.id, role_grant_id: accept_result.role_grant.id },
|
|
282
289
|
headers: test_app.create_session_headers(),
|
|
283
290
|
});
|
|
284
|
-
assert.ok(revoke_res.ok, `
|
|
291
|
+
assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
|
|
285
292
|
const events = await query_audit_events(test_app.backend.deps.db);
|
|
286
|
-
assert_has_event(events, '
|
|
293
|
+
assert_has_event(events, 'role_grant_revoke', 'role_grant_revoke RPC');
|
|
294
|
+
// Audit envelope must populate both target columns —
|
|
295
|
+
// `role_grant_revoke` is the canonical actor-bound-subject event.
|
|
296
|
+
const revoke_rows = await test_app.backend.deps.db.query(`SELECT target_account_id, target_actor_id FROM audit_log
|
|
297
|
+
WHERE event_type = 'role_grant_revoke' ORDER BY seq DESC LIMIT 1`);
|
|
298
|
+
const row = revoke_rows[0];
|
|
299
|
+
assert.strictEqual(row.target_account_id, target.account.id);
|
|
300
|
+
assert.strictEqual(row.target_actor_id, target.actor.id);
|
|
287
301
|
});
|
|
288
302
|
test('admin session revoke-all produces session_revoke_all event', async () => {
|
|
289
303
|
const test_app = await create_test_app(build_options(options, get_db()));
|
|
@@ -402,9 +416,9 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
402
416
|
'token_create',
|
|
403
417
|
'token_revoke',
|
|
404
418
|
'token_revoke_all',
|
|
405
|
-
'
|
|
406
|
-
'
|
|
407
|
-
'
|
|
419
|
+
'role_grant_offer_create',
|
|
420
|
+
'role_grant_create',
|
|
421
|
+
'role_grant_revoke',
|
|
408
422
|
'invite_create',
|
|
409
423
|
'invite_delete',
|
|
410
424
|
'app_settings_update',
|
|
@@ -412,20 +426,20 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
412
426
|
/** Event types excluded with justification. */
|
|
413
427
|
const EXCLUDED_EVENT_TYPES = new Set([
|
|
414
428
|
'bootstrap', // requires filesystem token — tested in bootstrap_account.db.test.ts
|
|
415
|
-
// The remaining `
|
|
416
|
-
// endpoint or via downstream effects of `
|
|
417
|
-
// coverage lives in `
|
|
418
|
-
// `
|
|
419
|
-
// `
|
|
420
|
-
// `
|
|
421
|
-
// `
|
|
422
|
-
// (`
|
|
429
|
+
// The remaining `role_grant_offer_*` events fire only via the RPC
|
|
430
|
+
// endpoint or via downstream effects of `role_grant_revoke`. Direct
|
|
431
|
+
// coverage lives in `role_grant_offer_queries.db.test.ts`,
|
|
432
|
+
// `role_grant_offer_actions.db.test.ts`,
|
|
433
|
+
// `role_grant_offer_actions.notifications.db.test.ts`, and
|
|
434
|
+
// `role_grant_offer_actions.notifications.revoke.db.test.ts`.
|
|
435
|
+
// `role_grant_offer_expire` fires from the cleanup sweep
|
|
436
|
+
// (`cleanup_expired_role_grant_offers` in `auth/cleanup.ts`) —
|
|
423
437
|
// covered in `cleanup.db.test.ts`.
|
|
424
|
-
'
|
|
425
|
-
'
|
|
426
|
-
'
|
|
427
|
-
'
|
|
428
|
-
'
|
|
438
|
+
'role_grant_offer_accept',
|
|
439
|
+
'role_grant_offer_decline',
|
|
440
|
+
'role_grant_offer_retract',
|
|
441
|
+
'role_grant_offer_expire',
|
|
442
|
+
'role_grant_offer_supersede',
|
|
429
443
|
]);
|
|
430
444
|
test('all audit event types are covered or explicitly excluded', () => {
|
|
431
445
|
const all_covered = new Set([...COVERED_EVENT_TYPES, ...EXCLUDED_EVENT_TYPES]);
|
|
@@ -8,11 +8,12 @@ import './assert_dev_env.js';
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
import { Hono } from 'hono';
|
|
11
|
-
import { type RouteSpec
|
|
11
|
+
import { type RouteSpec } from '../http/route_spec.js';
|
|
12
|
+
import { type RouteAuth } from '../http/auth_shape.js';
|
|
12
13
|
import { type RequestContext } from '../auth/request_context.js';
|
|
13
14
|
import { type CredentialType } from '../hono_context.js';
|
|
14
15
|
/**
|
|
15
|
-
* Create a mock `RequestContext` with optional role
|
|
16
|
+
* Create a mock `RequestContext` with optional role role_grant.
|
|
16
17
|
*/
|
|
17
18
|
export declare const create_test_request_context: (role?: string) => RequestContext;
|
|
18
19
|
/**
|
|
@@ -40,8 +41,8 @@ export declare const create_auth_test_apps: (route_specs: Array<RouteSpec>, role
|
|
|
40
41
|
/**
|
|
41
42
|
* Select the Hono test app with correct auth for a route.
|
|
42
43
|
*
|
|
43
|
-
* @throws Error if `auth.
|
|
44
|
-
*
|
|
44
|
+
* @throws Error if `auth.roles` names a role not present in `apps.by_role` —
|
|
45
|
+
* surfaces a missing entry in the `roles` array passed to
|
|
45
46
|
* `create_auth_test_apps`.
|
|
46
47
|
*/
|
|
47
48
|
export declare const select_auth_app: (apps: AuthTestApps, auth: RouteAuth) => Hono;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth_apps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/auth_apps.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAG1B,OAAO,EAAoB,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"auth_apps.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/auth_apps.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAG1B,OAAO,EAAoB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAiB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAErE,OAAO,EAGN,KAAK,cAAc,EACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAIN,KAAK,cAAc,EACnB,MAAM,oBAAoB,CAAC;AAI5B;;GAEG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,MAAM,KAAG,cAI1D,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACtC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,WAAW,cAAc,EACzB,kBAAkB,cAAc,KAC9B,IAuBF,CAAC;AAEF,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC5B,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,MAAM,EAAE,IAAI,CAAC;IACb,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,GACjC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,OAAO,KAAK,CAAC,MAAM,CAAC,KAClB,YAeF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,MAAM,YAAY,EAAE,MAAM,SAAS,KAAG,IAarE,CAAC;AAEF,6EAA6E;AAC7E,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAA4C,CAAC"}
|
|
@@ -10,18 +10,19 @@ import './assert_dev_env.js';
|
|
|
10
10
|
import { Hono } from 'hono';
|
|
11
11
|
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
12
12
|
import { apply_route_specs } from '../http/route_spec.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
13
|
+
import { is_public_auth } from '../http/auth_shape.js';
|
|
14
|
+
import { fuz_auth_guard_resolver } from '../auth/auth_guard_resolver.js';
|
|
15
|
+
import { REQUEST_CONTEXT_KEY, create_fuz_authorization_handler, } from '../auth/request_context.js';
|
|
16
|
+
import { ACCOUNT_ID_KEY, CREDENTIAL_TYPE_KEY, TEST_CONTEXT_PRESET_KEY, } from '../hono_context.js';
|
|
16
17
|
import { create_stub_db } from './stubs.js';
|
|
17
|
-
import { create_test_account, create_test_actor,
|
|
18
|
+
import { create_test_account, create_test_actor, create_test_role_grant } from './entities.js';
|
|
18
19
|
/**
|
|
19
|
-
* Create a mock `RequestContext` with optional role
|
|
20
|
+
* Create a mock `RequestContext` with optional role role_grant.
|
|
20
21
|
*/
|
|
21
22
|
export const create_test_request_context = (role) => ({
|
|
22
23
|
account: create_test_account({ id: 'acc_1', username: 'testuser' }),
|
|
23
24
|
actor: create_test_actor({ id: 'act_1', account_id: 'acc_1', name: 'testuser' }),
|
|
24
|
-
|
|
25
|
+
role_grants: role ? [create_test_role_grant({ id: 'perm_1', actor_id: 'act_1', role })] : [],
|
|
25
26
|
});
|
|
26
27
|
/**
|
|
27
28
|
* Create a Hono test app from route specs with optional auth context.
|
|
@@ -32,15 +33,19 @@ export const create_test_request_context = (role) => ({
|
|
|
32
33
|
*/
|
|
33
34
|
export const create_test_app_from_specs = (route_specs, auth_ctx, credential_type) => {
|
|
34
35
|
const app = new Hono();
|
|
36
|
+
const db = create_stub_db();
|
|
35
37
|
app.use('/*', async (c, next) => {
|
|
36
38
|
c.set('pending_effects', []);
|
|
39
|
+
c.set('post_commit_effects', []);
|
|
37
40
|
if (auth_ctx) {
|
|
41
|
+
c.set(ACCOUNT_ID_KEY, auth_ctx.account.id);
|
|
38
42
|
c.set(REQUEST_CONTEXT_KEY, auth_ctx);
|
|
39
43
|
c.set(CREDENTIAL_TYPE_KEY, credential_type ?? 'session');
|
|
44
|
+
c.set(TEST_CONTEXT_PRESET_KEY, true);
|
|
40
45
|
}
|
|
41
46
|
await next();
|
|
42
47
|
});
|
|
43
|
-
apply_route_specs(app, route_specs, fuz_auth_guard_resolver, new Logger('test', { level: 'off' }),
|
|
48
|
+
apply_route_specs(app, route_specs, fuz_auth_guard_resolver, new Logger('test', { level: 'off' }), db, create_fuz_authorization_handler({ db }));
|
|
44
49
|
return app;
|
|
45
50
|
};
|
|
46
51
|
/**
|
|
@@ -64,25 +69,26 @@ export const create_auth_test_apps = (route_specs, roles) => {
|
|
|
64
69
|
/**
|
|
65
70
|
* Select the Hono test app with correct auth for a route.
|
|
66
71
|
*
|
|
67
|
-
* @throws Error if `auth.
|
|
68
|
-
*
|
|
72
|
+
* @throws Error if `auth.roles` names a role not present in `apps.by_role` —
|
|
73
|
+
* surfaces a missing entry in the `roles` array passed to
|
|
69
74
|
* `create_auth_test_apps`.
|
|
70
75
|
*/
|
|
71
76
|
export const select_auth_app = (apps, auth) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
77
|
+
if (is_public_auth(auth))
|
|
78
|
+
return apps.public;
|
|
79
|
+
if (auth.credential_types?.includes('daemon_token'))
|
|
80
|
+
return apps.keeper;
|
|
81
|
+
if (auth.roles?.length) {
|
|
82
|
+
// Multi-role disjunction: any of the named roles admits the caller.
|
|
83
|
+
// Tests pick the first role's app; consumers wanting per-role coverage
|
|
84
|
+
// should hit each role's app explicitly.
|
|
85
|
+
const role = auth.roles[0];
|
|
86
|
+
const app = apps.by_role.get(role);
|
|
87
|
+
if (!app)
|
|
88
|
+
throw new Error(`No test app for role '${role}' — is it in the roles array?`);
|
|
89
|
+
return app;
|
|
85
90
|
}
|
|
91
|
+
return apps.authed;
|
|
86
92
|
};
|
|
87
93
|
/** Replace Hono route params (`:foo`) with dummy values for HTTP testing. */
|
|
88
94
|
export const resolve_test_path = (path) => path.replace(/:(\w+)/g, 'test_$1');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgB7B,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgB7B,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAE,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAChF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG9D,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAgB9D;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,QAAQ,OAAO,KAAG,GAAG,CAAC,MAAM,CAuB9E,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,UAAU,EACnB,mBAAkB,aAAa,CAAC,MAAM,CAA6B,KACjE,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,UAAU,EACnB,oBAAmB,aAAa,CAAC,MAAM,CAA8B,KACnE,IAcF,CAAC;AAIF,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACvC,4DAA4D;IAC5D,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,wCAAwC;IACxC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4CAA4C;IAC5C,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,iDAAiD;IACjD,WAAW,CAAC,EAAE,OAAO,CACpB,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,CAAC,CAC5E,CAAC;IACF,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IAmC/E,CAAC"}
|
|
@@ -18,6 +18,7 @@ import { resolve_valid_path, generate_valid_body } from './schema_generators.js'
|
|
|
18
18
|
import { run_migrations } from '../db/migrate.js';
|
|
19
19
|
import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
|
|
20
20
|
import { is_null_schema, is_strict_object_schema } from '../http/schema_helpers.js';
|
|
21
|
+
import { is_keeper_auth, is_public_auth } from '../http/auth_shape.js';
|
|
21
22
|
import { SENSITIVE_FIELD_BLOCKLIST, ADMIN_ONLY_FIELD_BLOCKLIST, assert_no_sensitive_fields_in_json, pick_auth_headers, } from './integration_helpers.js';
|
|
22
23
|
// --- Schema introspection ---
|
|
23
24
|
/**
|
|
@@ -71,7 +72,7 @@ export const assert_output_schemas_no_sensitive_fields = (surface, sensitive_fie
|
|
|
71
72
|
* Assert that non-admin route output schemas don't contain admin-only fields.
|
|
72
73
|
*/
|
|
73
74
|
export const assert_non_admin_schemas_no_admin_fields = (surface, admin_only_fields = ADMIN_ONLY_FIELD_BLOCKLIST) => {
|
|
74
|
-
const non_admin = surface.routes.filter((r) => r.auth
|
|
75
|
+
const non_admin = surface.routes.filter((r) => !is_keeper_auth(r.auth) && !(r.auth.roles?.includes('admin') ?? false));
|
|
75
76
|
for (const route of non_admin) {
|
|
76
77
|
if (route.output_schema === null)
|
|
77
78
|
continue;
|
|
@@ -154,7 +155,7 @@ const describe_data_exposure_runtime_tests = (options) => {
|
|
|
154
155
|
// Tests that don't fire authenticated requests run first — they don't
|
|
155
156
|
// invalidate sessions and are independent of test order.
|
|
156
157
|
test('unauthenticated error responses contain no sensitive fields', async () => {
|
|
157
|
-
const protected_specs = test_app.route_specs.filter((s) => s.auth
|
|
158
|
+
const protected_specs = test_app.route_specs.filter((s) => !is_public_auth(s.auth));
|
|
158
159
|
for (const spec of protected_specs) {
|
|
159
160
|
const route_key = `${spec.method} ${spec.path}`;
|
|
160
161
|
if (skip_set.has(route_key))
|
|
@@ -181,7 +182,7 @@ const describe_data_exposure_runtime_tests = (options) => {
|
|
|
181
182
|
// Cross-privilege test runs before 2xx tests — admin routes reject
|
|
182
183
|
// without calling handlers, so sessions stay intact.
|
|
183
184
|
test('admin routes return 403 for non-admin user', async () => {
|
|
184
|
-
const admin_specs = test_app.route_specs.filter((s) => s.auth.
|
|
185
|
+
const admin_specs = test_app.route_specs.filter((s) => s.auth.roles?.includes('admin') ?? false);
|
|
185
186
|
for (const spec of admin_specs) {
|
|
186
187
|
const route_key = `${spec.method} ${spec.path}`;
|
|
187
188
|
if (skip_set.has(route_key))
|
|
@@ -221,8 +222,7 @@ const describe_data_exposure_runtime_tests = (options) => {
|
|
|
221
222
|
if (skip_set.has(route_key))
|
|
222
223
|
continue;
|
|
223
224
|
// keeper auth (daemon token) is strictly more privileged than admin
|
|
224
|
-
const is_elevated = spec.auth
|
|
225
|
-
(spec.auth.type === 'role' && spec.auth.role === 'admin');
|
|
225
|
+
const is_elevated = is_keeper_auth(spec.auth) || (spec.auth.roles?.includes('admin') ?? false);
|
|
226
226
|
const url = resolve_valid_path(spec.path, spec.params);
|
|
227
227
|
const body = generate_valid_body(spec.input);
|
|
228
228
|
const headers = pick_auth_headers(spec, test_app, authed_account, admin_account);
|
package/dist/testing/db.d.ts
CHANGED
|
@@ -79,7 +79,7 @@ export declare const AUTH_INTEGRATION_TRUNCATE_TABLES: string[];
|
|
|
79
79
|
*
|
|
80
80
|
* When adding tables to `AUTH_MIGRATIONS`, add them here too.
|
|
81
81
|
*/
|
|
82
|
-
export declare const AUTH_DROP_TABLES: readonly ["app_settings", "invite", "audit_log", "api_token", "auth_session", "
|
|
82
|
+
export declare const AUTH_DROP_TABLES: readonly ["app_settings", "invite", "audit_log", "api_token", "auth_session", "role_grant", "role_grant_offer", "actor", "account", "bootstrap_lock"];
|
|
83
83
|
/**
|
|
84
84
|
* Drop all auth tables and schema version tracking for a clean slate.
|
|
85
85
|
*
|
package/dist/testing/db.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA6B7B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAKpC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAMF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAG,SAkB7E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SA2DF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA6B7B,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAKpC;;GAEG;AACH,eAAO,MAAM,KAAK,SAA4B,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1B,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAGvD,CAAC;AAMF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,qBAAqB,GAAI,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,KAAG,SAkB7E,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,iBAAiB,GAC7B,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,EACtC,WAAW,MAAM,KACf,SA2DF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,UAQhC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,UAAyC,CAAC;AAEvF;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,uJAWnB,CAAC;AAEX;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAU,IAAI,EAAE,KAAG,OAAO,CAAC,IAAI,CAK3D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,kBAAkB,GAC9B,WAAW,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,EACvC,iBAAiB,KAAK,CAAC,MAAM,CAAC,KAC5B,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,IAAI,KAAK,IAAI,CAwBzD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,WAAW,KAAK,CAAC,SAAS,CAAC,KAAG,IAMnE,CAAC"}
|