@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/auth/role_schema.js
CHANGED
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Role system — builtin roles, role
|
|
2
|
+
* Role system — builtin roles, role specs, and extensible role schema factory.
|
|
3
3
|
*
|
|
4
|
-
* Defines the authorization policy vocabulary: which roles exist,
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Defines the authorization policy vocabulary: which roles exist, their
|
|
5
|
+
* required credential types, the scope kinds each role applies to, and
|
|
6
|
+
* the grant paths through which each role can be granted. Each role
|
|
7
|
+
* gets a structured `RoleSpec`; the factory `create_role_schema` merges
|
|
8
|
+
* builtins with consumer-declared specs and validates every cross-axis
|
|
9
|
+
* field against the corresponding open registries
|
|
10
|
+
* (`create_credential_type_schema`, `create_scope_kind_schema`,
|
|
11
|
+
* `create_grant_path_schema`) at construction time so misconfigurations
|
|
12
|
+
* fire at server startup, not at first call.
|
|
13
|
+
*
|
|
14
|
+
* `RoleSpec` carries the four cross-axis fields that the dispatcher
|
|
15
|
+
* branches on: credential type, scope kind, grant path, and the
|
|
16
|
+
* role-name itself. v1 keeps the cross-axis fields informative-only
|
|
17
|
+
* (registry-membership validation, no INSERT-time enforcement); v2 may
|
|
18
|
+
* add `(role, scope_kind)` enforcement once the shape is clear from
|
|
19
|
+
* real consumer usage.
|
|
7
20
|
*
|
|
8
21
|
* @module
|
|
9
22
|
*/
|
|
10
23
|
import { z } from 'zod';
|
|
24
|
+
import { CREDENTIAL_TYPE_DAEMON_TOKEN, } from './credential_type_schema.js';
|
|
25
|
+
import { GRANT_PATH_ADMIN, GRANT_PATH_BOOTSTRAP, } from './grant_path_schema.js';
|
|
11
26
|
/** Valid role name: lowercase letters and underscores, no leading/trailing underscore. */
|
|
12
27
|
export const RoleName = z
|
|
13
28
|
.string()
|
|
@@ -15,67 +30,151 @@ export const RoleName = z
|
|
|
15
30
|
// Builtin roles — provided by fuz_app, always available.
|
|
16
31
|
/** System-level role. Requires daemon token (filesystem proof). Controls the keep. */
|
|
17
32
|
export const ROLE_KEEPER = 'keeper';
|
|
18
|
-
/** App-level administrative role.
|
|
33
|
+
/** App-level administrative role. Granted via the admin path. */
|
|
19
34
|
export const ROLE_ADMIN = 'admin';
|
|
20
35
|
/** The builtin role names as a const tuple. */
|
|
21
36
|
export const BUILTIN_ROLES = [ROLE_KEEPER, ROLE_ADMIN];
|
|
22
37
|
/** Zod schema for builtin roles only. */
|
|
23
38
|
export const BuiltinRole = z.enum(BUILTIN_ROLES);
|
|
24
39
|
/**
|
|
25
|
-
* Builtin role
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* action factories; runtime mutation has no effect on already-built role
|
|
32
|
-
* schemas.
|
|
40
|
+
* Builtin role specs, keyed by role name. Not overridable by consumers
|
|
41
|
+
* — read once at startup by `create_role_schema` and the action
|
|
42
|
+
* factories that fall back to builtins when no consumer `roles` is
|
|
43
|
+
* supplied. `ReadonlyMap` encodes the contract; runtime mutation has
|
|
44
|
+
* no effect on already-built role schemas (the factory copies entries
|
|
45
|
+
* into a fresh `Map`).
|
|
33
46
|
*/
|
|
34
|
-
export const
|
|
35
|
-
[
|
|
36
|
-
|
|
47
|
+
export const BUILTIN_ROLE_SPECS_BY_NAME = new Map([
|
|
48
|
+
[
|
|
49
|
+
ROLE_KEEPER,
|
|
50
|
+
{
|
|
51
|
+
name: ROLE_KEEPER,
|
|
52
|
+
description: 'System-level role; controls the keep. Requires the daemon-token credential and lands via the bootstrap grant path.',
|
|
53
|
+
required_credential_types: [CREDENTIAL_TYPE_DAEMON_TOKEN],
|
|
54
|
+
applicable_scope_kinds: [],
|
|
55
|
+
grant_paths: [GRANT_PATH_BOOTSTRAP],
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
[
|
|
59
|
+
ROLE_ADMIN,
|
|
60
|
+
{
|
|
61
|
+
name: ROLE_ADMIN,
|
|
62
|
+
description: 'App-level administrative role. Web-grantable through the admin path; manages users and content.',
|
|
63
|
+
required_credential_types: [],
|
|
64
|
+
applicable_scope_kinds: [],
|
|
65
|
+
grant_paths: [GRANT_PATH_ADMIN],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
37
68
|
]);
|
|
69
|
+
const validate_registry_membership = (role_name, field, values, registry) => {
|
|
70
|
+
if (!registry || !values)
|
|
71
|
+
return;
|
|
72
|
+
for (const value of values) {
|
|
73
|
+
if (!registry.has(value)) {
|
|
74
|
+
throw new Error(`Role "${role_name}" declares ${field}="${value}" which is not a registered ${field.replace(/s$/, '')}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
};
|
|
38
78
|
/**
|
|
39
|
-
* Create a role schema and
|
|
79
|
+
* Create a role schema and spec map that extends the builtins with
|
|
80
|
+
* app-defined roles.
|
|
81
|
+
*
|
|
82
|
+
* Call once at server init. The returned `Role` schema validates role
|
|
83
|
+
* strings at I/O boundaries (grant endpoint, role_grant queries). The
|
|
84
|
+
* `role_specs` map is read by middleware for `required_credential_types`
|
|
85
|
+
* checks and by admin / self-service factories to derive their default
|
|
86
|
+
* eligibility filters from `RoleSpec.grant_paths`.
|
|
40
87
|
*
|
|
41
|
-
*
|
|
42
|
-
* at I/O boundaries (grant endpoint, permit queries). The `role_options` map
|
|
43
|
-
* is used by middleware to check `requires_daemon_token` and by admin UI to
|
|
44
|
-
* filter `web_grantable` roles.
|
|
88
|
+
* Construction-time guards (all fire on misconfiguration):
|
|
45
89
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
90
|
+
* 1. Every `consumer_roles[i].name` matches `RoleName` regex.
|
|
91
|
+
* 2. No two consumer roles share a name.
|
|
92
|
+
* 3. No consumer role collides with a builtin (`keeper` / `admin`).
|
|
93
|
+
* 4. When `options.credential_types` is supplied, every entry in
|
|
94
|
+
* `required_credential_types` is registered in that map.
|
|
95
|
+
* 5. When `options.scope_kinds` is supplied, every entry in
|
|
96
|
+
* `applicable_scope_kinds` is registered in that map. (Builtins
|
|
97
|
+
* declare empty `applicable_scope_kinds`, so they pass any registry.)
|
|
98
|
+
* 6. When `options.grant_paths` is supplied, every entry in
|
|
99
|
+
* `grant_paths` is registered in that map. (Builtins use only
|
|
100
|
+
* `'admin'` and `'bootstrap'`, both of which are builtin grant
|
|
101
|
+
* paths, so they pass the default registry from
|
|
102
|
+
* `create_grant_path_schema()`.)
|
|
48
103
|
*
|
|
49
|
-
* @
|
|
104
|
+
* @param consumer_roles - app-defined role specs
|
|
105
|
+
* @param options - optional registries for cross-axis validation
|
|
106
|
+
* @returns `{Role, role_specs}` — Zod schema and full spec map
|
|
107
|
+
*
|
|
108
|
+
* @throws Error if any `consumer_roles` entry fails any of the construction-time guards above
|
|
50
109
|
*
|
|
51
110
|
* @example
|
|
52
111
|
* ```ts
|
|
53
|
-
* // visiones
|
|
54
|
-
* const
|
|
55
|
-
*
|
|
112
|
+
* // visiones — opt into all four registries for full construction-time validation
|
|
113
|
+
* const credential_types = create_credential_type_schema();
|
|
114
|
+
* const scope_kinds = create_scope_kind_schema({
|
|
115
|
+
* classroom: {description: 'A classroom — teacher and student role_grants scope here.'},
|
|
56
116
|
* });
|
|
57
|
-
*
|
|
58
|
-
*
|
|
117
|
+
* const grant_paths = create_grant_path_schema();
|
|
118
|
+
*
|
|
119
|
+
* const {Role, role_specs} = create_role_schema(
|
|
120
|
+
* [
|
|
121
|
+
* {
|
|
122
|
+
* name: 'teacher',
|
|
123
|
+
* description: 'Educator role. Web-grantable; applies at classroom scope.',
|
|
124
|
+
* grant_paths: ['admin'],
|
|
125
|
+
* applicable_scope_kinds: ['classroom'],
|
|
126
|
+
* },
|
|
127
|
+
* ],
|
|
128
|
+
* {credential_types, scope_kinds, grant_paths},
|
|
129
|
+
* );
|
|
59
130
|
* ```
|
|
60
131
|
*/
|
|
61
|
-
export const create_role_schema = (
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
132
|
+
export const create_role_schema = (consumer_roles, options = {}) => {
|
|
133
|
+
const credential_types_registry = options.credential_types?.credential_types ?? null;
|
|
134
|
+
const scope_kinds_registry = options.scope_kinds?.scope_kinds ?? null;
|
|
135
|
+
const grant_paths_registry = options.grant_paths?.grant_paths ?? null;
|
|
136
|
+
const seen = new Set();
|
|
137
|
+
for (const spec of consumer_roles) {
|
|
138
|
+
const parsed = RoleName.safeParse(spec.name);
|
|
139
|
+
if (!parsed.success) {
|
|
140
|
+
throw new Error(`Invalid role name "${spec.name}": ${parsed.error.issues[0].message}`);
|
|
68
141
|
}
|
|
142
|
+
if (BUILTIN_ROLE_SPECS_BY_NAME.has(spec.name)) {
|
|
143
|
+
throw new Error(`App role "${spec.name}" collides with builtin role`);
|
|
144
|
+
}
|
|
145
|
+
if (seen.has(spec.name)) {
|
|
146
|
+
throw new Error(`Duplicate role name "${spec.name}"`);
|
|
147
|
+
}
|
|
148
|
+
seen.add(spec.name);
|
|
149
|
+
validate_registry_membership(spec.name, 'required_credential_types', spec.required_credential_types, credential_types_registry);
|
|
150
|
+
validate_registry_membership(spec.name, 'applicable_scope_kinds', spec.applicable_scope_kinds, scope_kinds_registry);
|
|
151
|
+
validate_registry_membership(spec.name, 'grant_paths', spec.grant_paths, grant_paths_registry);
|
|
69
152
|
}
|
|
70
|
-
const
|
|
153
|
+
const role_specs = new Map(BUILTIN_ROLE_SPECS_BY_NAME);
|
|
154
|
+
for (const spec of consumer_roles) {
|
|
155
|
+
role_specs.set(spec.name, spec);
|
|
156
|
+
}
|
|
157
|
+
const all_names = [...role_specs.keys()];
|
|
71
158
|
const Role = z.enum(all_names);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
159
|
+
return { Role, role_specs };
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Predicate over a `RoleSpec` map: does the named role include the given
|
|
163
|
+
* grant path? Returns `false` for unknown roles. Used by
|
|
164
|
+
* `admin_actions.create_admin_actions` (path = `'admin'`) and
|
|
165
|
+
* `self_service_role_actions.create_self_service_role_actions` (path =
|
|
166
|
+
* `'self_service'`) to derive their default eligibility filters.
|
|
167
|
+
*/
|
|
168
|
+
export const role_has_grant_path = (role_specs, role, grant_path) => {
|
|
169
|
+
const spec = role_specs.get(role);
|
|
170
|
+
return !!spec?.grant_paths?.includes(grant_path);
|
|
171
|
+
};
|
|
172
|
+
/** Filter helper: list every role whose `grant_paths` includes the given path. */
|
|
173
|
+
export const list_roles_with_grant_path = (role_specs, grant_path) => {
|
|
174
|
+
const out = [];
|
|
175
|
+
for (const [name, spec] of role_specs) {
|
|
176
|
+
if (spec.grant_paths?.includes(grant_path))
|
|
177
|
+
out.push(name);
|
|
79
178
|
}
|
|
80
|
-
return
|
|
179
|
+
return out;
|
|
81
180
|
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope-kind registry for role_grants and role_grant offers.
|
|
3
|
+
*
|
|
4
|
+
* Role grants have a polymorphic `scope_id` that references whatever entity
|
|
5
|
+
* the consumer chooses (a classroom, a tenant, a workspace, etc.); the
|
|
6
|
+
* `scope_kind` column tags each row with a machine-readable kind so
|
|
7
|
+
* admin UIs, codegen, and (in v2) registry-time `(role, scope_kind)`
|
|
8
|
+
* compatibility checks can read it without re-deriving from `scope_id`.
|
|
9
|
+
*
|
|
10
|
+
* `scope_kind` is encoded as nullable paired with the existing nullable
|
|
11
|
+
* `scope_id` — both null for global, both non-null for scoped, mismatch
|
|
12
|
+
* rejected at the DB layer by the `role_grant_scope_kind_paired` /
|
|
13
|
+
* `role_grant_offer_scope_kind_paired` CHECK constraints. There is no
|
|
14
|
+
* `'global'` magic-string value; the global case is unambiguously
|
|
15
|
+
* `(scope_kind=NULL, scope_id=NULL)`.
|
|
16
|
+
*
|
|
17
|
+
* Open registry, no builtins. Consumers declare their kinds via
|
|
18
|
+
* `create_scope_kind_schema(consumer_kinds)` and pass the result to
|
|
19
|
+
* `create_role_schema` so `RoleSpec.applicable_scope_kinds` can be
|
|
20
|
+
* validated at construction time. Mirrors the open-string registry
|
|
21
|
+
* pattern used for `RoleName`, `AuditEventTypeName`, and `CredentialType`.
|
|
22
|
+
*
|
|
23
|
+
* The literal `'GLOBAL'` (uppercase) appears as an index expression
|
|
24
|
+
* inside the partial unique indexes on `role_grant` and `role_grant_offer`
|
|
25
|
+
* (`COALESCE(scope_kind, 'GLOBAL')`) — never as a column value, never
|
|
26
|
+
* as a registry entry. The uppercase form is structurally distinct
|
|
27
|
+
* from any consumer-declared kind (which match the lowercase
|
|
28
|
+
* `ScopeKindName` regex), so it cannot collide.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import { z } from 'zod';
|
|
33
|
+
/**
|
|
34
|
+
* Letter (lowercase a-z) start and end (or single letter), with letters
|
|
35
|
+
* and underscores in between. Mirrors `RoleName`. Rejects empty strings,
|
|
36
|
+
* leading or trailing underscores, uppercase, digits, and the index-side
|
|
37
|
+
* `'GLOBAL'` token.
|
|
38
|
+
*/
|
|
39
|
+
export declare const SCOPE_KIND_NAME_REGEX: RegExp;
|
|
40
|
+
/** Zod schema for valid scope-kind name strings. */
|
|
41
|
+
export declare const ScopeKindName: z.ZodString;
|
|
42
|
+
export type ScopeKindName = z.infer<typeof ScopeKindName>;
|
|
43
|
+
/**
|
|
44
|
+
* Per-scope-kind metadata. `description` is admin-UI-facing copy
|
|
45
|
+
* (mirrors `RoleSpec.description`). Open shape so v2 can extend without
|
|
46
|
+
* a breaking change.
|
|
47
|
+
*/
|
|
48
|
+
export interface ScopeKindMeta {
|
|
49
|
+
description?: string;
|
|
50
|
+
}
|
|
51
|
+
/** The result of `create_scope_kind_schema` — a Zod schema and metadata map. */
|
|
52
|
+
export interface ScopeKindSchemaResult {
|
|
53
|
+
/**
|
|
54
|
+
* Zod schema that validates scope-kind name strings against the
|
|
55
|
+
* registered set. Use at I/O boundaries (admin UIs, codegen) and as
|
|
56
|
+
* the construction-time check inside `create_role_schema` for every
|
|
57
|
+
* `RoleSpec.applicable_scope_kinds` entry.
|
|
58
|
+
*/
|
|
59
|
+
ScopeKind: z.ZodType<string>;
|
|
60
|
+
/**
|
|
61
|
+
* Map of every registered scope-kind to its metadata. Keyed by name.
|
|
62
|
+
* Read at startup by admin / codegen surfaces.
|
|
63
|
+
*/
|
|
64
|
+
scope_kinds: ReadonlyMap<string, ScopeKindMeta>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Create a scope-kind schema from a consumer-declared registry.
|
|
68
|
+
*
|
|
69
|
+
* Open registry — no builtins. The `'GLOBAL'` token used inside the
|
|
70
|
+
* partial unique indexes on `role_grant` and `role_grant_offer` is not a
|
|
71
|
+
* registry entry (it's an index expression only) and cannot collide
|
|
72
|
+
* with consumer-declared kinds because the regex rejects uppercase.
|
|
73
|
+
*
|
|
74
|
+
* Call once at server init. Pass the result into `create_role_schema`'s
|
|
75
|
+
* optional `scope_kinds` parameter so each role's
|
|
76
|
+
* `applicable_scope_kinds` entries are validated against this set at
|
|
77
|
+
* construction time. v1 keeps `applicable_scope_kinds` informative-only
|
|
78
|
+
* (registry-membership validation only); v2 may add INSERT-time
|
|
79
|
+
* `(role, scope_kind)` enforcement once the shape is clear from real
|
|
80
|
+
* consumer usage.
|
|
81
|
+
*
|
|
82
|
+
* @param consumer_kinds - the consumer-declared scope-kind set with optional metadata
|
|
83
|
+
* @returns `{ScopeKind, scope_kinds}` — Zod schema and metadata map
|
|
84
|
+
*
|
|
85
|
+
* @throws Error if any `consumer_kinds` key fails the `ScopeKindName` regex or appears more than once
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* // visiones
|
|
90
|
+
* const {ScopeKind, scope_kinds} = create_scope_kind_schema({
|
|
91
|
+
* classroom: {description: 'A classroom — teacher and student role_grants scope here.'},
|
|
92
|
+
* });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare const create_scope_kind_schema: (consumer_kinds: Record<string, ScopeKindMeta>) => ScopeKindSchemaResult;
|
|
96
|
+
//# sourceMappingURL=scope_kind_schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope_kind_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/scope_kind_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,QAAgC,CAAC;AAEnE,oDAAoD;AACpD,eAAO,MAAM,aAAa,aAKxB,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAE1D;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,qBAAqB;IACrC;;;;;OAKG;IACH,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7B;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,wBAAwB,GACpC,gBAAgB,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,KAC3C,qBA0BF,CAAC"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scope-kind registry for role_grants and role_grant offers.
|
|
3
|
+
*
|
|
4
|
+
* Role grants have a polymorphic `scope_id` that references whatever entity
|
|
5
|
+
* the consumer chooses (a classroom, a tenant, a workspace, etc.); the
|
|
6
|
+
* `scope_kind` column tags each row with a machine-readable kind so
|
|
7
|
+
* admin UIs, codegen, and (in v2) registry-time `(role, scope_kind)`
|
|
8
|
+
* compatibility checks can read it without re-deriving from `scope_id`.
|
|
9
|
+
*
|
|
10
|
+
* `scope_kind` is encoded as nullable paired with the existing nullable
|
|
11
|
+
* `scope_id` — both null for global, both non-null for scoped, mismatch
|
|
12
|
+
* rejected at the DB layer by the `role_grant_scope_kind_paired` /
|
|
13
|
+
* `role_grant_offer_scope_kind_paired` CHECK constraints. There is no
|
|
14
|
+
* `'global'` magic-string value; the global case is unambiguously
|
|
15
|
+
* `(scope_kind=NULL, scope_id=NULL)`.
|
|
16
|
+
*
|
|
17
|
+
* Open registry, no builtins. Consumers declare their kinds via
|
|
18
|
+
* `create_scope_kind_schema(consumer_kinds)` and pass the result to
|
|
19
|
+
* `create_role_schema` so `RoleSpec.applicable_scope_kinds` can be
|
|
20
|
+
* validated at construction time. Mirrors the open-string registry
|
|
21
|
+
* pattern used for `RoleName`, `AuditEventTypeName`, and `CredentialType`.
|
|
22
|
+
*
|
|
23
|
+
* The literal `'GLOBAL'` (uppercase) appears as an index expression
|
|
24
|
+
* inside the partial unique indexes on `role_grant` and `role_grant_offer`
|
|
25
|
+
* (`COALESCE(scope_kind, 'GLOBAL')`) — never as a column value, never
|
|
26
|
+
* as a registry entry. The uppercase form is structurally distinct
|
|
27
|
+
* from any consumer-declared kind (which match the lowercase
|
|
28
|
+
* `ScopeKindName` regex), so it cannot collide.
|
|
29
|
+
*
|
|
30
|
+
* @module
|
|
31
|
+
*/
|
|
32
|
+
import { z } from 'zod';
|
|
33
|
+
/**
|
|
34
|
+
* Letter (lowercase a-z) start and end (or single letter), with letters
|
|
35
|
+
* and underscores in between. Mirrors `RoleName`. Rejects empty strings,
|
|
36
|
+
* leading or trailing underscores, uppercase, digits, and the index-side
|
|
37
|
+
* `'GLOBAL'` token.
|
|
38
|
+
*/
|
|
39
|
+
export const SCOPE_KIND_NAME_REGEX = /^[a-z][a-z_]*[a-z]$|^[a-z]$/;
|
|
40
|
+
/** Zod schema for valid scope-kind name strings. */
|
|
41
|
+
export const ScopeKindName = z
|
|
42
|
+
.string()
|
|
43
|
+
.regex(SCOPE_KIND_NAME_REGEX, 'Scope-kind names must be lowercase letters and underscores (a-z_), no leading/trailing underscore');
|
|
44
|
+
/**
|
|
45
|
+
* Create a scope-kind schema from a consumer-declared registry.
|
|
46
|
+
*
|
|
47
|
+
* Open registry — no builtins. The `'GLOBAL'` token used inside the
|
|
48
|
+
* partial unique indexes on `role_grant` and `role_grant_offer` is not a
|
|
49
|
+
* registry entry (it's an index expression only) and cannot collide
|
|
50
|
+
* with consumer-declared kinds because the regex rejects uppercase.
|
|
51
|
+
*
|
|
52
|
+
* Call once at server init. Pass the result into `create_role_schema`'s
|
|
53
|
+
* optional `scope_kinds` parameter so each role's
|
|
54
|
+
* `applicable_scope_kinds` entries are validated against this set at
|
|
55
|
+
* construction time. v1 keeps `applicable_scope_kinds` informative-only
|
|
56
|
+
* (registry-membership validation only); v2 may add INSERT-time
|
|
57
|
+
* `(role, scope_kind)` enforcement once the shape is clear from real
|
|
58
|
+
* consumer usage.
|
|
59
|
+
*
|
|
60
|
+
* @param consumer_kinds - the consumer-declared scope-kind set with optional metadata
|
|
61
|
+
* @returns `{ScopeKind, scope_kinds}` — Zod schema and metadata map
|
|
62
|
+
*
|
|
63
|
+
* @throws Error if any `consumer_kinds` key fails the `ScopeKindName` regex or appears more than once
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* // visiones
|
|
68
|
+
* const {ScopeKind, scope_kinds} = create_scope_kind_schema({
|
|
69
|
+
* classroom: {description: 'A classroom — teacher and student role_grants scope here.'},
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export const create_scope_kind_schema = (consumer_kinds) => {
|
|
74
|
+
const names = Object.keys(consumer_kinds);
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const name of names) {
|
|
77
|
+
const parsed = ScopeKindName.safeParse(name);
|
|
78
|
+
if (!parsed.success) {
|
|
79
|
+
throw new Error(`Invalid scope-kind name "${name}": ${parsed.error.issues[0].message}`);
|
|
80
|
+
}
|
|
81
|
+
if (seen.has(name)) {
|
|
82
|
+
throw new Error(`Duplicate scope-kind name "${name}"`);
|
|
83
|
+
}
|
|
84
|
+
seen.add(name);
|
|
85
|
+
}
|
|
86
|
+
const ScopeKind = names.length === 0
|
|
87
|
+
? z.never()
|
|
88
|
+
: z.enum(names);
|
|
89
|
+
const scope_kinds = new Map();
|
|
90
|
+
for (const name of names) {
|
|
91
|
+
scope_kinds.set(name, consumer_kinds[name]);
|
|
92
|
+
}
|
|
93
|
+
return { ScopeKind, scope_kinds };
|
|
94
|
+
};
|
|
@@ -15,6 +15,7 @@ export declare const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE: "role_not_self_servic
|
|
|
15
15
|
export declare const SelfServiceRoleSetInput: z.ZodObject<{
|
|
16
16
|
role: z.ZodString;
|
|
17
17
|
enabled: z.ZodBoolean;
|
|
18
|
+
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
18
19
|
}, z.core.$strict>;
|
|
19
20
|
export type SelfServiceRoleSetInput = z.infer<typeof SelfServiceRoleSetInput>;
|
|
20
21
|
/**
|
|
@@ -32,11 +33,15 @@ export declare const self_service_role_set_action_spec: {
|
|
|
32
33
|
method: string;
|
|
33
34
|
kind: "request_response";
|
|
34
35
|
initiator: "frontend";
|
|
35
|
-
auth:
|
|
36
|
+
auth: {
|
|
37
|
+
account: "required";
|
|
38
|
+
actor: "required";
|
|
39
|
+
};
|
|
36
40
|
side_effects: true;
|
|
37
41
|
input: z.ZodObject<{
|
|
38
42
|
role: z.ZodString;
|
|
39
43
|
enabled: z.ZodBoolean;
|
|
44
|
+
acting: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
|
|
40
45
|
}, z.core.$strict>;
|
|
41
46
|
output: z.ZodObject<{
|
|
42
47
|
ok: z.ZodLiteral<true>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;CAWT,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,aAAa,CAAC,yBAAyB,CAEvF,CAAC"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import { RoleName } from './role_schema.js';
|
|
12
|
+
import { ActingActor } from '../http/auth_shape.js';
|
|
12
13
|
/** Error reason — caller asked to self-toggle a role outside the configured allowlist. */
|
|
13
14
|
export const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE = 'role_not_self_service_eligible';
|
|
14
15
|
/** Input for `self_service_role_set`. */
|
|
@@ -17,6 +18,7 @@ export const SelfServiceRoleSetInput = z.strictObject({
|
|
|
17
18
|
enabled: z.boolean().meta({
|
|
18
19
|
description: 'Desired post-call state. `true` grants if not held; `false` revokes if held. Idempotent in both directions.',
|
|
19
20
|
}),
|
|
21
|
+
acting: ActingActor,
|
|
20
22
|
});
|
|
21
23
|
/**
|
|
22
24
|
* Output for `self_service_role_set`. `enabled` echoes the post-call state
|
|
@@ -32,7 +34,7 @@ export const self_service_role_set_action_spec = {
|
|
|
32
34
|
method: 'self_service_role_set',
|
|
33
35
|
kind: 'request_response',
|
|
34
36
|
initiator: 'frontend',
|
|
35
|
-
auth: '
|
|
37
|
+
auth: { account: 'required', actor: 'required' },
|
|
36
38
|
side_effects: true,
|
|
37
39
|
input: SelfServiceRoleSetInput,
|
|
38
40
|
output: SelfServiceRoleSetOutput,
|
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
* Unified self-service role toggle RPC action.
|
|
3
3
|
*
|
|
4
4
|
* One static `request_response` action — `self_service_role_set` — that
|
|
5
|
-
* takes `{role, enabled}` and toggles a global
|
|
5
|
+
* takes `{role, enabled}` and toggles a global role_grant on the caller for an
|
|
6
6
|
* allowlisted role. Idempotent in both directions: re-enabling an
|
|
7
7
|
* already-held role returns `changed: false`; disabling a role the caller
|
|
8
8
|
* doesn't hold returns `changed: false`.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Eligibility is derived by default from `RoleSpec.grant_paths` —
|
|
11
|
+
* every role whose `grant_paths` includes `'self_service'`
|
|
12
|
+
* (`GRANT_PATH_SELF_SERVICE`) is eligible. The factory accepts an
|
|
13
|
+
* optional `eligible_roles` override (validated against the supplied
|
|
14
|
+
* `roles.role_specs` at factory time so typos surface at startup
|
|
15
|
+
* instead of at first call) for deployments that want to lock the
|
|
16
|
+
* surface down further than the role spec declares. Roles outside
|
|
17
|
+
* the eligible set are rejected with `forbidden` + reason
|
|
18
|
+
* `role_not_self_service_eligible`.
|
|
14
19
|
*
|
|
15
20
|
* Audit metadata carries `self_service: true` so admin reviewers can
|
|
16
|
-
* distinguish self-toggled
|
|
17
|
-
* `
|
|
21
|
+
* distinguish self-toggled role_grants from admin grants/offers. The
|
|
22
|
+
* `role_grant_create` / `role_grant_revoke` metadata schemas declare
|
|
18
23
|
* `self_service: z.boolean().optional()` explicitly, so the field is
|
|
19
24
|
* part of the documented schema surface and is round-trip-validated by
|
|
20
25
|
* `query_audit_log`.
|
|
@@ -22,7 +27,7 @@
|
|
|
22
27
|
* Static method name — `role` lives in the input, not the method name —
|
|
23
28
|
* so the spec is codegen-compatible (`satisfies RequestResponseActionSpec`)
|
|
24
29
|
* and the surface stays constant as consumers add eligible roles. Mirrors
|
|
25
|
-
* the existing `
|
|
30
|
+
* the existing `role_grant_offer_create({role})` precedent rather than
|
|
26
31
|
* generating per-role methods.
|
|
27
32
|
*
|
|
28
33
|
* Specs and schemas live in `auth/self_service_role_action_specs.ts` so
|
|
@@ -32,37 +37,39 @@
|
|
|
32
37
|
* @module
|
|
33
38
|
*/
|
|
34
39
|
import { type RpcAction } from '../actions/action_rpc.js';
|
|
35
|
-
import type
|
|
40
|
+
import { type RoleSchemaResult } from './role_schema.js';
|
|
36
41
|
import type { RouteFactoryDeps } from './deps.js';
|
|
37
42
|
/** Options for `create_self_service_role_actions`. */
|
|
38
43
|
export interface SelfServiceRoleActionsOptions {
|
|
39
44
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* `
|
|
45
|
+
* Optional override allowlist of role strings eligible for
|
|
46
|
+
* self-service. When omitted, eligibility is derived from
|
|
47
|
+
* `roles.role_specs` (or `BUILTIN_ROLE_SPECS_BY_NAME` when `roles`
|
|
48
|
+
* is also omitted) by selecting every role whose
|
|
49
|
+
* `RoleSpec.grant_paths` includes `'self_service'`. Pass an empty
|
|
50
|
+
* array to lock the surface down (every call comes back as
|
|
51
|
+
* `forbidden` with reason `role_not_self_service_eligible`).
|
|
52
|
+
*
|
|
53
|
+
* When supplied alongside `roles`, every entry is checked against
|
|
54
|
+
* `roles.role_specs` at factory time so typos throw at startup.
|
|
43
55
|
*/
|
|
44
|
-
eligible_roles
|
|
56
|
+
eligible_roles?: ReadonlyArray<string>;
|
|
45
57
|
/**
|
|
46
|
-
* Optional role schema.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
58
|
+
* Optional role schema. Drives default eligibility derivation from
|
|
59
|
+
* `RoleSpec.grant_paths` and validates the `eligible_roles` override
|
|
60
|
+
* (when supplied) against the registered role set.
|
|
49
61
|
*/
|
|
50
62
|
roles?: RoleSchemaResult;
|
|
51
63
|
}
|
|
52
|
-
/**
|
|
53
|
-
* Dependencies for `create_self_service_role_actions`. Same shape as the
|
|
54
|
-
* peer factories so consumers thread one deps object through all three.
|
|
55
|
-
* `audit_log_config` flows from `AppDeps` and is consumed by
|
|
56
|
-
* `audit_log_fire_and_forget`.
|
|
57
|
-
*/
|
|
58
|
-
export type SelfServiceRoleActionDeps = Pick<RouteFactoryDeps, 'log' | 'on_audit_event' | 'audit_log_config'>;
|
|
59
64
|
/**
|
|
60
65
|
* Build the unified self-service role toggle RPC action.
|
|
61
66
|
*
|
|
62
|
-
* @param deps - `
|
|
63
|
-
*
|
|
67
|
+
* @param deps - `RouteFactoryDeps` (`log`, `audit`, …); `audit.emit` writes
|
|
68
|
+
* audit rows via the captured pool. The bound emitter encapsulates
|
|
69
|
+
* `on_audit_event` fan-out and the optional `AuditLogConfig`.
|
|
70
|
+
* @param options - optional eligible-role override plus optional role schema for default-eligibility derivation
|
|
64
71
|
* @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
|
|
65
|
-
* @throws Error at factory time if any `eligible_roles` entry is missing from `options.roles.
|
|
72
|
+
* @throws Error at factory time if any `eligible_roles` entry is missing from `options.roles.role_specs`
|
|
66
73
|
*/
|
|
67
|
-
export declare const create_self_service_role_actions: (deps:
|
|
74
|
+
export declare const create_self_service_role_actions: (deps: Pick<RouteFactoryDeps, "log" | "audit">, options?: SelfServiceRoleActionsOptions) => Array<RpcAction>;
|
|
68
75
|
//# sourceMappingURL=self_service_role_actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAsC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAE7F,OAAO,EAGN,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAWhD,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC7C;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,IAAI,CAAC,gBAAgB,EAAE,KAAK,GAAG,OAAO,CAAC,EAC7C,UAAS,6BAAkC,KACzC,KAAK,CAAC,SAAS,CAyHjB,CAAC"}
|