@adonis-agora/authkit-server 0.34.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/LICENSE +21 -0
- package/README.md +158 -0
- package/build/assets/grafana/authkit-dashboard.json +118 -0
- package/build/commands/clients_create.d.ts +27 -0
- package/build/commands/clients_create.js +121 -0
- package/build/commands/commands.json +292 -0
- package/build/commands/configure.d.ts +2 -0
- package/build/commands/configure.js +45 -0
- package/build/commands/doctor.d.ts +10 -0
- package/build/commands/doctor.js +113 -0
- package/build/commands/eject.d.ts +11 -0
- package/build/commands/eject.js +96 -0
- package/build/commands/expire_scan.d.ts +28 -0
- package/build/commands/expire_scan.js +92 -0
- package/build/commands/import_users.d.ts +17 -0
- package/build/commands/import_users.js +80 -0
- package/build/commands/keys_rotate.d.ts +21 -0
- package/build/commands/keys_rotate.js +126 -0
- package/build/commands/main.d.ts +12 -0
- package/build/commands/main.js +38 -0
- package/build/commands/settings_get.d.ts +18 -0
- package/build/commands/settings_get.js +43 -0
- package/build/commands/settings_list.d.ts +17 -0
- package/build/commands/settings_list.js +39 -0
- package/build/commands/settings_set.d.ts +20 -0
- package/build/commands/settings_set.js +55 -0
- package/build/commands/settings_unset.d.ts +18 -0
- package/build/commands/settings_unset.js +46 -0
- package/build/commands/ui_preset.d.ts +4 -0
- package/build/commands/ui_preset.js +32 -0
- package/build/database/migrations/make_authkit_oidc_table.d.ts +6 -0
- package/build/database/migrations/make_authkit_oidc_table.js +19 -0
- package/build/host/ui/admin.html +29 -0
- package/build/host/views/account/apps.edge +59 -0
- package/build/host/views/account/confirm.edge +70 -0
- package/build/host/views/account/email-confirmed.edge +16 -0
- package/build/host/views/account/login.edge +33 -0
- package/build/host/views/account/mfa.edge +152 -0
- package/build/host/views/account/orgs.edge +58 -0
- package/build/host/views/account/security.edge +215 -0
- package/build/host/views/account/tokens.edge +75 -0
- package/build/host/views/consent.edge +20 -0
- package/build/host/views/forgot.edge +43 -0
- package/build/host/views/login.edge +251 -0
- package/build/host/views/maintenance.edge +32 -0
- package/build/host/views/mfa-challenge.edge +107 -0
- package/build/host/views/otp-unlock.edge +40 -0
- package/build/host/views/partials/styles.edge +2 -0
- package/build/host/views/reset.edge +30 -0
- package/build/host/views/signup.edge +62 -0
- package/build/host/views/verify-email.edge +17 -0
- package/build/index.d.ts +115 -0
- package/build/index.js +76 -0
- package/build/password/common_passwords.txt +10000 -0
- package/build/providers/authkit_server_provider.d.ts +27 -0
- package/build/providers/authkit_server_provider.js +242 -0
- package/build/src/accounts/account_store.d.ts +578 -0
- package/build/src/accounts/account_store.js +60 -0
- package/build/src/accounts/lucid_account_store.d.ts +156 -0
- package/build/src/accounts/lucid_account_store.js +294 -0
- package/build/src/accounts/lucid_store/core.d.ts +9 -0
- package/build/src/accounts/lucid_store/core.js +306 -0
- package/build/src/accounts/lucid_store/mfa.d.ts +15 -0
- package/build/src/accounts/lucid_store/mfa.js +108 -0
- package/build/src/accounts/lucid_store/organizations.d.ts +13 -0
- package/build/src/accounts/lucid_store/organizations.js +294 -0
- package/build/src/accounts/lucid_store/password_hygiene.d.ts +20 -0
- package/build/src/accounts/lucid_store/password_hygiene.js +130 -0
- package/build/src/accounts/lucid_store/provider_identity.d.ts +8 -0
- package/build/src/accounts/lucid_store/provider_identity.js +59 -0
- package/build/src/accounts/lucid_store/shared.d.ts +113 -0
- package/build/src/accounts/lucid_store/shared.js +130 -0
- package/build/src/accounts/lucid_store/status_profile.d.ts +34 -0
- package/build/src/accounts/lucid_store/status_profile.js +100 -0
- package/build/src/accounts/lucid_store/webauthn.d.ts +8 -0
- package/build/src/accounts/lucid_store/webauthn.js +140 -0
- package/build/src/accounts/lucid_stores.d.ts +45 -0
- package/build/src/accounts/lucid_stores.js +30 -0
- package/build/src/adapters/adapter_contract.d.ts +37 -0
- package/build/src/adapters/adapter_contract.js +1 -0
- package/build/src/adapters/database_adapter.d.ts +21 -0
- package/build/src/adapters/database_adapter.js +79 -0
- package/build/src/adapters/factory.d.ts +30 -0
- package/build/src/adapters/factory.js +43 -0
- package/build/src/adapters/redis_adapter.d.ts +24 -0
- package/build/src/adapters/redis_adapter.js +122 -0
- package/build/src/audit/audit_sink.d.ts +63 -0
- package/build/src/audit/audit_sink.js +1 -0
- package/build/src/audit/lucid_audit_sink.d.ts +10 -0
- package/build/src/audit/lucid_audit_sink.js +99 -0
- package/build/src/commands/expire_scan_command.d.ts +60 -0
- package/build/src/commands/expire_scan_command.js +229 -0
- package/build/src/commands/import_clients.d.ts +40 -0
- package/build/src/commands/import_clients.js +79 -0
- package/build/src/commands/import_users.d.ts +51 -0
- package/build/src/commands/import_users.js +94 -0
- package/build/src/commands/resolve_config.d.ts +13 -0
- package/build/src/commands/resolve_config.js +18 -0
- package/build/src/commands/settings_commands.d.ts +47 -0
- package/build/src/commands/settings_commands.js +270 -0
- package/build/src/controllers/oidc_callback_controller.d.ts +22 -0
- package/build/src/controllers/oidc_callback_controller.js +33 -0
- package/build/src/define_config.d.ts +772 -0
- package/build/src/define_config.js +351 -0
- package/build/src/doctor/checks.d.ts +197 -0
- package/build/src/doctor/checks.js +858 -0
- package/build/src/events/dispatcher.d.ts +45 -0
- package/build/src/events/dispatcher.js +117 -0
- package/build/src/host/account_api/account_api_controller.d.ts +245 -0
- package/build/src/host/account_api/account_api_controller.js +827 -0
- package/build/src/host/account_deletion_ops.d.ts +80 -0
- package/build/src/host/account_deletion_ops.js +118 -0
- package/build/src/host/account_deletion_service.d.ts +63 -0
- package/build/src/host/account_deletion_service.js +133 -0
- package/build/src/host/account_export_service.d.ts +76 -0
- package/build/src/host/account_export_service.js +140 -0
- package/build/src/host/account_home.d.ts +11 -0
- package/build/src/host/account_home.js +11 -0
- package/build/src/host/account_lockout.d.ts +86 -0
- package/build/src/host/account_lockout.js +185 -0
- package/build/src/host/active_org_cookie.d.ts +28 -0
- package/build/src/host/active_org_cookie.js +47 -0
- package/build/src/host/admin_api/admin_api_guard.d.ts +16 -0
- package/build/src/host/admin_api/admin_api_guard.js +59 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +125 -0
- package/build/src/host/admin_api/admin_orgs_service.js +324 -0
- package/build/src/host/admin_api/admin_users_service.d.ts +163 -0
- package/build/src/host/admin_api/admin_users_service.js +308 -0
- package/build/src/host/admin_api/api_clients_controller.d.ts +54 -0
- package/build/src/host/admin_api/api_clients_controller.js +89 -0
- package/build/src/host/admin_api/api_keys_controller.d.ts +19 -0
- package/build/src/host/admin_api/api_keys_controller.js +36 -0
- package/build/src/host/admin_api/api_misc_controller.d.ts +19 -0
- package/build/src/host/admin_api/api_misc_controller.js +44 -0
- package/build/src/host/admin_api/api_orgs_controller.d.ts +108 -0
- package/build/src/host/admin_api/api_orgs_controller.js +189 -0
- package/build/src/host/admin_api/api_settings_controller.d.ts +55 -0
- package/build/src/host/admin_api/api_settings_controller.js +134 -0
- package/build/src/host/admin_api/api_users_controller.d.ts +108 -0
- package/build/src/host/admin_api/api_users_controller.js +187 -0
- package/build/src/host/admin_api/dto.d.ts +131 -0
- package/build/src/host/admin_api/dto.js +122 -0
- package/build/src/host/admin_api/token_verify_service.d.ts +34 -0
- package/build/src/host/admin_api/token_verify_service.js +74 -0
- package/build/src/host/admin_clients_service.d.ts +88 -0
- package/build/src/host/admin_clients_service.js +181 -0
- package/build/src/host/admin_console/admin_shell_controller.d.ts +27 -0
- package/build/src/host/admin_console/admin_shell_controller.js +167 -0
- package/build/src/host/admin_console/console_audit_controller.d.ts +17 -0
- package/build/src/host/admin_console/console_audit_controller.js +25 -0
- package/build/src/host/admin_console/console_clients_controller.d.ts +53 -0
- package/build/src/host/admin_console/console_clients_controller.js +102 -0
- package/build/src/host/admin_console/console_impersonation_controller.d.ts +23 -0
- package/build/src/host/admin_console/console_impersonation_controller.js +45 -0
- package/build/src/host/admin_console/console_keys_controller.d.ts +24 -0
- package/build/src/host/admin_console/console_keys_controller.js +41 -0
- package/build/src/host/admin_console/console_orgs_controller.d.ts +96 -0
- package/build/src/host/admin_console/console_orgs_controller.js +238 -0
- package/build/src/host/admin_console/console_overview_controller.d.ts +27 -0
- package/build/src/host/admin_console/console_overview_controller.js +44 -0
- package/build/src/host/admin_console/console_roles_controller.d.ts +31 -0
- package/build/src/host/admin_console/console_roles_controller.js +108 -0
- package/build/src/host/admin_console/console_sessions_controller.d.ts +91 -0
- package/build/src/host/admin_console/console_sessions_controller.js +125 -0
- package/build/src/host/admin_console/console_settings_controller.d.ts +47 -0
- package/build/src/host/admin_console/console_settings_controller.js +115 -0
- package/build/src/host/admin_console/console_users_controller.d.ts +110 -0
- package/build/src/host/admin_console/console_users_controller.js +212 -0
- package/build/src/host/admin_prefix.d.ts +50 -0
- package/build/src/host/admin_prefix.js +77 -0
- package/build/src/host/admin_sessions_service.d.ts +119 -0
- package/build/src/host/admin_sessions_service.js +284 -0
- package/build/src/host/admin_stats_service.d.ts +44 -0
- package/build/src/host/admin_stats_service.js +130 -0
- package/build/src/host/admin_validators.d.ts +247 -0
- package/build/src/host/admin_validators.js +197 -0
- package/build/src/host/augmentations.d.ts +12 -0
- package/build/src/host/augmentations.js +1 -0
- package/build/src/host/auth_host_config.d.ts +22 -0
- package/build/src/host/auth_host_config.js +13 -0
- package/build/src/host/avatar_storage.d.ts +68 -0
- package/build/src/host/avatar_storage.js +166 -0
- package/build/src/host/bot_protection.d.ts +187 -0
- package/build/src/host/bot_protection.js +132 -0
- package/build/src/host/branding.d.ts +19 -0
- package/build/src/host/branding.js +8 -0
- package/build/src/host/config_locks.d.ts +38 -0
- package/build/src/host/config_locks.js +70 -0
- package/build/src/host/console_session.d.ts +27 -0
- package/build/src/host/console_session.js +36 -0
- package/build/src/host/controllers/account_apps_controller.d.ts +15 -0
- package/build/src/host/controllers/account_apps_controller.js +61 -0
- package/build/src/host/controllers/account_confirm_controller.d.ts +24 -0
- package/build/src/host/controllers/account_confirm_controller.js +144 -0
- package/build/src/host/controllers/account_mfa_controller.d.ts +28 -0
- package/build/src/host/controllers/account_mfa_controller.js +208 -0
- package/build/src/host/controllers/account_orgs_controller.d.ts +80 -0
- package/build/src/host/controllers/account_orgs_controller.js +401 -0
- package/build/src/host/controllers/account_security_controller.d.ts +43 -0
- package/build/src/host/controllers/account_security_controller.js +546 -0
- package/build/src/host/controllers/account_session_controller.d.ts +21 -0
- package/build/src/host/controllers/account_session_controller.js +113 -0
- package/build/src/host/controllers/account_tokens_controller.d.ts +7 -0
- package/build/src/host/controllers/account_tokens_controller.js +67 -0
- package/build/src/host/controllers/interaction_controller.d.ts +125 -0
- package/build/src/host/controllers/interaction_controller.js +981 -0
- package/build/src/host/controllers/pat_introspection_controller.d.ts +22 -0
- package/build/src/host/controllers/pat_introspection_controller.js +46 -0
- package/build/src/host/controllers/registration_controller.d.ts +18 -0
- package/build/src/host/controllers/registration_controller.js +382 -0
- package/build/src/host/controllers/social_controller.d.ts +8 -0
- package/build/src/host/controllers/social_controller.js +89 -0
- package/build/src/host/csrf.d.ts +29 -0
- package/build/src/host/csrf.js +14 -0
- package/build/src/host/default_mailer.d.ts +112 -0
- package/build/src/host/default_mailer.js +417 -0
- package/build/src/host/durable/account_deletion_workflow.d.ts +48 -0
- package/build/src/host/durable/account_deletion_workflow.js +86 -0
- package/build/src/host/durable/account_export_workflow.d.ts +68 -0
- package/build/src/host/durable/account_export_workflow.js +93 -0
- package/build/src/host/durable/index.d.ts +68 -0
- package/build/src/host/durable/index.js +77 -0
- package/build/src/host/email_templates.d.ts +39 -0
- package/build/src/host/email_templates.js +69 -0
- package/build/src/host/geo.d.ts +22 -0
- package/build/src/host/geo.js +39 -0
- package/build/src/host/i18n.d.ts +1416 -0
- package/build/src/host/i18n.js +1583 -0
- package/build/src/host/impersonation.d.ts +25 -0
- package/build/src/host/impersonation.js +31 -0
- package/build/src/host/key_rotation.d.ts +14 -0
- package/build/src/host/key_rotation.js +27 -0
- package/build/src/host/key_rotation_actions.d.ts +42 -0
- package/build/src/host/key_rotation_actions.js +27 -0
- package/build/src/host/login_attempt.d.ts +98 -0
- package/build/src/host/login_attempt.js +211 -0
- package/build/src/host/login_notify.d.ts +27 -0
- package/build/src/host/login_notify.js +142 -0
- package/build/src/host/middleware/account_auth.d.ts +7 -0
- package/build/src/host/middleware/account_auth.js +11 -0
- package/build/src/host/otp_lockout.d.ts +103 -0
- package/build/src/host/otp_lockout.js +221 -0
- package/build/src/host/rate_limit.d.ts +39 -0
- package/build/src/host/rate_limit.js +94 -0
- package/build/src/host/register_auth_host.d.ts +97 -0
- package/build/src/host/register_auth_host.js +466 -0
- package/build/src/host/renderers/edge_renderer.d.ts +12 -0
- package/build/src/host/renderers/edge_renderer.js +38 -0
- package/build/src/host/renderers/inertia_renderer.d.ts +70 -0
- package/build/src/host/renderers/inertia_renderer.js +54 -0
- package/build/src/host/runtime_settings.d.ts +181 -0
- package/build/src/host/runtime_settings.js +307 -0
- package/build/src/host/runtime_toggles.d.ts +807 -0
- package/build/src/host/runtime_toggles.js +784 -0
- package/build/src/host/security_notice_service.d.ts +36 -0
- package/build/src/host/security_notice_service.js +87 -0
- package/build/src/host/session_context.d.ts +23 -0
- package/build/src/host/session_context.js +89 -0
- package/build/src/host/sudo_mode.d.ts +56 -0
- package/build/src/host/sudo_mode.js +99 -0
- package/build/src/host/svg_chart.d.ts +15 -0
- package/build/src/host/svg_chart.js +45 -0
- package/build/src/host/trusted_device.d.ts +61 -0
- package/build/src/host/trusted_device.js +65 -0
- package/build/src/host/ui-dist/assets/index-CRWcmsex.js +137 -0
- package/build/src/host/ui-dist/assets/index-DTSmD4RU.css +1 -0
- package/build/src/host/ui-dist/index.html +17 -0
- package/build/src/host/user_agent.d.ts +18 -0
- package/build/src/host/user_agent.js +50 -0
- package/build/src/host/validators.d.ts +127 -0
- package/build/src/host/validators.js +53 -0
- package/build/src/keys/jwks_manager.d.ts +6 -0
- package/build/src/keys/jwks_manager.js +11 -0
- package/build/src/keys/keystore.d.ts +52 -0
- package/build/src/keys/keystore.js +68 -0
- package/build/src/keys/keystore_codec.d.ts +20 -0
- package/build/src/keys/keystore_codec.js +42 -0
- package/build/src/keys/keystore_crypto.d.ts +9 -0
- package/build/src/keys/keystore_crypto.js +27 -0
- package/build/src/keys/keystore_manager.d.ts +43 -0
- package/build/src/keys/keystore_manager.js +104 -0
- package/build/src/keys/keystore_vault.d.ts +113 -0
- package/build/src/keys/keystore_vault.js +265 -0
- package/build/src/mixins/json_column.d.ts +33 -0
- package/build/src/mixins/json_column.js +38 -0
- package/build/src/mixins/with_audit_log.d.ts +19 -0
- package/build/src/mixins/with_audit_log.js +39 -0
- package/build/src/mixins/with_auth_user.d.ts +24 -0
- package/build/src/mixins/with_auth_user.js +46 -0
- package/build/src/mixins/with_credentials.d.ts +20 -0
- package/build/src/mixins/with_credentials.js +29 -0
- package/build/src/mixins/with_mfa.d.ts +35 -0
- package/build/src/mixins/with_mfa.js +21 -0
- package/build/src/mixins/with_personal_access_token.d.ts +19 -0
- package/build/src/mixins/with_personal_access_token.js +42 -0
- package/build/src/mixins/with_provider_identity.d.ts +20 -0
- package/build/src/mixins/with_provider_identity.js +32 -0
- package/build/src/mixins/with_webauthn_credential.d.ts +37 -0
- package/build/src/mixins/with_webauthn_credential.js +46 -0
- package/build/src/observability/diagnostics_bridge.d.ts +6 -0
- package/build/src/observability/diagnostics_bridge.js +27 -0
- package/build/src/observability/metrics_controller.d.ts +5 -0
- package/build/src/observability/metrics_controller.js +24 -0
- package/build/src/observability/metrics_service.d.ts +2 -0
- package/build/src/observability/metrics_service.js +7 -0
- package/build/src/observability/otel_recorder.d.ts +10 -0
- package/build/src/observability/otel_recorder.js +59 -0
- package/build/src/observability/telescope/data_providers.d.ts +24 -0
- package/build/src/observability/telescope/data_providers.js +235 -0
- package/build/src/observability/telescope/extension.d.ts +29 -0
- package/build/src/observability/telescope/extension.js +167 -0
- package/build/src/observability/telescope/index.d.ts +8 -0
- package/build/src/observability/telescope/index.js +8 -0
- package/build/src/observability/wire_provider_events.d.ts +12 -0
- package/build/src/observability/wire_provider_events.js +30 -0
- package/build/src/password/common_passwords.d.ts +31 -0
- package/build/src/password/common_passwords.js +73 -0
- package/build/src/password/password_manager.d.ts +163 -0
- package/build/src/password/password_manager.js +191 -0
- package/build/src/password/policy.d.ts +71 -0
- package/build/src/password/policy.js +55 -0
- package/build/src/password/pwned.d.ts +36 -0
- package/build/src/password/pwned.js +52 -0
- package/build/src/pat/lucid_pat_store.d.ts +6 -0
- package/build/src/pat/lucid_pat_store.js +62 -0
- package/build/src/pat/pat_store.d.ts +31 -0
- package/build/src/pat/pat_store.js +1 -0
- package/build/src/pat/pat_tokens.d.ts +4 -0
- package/build/src/pat/pat_tokens.js +9 -0
- package/build/src/provider/build_provider.d.ts +45 -0
- package/build/src/provider/build_provider.js +219 -0
- package/build/src/provider/device_sources.d.ts +6 -0
- package/build/src/provider/device_sources.js +65 -0
- package/build/src/provider/interaction_actions.d.ts +36 -0
- package/build/src/provider/interaction_actions.js +55 -0
- package/build/src/provider/key_rotation_scheduler.d.ts +28 -0
- package/build/src/provider/key_rotation_scheduler.js +40 -0
- package/build/src/provider/keystore_reload.d.ts +21 -0
- package/build/src/provider/keystore_reload.js +39 -0
- package/build/src/provider/logout_sources.d.ts +13 -0
- package/build/src/provider/logout_sources.js +64 -0
- package/build/src/provider/oidc_service.d.ts +81 -0
- package/build/src/provider/oidc_service.js +254 -0
- package/build/src/provider/single_flight_lock.d.ts +18 -0
- package/build/src/provider/single_flight_lock.js +31 -0
- package/build/src/provider/token_exchange.d.ts +21 -0
- package/build/src/provider/token_exchange.js +124 -0
- package/build/src/register_routes.d.ts +16 -0
- package/build/src/register_routes.js +21 -0
- package/build/src/schema/ensure.d.ts +41 -0
- package/build/src/schema/ensure.js +262 -0
- package/build/stubs/config/authkit.stub +20 -0
- package/build/stubs/config/authkit_react.stub +52 -0
- package/build/stubs/main.d.ts +1 -0
- package/build/stubs/main.js +2 -0
- package/build/stubs/models/auth_user.stub +22 -0
- package/build/stubs/ui/edge/views/consent.edge +13 -0
- package/build/stubs/ui/edge/views/login.edge +19 -0
- package/build/stubs/ui/react/components/auth_shell.tsx +67 -0
- package/build/stubs/ui/react/pages/account/login.tsx +56 -0
- package/build/stubs/ui/react/pages/account/mfa.tsx +132 -0
- package/build/stubs/ui/react/pages/account/tokens.tsx +88 -0
- package/build/stubs/ui/react/pages/consent.tsx +39 -0
- package/build/stubs/ui/react/pages/forgot.tsx +44 -0
- package/build/stubs/ui/react/pages/login.tsx +171 -0
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
- package/build/stubs/ui/react/pages/reset.tsx +58 -0
- package/build/stubs/ui/react/pages/signup.tsx +78 -0
- package/build/stubs/ui/react/pages/verify-email.tsx +24 -0
- package/build/types.d.ts +7 -0
- package/build/types.js +1 -0
- package/package.json +147 -0
- package/stubs/config/authkit.stub +20 -0
- package/stubs/config/authkit_react.stub +52 -0
- package/stubs/main.ts +2 -0
- package/stubs/models/auth_user.stub +22 -0
- package/stubs/ui/edge/views/consent.edge +13 -0
- package/stubs/ui/edge/views/login.edge +19 -0
- package/stubs/ui/react/components/auth_shell.tsx +67 -0
- package/stubs/ui/react/pages/account/login.tsx +56 -0
- package/stubs/ui/react/pages/account/mfa.tsx +132 -0
- package/stubs/ui/react/pages/account/tokens.tsx +88 -0
- package/stubs/ui/react/pages/consent.tsx +39 -0
- package/stubs/ui/react/pages/forgot.tsx +44 -0
- package/stubs/ui/react/pages/login.tsx +171 -0
- package/stubs/ui/react/pages/mfa-challenge.tsx +72 -0
- package/stubs/ui/react/pages/reset.tsx +58 -0
- package/stubs/ui/react/pages/signup.tsx +78 -0
- package/stubs/ui/react/pages/verify-email.tsx +24 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { AuditSink } from '../audit/audit_sink.js';
|
|
2
|
+
import type { ResolvedLockoutConfig } from '../define_config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bloqueio progressivo de conta (anti-brute-force keyed por EMAIL, não por IP).
|
|
5
|
+
*
|
|
6
|
+
* Construído sobre o `@adonisjs/limiter` do HOST (peer/opt-in), exatamente como o
|
|
7
|
+
* `rate_limit.ts`: o service do limiter é importado de forma preguiçosa e fail-safe.
|
|
8
|
+
* Se o `@adonisjs/limiter` não estiver instalado/configurado, TODOS os métodos viram
|
|
9
|
+
* no-op (lockout desligado) e `isLocked` devolve `{ locked: false }` — NUNCA lança no
|
|
10
|
+
* caminho da request, e NÃO há migração/DB envolvido (usa o store do limiter do host).
|
|
11
|
+
*
|
|
12
|
+
* Esquema de chaves (todas namespaced + email normalizado):
|
|
13
|
+
* - `authkit_lockout_fail:{email}` — contador de falhas dentro da janela deslizante.
|
|
14
|
+
* - `authkit_lockout:{email}` — chave bloqueada (TTL = duração progressiva do lock).
|
|
15
|
+
* - `authkit_lockout_count:{email}`— quantos locks já ocorreram (alimenta o backoff).
|
|
16
|
+
*/
|
|
17
|
+
/** Service do `@adonisjs/limiter` resolvido preguiçosamente. `any` de propósito (peer/opt-in). */
|
|
18
|
+
type LimiterService = any;
|
|
19
|
+
/**
|
|
20
|
+
* Permite reapontar/limpar o loader do limiter (usado em testes).
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
export declare function __setLockoutLimiterLoaderForTests(fn: (() => Promise<LimiterService | null>) | undefined): void;
|
|
24
|
+
/**
|
|
25
|
+
* Backoff progressivo PURO (sem I/O — fácil de testar): a duração do lock cresce
|
|
26
|
+
* com o número de locks já ocorridos para a chave: `base * 2^(lockCount-1)`,
|
|
27
|
+
* limitada a `maxLockoutSec`. `lockCount` é 1-based (1 = primeiro lock).
|
|
28
|
+
*/
|
|
29
|
+
export declare function computeLockoutSec(lockCount: number, cfg: ResolvedLockoutConfig): number;
|
|
30
|
+
/** Resultado de uma checagem de bloqueio. */
|
|
31
|
+
export interface LockState {
|
|
32
|
+
locked: boolean;
|
|
33
|
+
/** Segundos até a conta destravar (presente quando `locked`). */
|
|
34
|
+
retryAfterSec?: number;
|
|
35
|
+
}
|
|
36
|
+
/** Contexto opcional de auditoria para o evento `account.locked`. */
|
|
37
|
+
export interface LockoutAuditContext {
|
|
38
|
+
sink?: AuditSink;
|
|
39
|
+
ip?: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Helper de lockout amarrado a uma config resolvida. Cada método é fail-safe:
|
|
43
|
+
* resolve o limiter preguiçosamente e degrada para no-op quando ele está ausente
|
|
44
|
+
* ou quando `cfg.enabled === false`.
|
|
45
|
+
*/
|
|
46
|
+
export declare class AccountLockout {
|
|
47
|
+
private cfg;
|
|
48
|
+
constructor(cfg: ResolvedLockoutConfig);
|
|
49
|
+
private failKey;
|
|
50
|
+
private lockKey;
|
|
51
|
+
private countKey;
|
|
52
|
+
/**
|
|
53
|
+
* Resolve o limiter (ou `null`). Retorna `null` também quando lockout está
|
|
54
|
+
* desligado por config — assim o caminho de no-op é o mesmo.
|
|
55
|
+
*/
|
|
56
|
+
private limiter;
|
|
57
|
+
/** Store de contagem de falhas (janela deslizante de `windowSec`). */
|
|
58
|
+
private failStore;
|
|
59
|
+
/**
|
|
60
|
+
* Store da contagem de locks. A janela é longa (mantém o histórico de locks por
|
|
61
|
+
* um tempo para o backoff progressivo crescer entre locks sucessivos).
|
|
62
|
+
*/
|
|
63
|
+
private countStore;
|
|
64
|
+
/**
|
|
65
|
+
* Store dedicado a marcar a chave como bloqueada. `requests: 1` + `block(...)`
|
|
66
|
+
* com a duração progressiva; `isBlocked`/`availableIn` consultam o estado.
|
|
67
|
+
*/
|
|
68
|
+
private lockStore;
|
|
69
|
+
/** `true`/retryAfter quando a conta está bloqueada. Fail-safe: `{ locked: false }`. */
|
|
70
|
+
isLocked(email: string): Promise<LockState>;
|
|
71
|
+
/**
|
|
72
|
+
* Registra uma falha de login. Ao cruzar `maxAttempts` dentro da janela, marca
|
|
73
|
+
* a chave como bloqueada com TTL progressivo e incrementa a contagem de locks.
|
|
74
|
+
* Emite `account.locked` UMA vez (na transição), não a cada tentativa bloqueada.
|
|
75
|
+
*/
|
|
76
|
+
recordFailure(email: string, audit?: LockoutAuditContext): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Limpa o estado de falhas após um login bem-sucedido. Remove o contador de
|
|
79
|
+
* falhas e o lock; mantém a contagem de locks (histórico para o backoff) — ela
|
|
80
|
+
* expira naturalmente pela janela longa do `countStore`.
|
|
81
|
+
*/
|
|
82
|
+
clearFailures(email: string): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/** Fabrica o helper de lockout a partir da config resolvida. */
|
|
85
|
+
export declare function createAccountLockout(cfg: ResolvedLockoutConfig): AccountLockout;
|
|
86
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
2
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
3
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
4
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
5
|
+
});
|
|
6
|
+
}
|
|
7
|
+
return path;
|
|
8
|
+
};
|
|
9
|
+
let limiterServicePromise;
|
|
10
|
+
/**
|
|
11
|
+
* Importa o service do limiter do HOST de forma preguiçosa e fail-safe (mesmo
|
|
12
|
+
* padrão de `rate_limit.ts`). Resolve `null` quando o limiter não está disponível.
|
|
13
|
+
*/
|
|
14
|
+
async function loadLimiter() {
|
|
15
|
+
if (!limiterServicePromise) {
|
|
16
|
+
const specifier = '@adonisjs/limiter/services/main';
|
|
17
|
+
limiterServicePromise = import(__rewriteRelativeImportExtension(specifier))
|
|
18
|
+
.then((mod) => mod.default ?? null)
|
|
19
|
+
.catch(() => null);
|
|
20
|
+
}
|
|
21
|
+
return limiterServicePromise;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Permite reapontar/limpar o loader do limiter (usado em testes).
|
|
25
|
+
* @internal
|
|
26
|
+
*/
|
|
27
|
+
export function __setLockoutLimiterLoaderForTests(fn) {
|
|
28
|
+
if (fn) {
|
|
29
|
+
limiterServicePromise = fn();
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
limiterServicePromise = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Normaliza o email para virar chave estável (lowercase + trim). */
|
|
36
|
+
function normalizeEmail(email) {
|
|
37
|
+
return (email ?? '').trim().toLowerCase();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Backoff progressivo PURO (sem I/O — fácil de testar): a duração do lock cresce
|
|
41
|
+
* com o número de locks já ocorridos para a chave: `base * 2^(lockCount-1)`,
|
|
42
|
+
* limitada a `maxLockoutSec`. `lockCount` é 1-based (1 = primeiro lock).
|
|
43
|
+
*/
|
|
44
|
+
export function computeLockoutSec(lockCount, cfg) {
|
|
45
|
+
const n = Math.max(1, lockCount);
|
|
46
|
+
const grown = cfg.baseLockoutSec * 2 ** (n - 1);
|
|
47
|
+
return Math.min(grown, cfg.maxLockoutSec);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Helper de lockout amarrado a uma config resolvida. Cada método é fail-safe:
|
|
51
|
+
* resolve o limiter preguiçosamente e degrada para no-op quando ele está ausente
|
|
52
|
+
* ou quando `cfg.enabled === false`.
|
|
53
|
+
*/
|
|
54
|
+
export class AccountLockout {
|
|
55
|
+
cfg;
|
|
56
|
+
constructor(cfg) {
|
|
57
|
+
this.cfg = cfg;
|
|
58
|
+
}
|
|
59
|
+
failKey(email) {
|
|
60
|
+
return `authkit_lockout_fail:${email}`;
|
|
61
|
+
}
|
|
62
|
+
lockKey(email) {
|
|
63
|
+
return `authkit_lockout:${email}`;
|
|
64
|
+
}
|
|
65
|
+
countKey(email) {
|
|
66
|
+
return `authkit_lockout_count:${email}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve o limiter (ou `null`). Retorna `null` também quando lockout está
|
|
70
|
+
* desligado por config — assim o caminho de no-op é o mesmo.
|
|
71
|
+
*/
|
|
72
|
+
async limiter() {
|
|
73
|
+
if (!this.cfg.enabled)
|
|
74
|
+
return null;
|
|
75
|
+
return loadLimiter();
|
|
76
|
+
}
|
|
77
|
+
/** Store de contagem de falhas (janela deslizante de `windowSec`). */
|
|
78
|
+
failStore(limiter) {
|
|
79
|
+
const opts = { requests: this.cfg.maxAttempts, duration: this.cfg.windowSec };
|
|
80
|
+
return this.cfg.store ? limiter.use(this.cfg.store, opts) : limiter.use(opts);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Store da contagem de locks. A janela é longa (mantém o histórico de locks por
|
|
84
|
+
* um tempo para o backoff progressivo crescer entre locks sucessivos).
|
|
85
|
+
*/
|
|
86
|
+
countStore(limiter) {
|
|
87
|
+
const opts = { requests: 1_000_000, duration: this.cfg.maxLockoutSec * 4 };
|
|
88
|
+
return this.cfg.store ? limiter.use(this.cfg.store, opts) : limiter.use(opts);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Store dedicado a marcar a chave como bloqueada. `requests: 1` + `block(...)`
|
|
92
|
+
* com a duração progressiva; `isBlocked`/`availableIn` consultam o estado.
|
|
93
|
+
*/
|
|
94
|
+
lockStore(limiter) {
|
|
95
|
+
const opts = { requests: 1, duration: this.cfg.maxLockoutSec };
|
|
96
|
+
return this.cfg.store ? limiter.use(this.cfg.store, opts) : limiter.use(opts);
|
|
97
|
+
}
|
|
98
|
+
/** `true`/retryAfter quando a conta está bloqueada. Fail-safe: `{ locked: false }`. */
|
|
99
|
+
async isLocked(email) {
|
|
100
|
+
const key = normalizeEmail(email);
|
|
101
|
+
if (!key)
|
|
102
|
+
return { locked: false };
|
|
103
|
+
const limiter = await this.limiter();
|
|
104
|
+
if (!limiter)
|
|
105
|
+
return { locked: false };
|
|
106
|
+
try {
|
|
107
|
+
const store = this.lockStore(limiter);
|
|
108
|
+
const blocked = await store.isBlocked(this.lockKey(key));
|
|
109
|
+
if (!blocked)
|
|
110
|
+
return { locked: false };
|
|
111
|
+
const retryAfterSec = await store.availableIn(this.lockKey(key));
|
|
112
|
+
return { locked: true, retryAfterSec };
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Falha do limiter NUNCA derruba o login — degrada para "não bloqueado".
|
|
116
|
+
return { locked: false };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Registra uma falha de login. Ao cruzar `maxAttempts` dentro da janela, marca
|
|
121
|
+
* a chave como bloqueada com TTL progressivo e incrementa a contagem de locks.
|
|
122
|
+
* Emite `account.locked` UMA vez (na transição), não a cada tentativa bloqueada.
|
|
123
|
+
*/
|
|
124
|
+
async recordFailure(email, audit) {
|
|
125
|
+
const key = normalizeEmail(email);
|
|
126
|
+
if (!key)
|
|
127
|
+
return;
|
|
128
|
+
const limiter = await this.limiter();
|
|
129
|
+
if (!limiter)
|
|
130
|
+
return;
|
|
131
|
+
try {
|
|
132
|
+
// Se já está bloqueada, não conta de novo nem reemite o evento.
|
|
133
|
+
const lockStore = this.lockStore(limiter);
|
|
134
|
+
if (await lockStore.isBlocked(this.lockKey(key)))
|
|
135
|
+
return;
|
|
136
|
+
const failStore = this.failStore(limiter);
|
|
137
|
+
const res = await failStore.increment(this.failKey(key));
|
|
138
|
+
// `consumed` = falhas acumuladas na janela. Bloqueia ao atingir o teto.
|
|
139
|
+
const consumed = res?.consumed ?? 0;
|
|
140
|
+
if (consumed < this.cfg.maxAttempts)
|
|
141
|
+
return;
|
|
142
|
+
// Transição para bloqueado: incrementa a contagem de locks e calcula o TTL.
|
|
143
|
+
const countStore = this.countStore(limiter);
|
|
144
|
+
const countRes = await countStore.increment(this.countKey(key));
|
|
145
|
+
const lockCount = countRes?.consumed ?? 1;
|
|
146
|
+
const lockoutSec = computeLockoutSec(lockCount, this.cfg);
|
|
147
|
+
await lockStore.block(this.lockKey(key), lockoutSec);
|
|
148
|
+
// Zera o contador de falhas (a contagem recomeça após o lock).
|
|
149
|
+
await failStore.delete(this.failKey(key));
|
|
150
|
+
await audit?.sink?.record({
|
|
151
|
+
type: 'account.locked',
|
|
152
|
+
email: key,
|
|
153
|
+
ip: audit?.ip ?? null,
|
|
154
|
+
metadata: { lockCount, lockoutSec },
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// Best-effort: nunca propaga erro do limiter para o caminho da request.
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Limpa o estado de falhas após um login bem-sucedido. Remove o contador de
|
|
163
|
+
* falhas e o lock; mantém a contagem de locks (histórico para o backoff) — ela
|
|
164
|
+
* expira naturalmente pela janela longa do `countStore`.
|
|
165
|
+
*/
|
|
166
|
+
async clearFailures(email) {
|
|
167
|
+
const key = normalizeEmail(email);
|
|
168
|
+
if (!key)
|
|
169
|
+
return;
|
|
170
|
+
const limiter = await this.limiter();
|
|
171
|
+
if (!limiter)
|
|
172
|
+
return;
|
|
173
|
+
try {
|
|
174
|
+
await this.failStore(limiter).delete(this.failKey(key));
|
|
175
|
+
await this.lockStore(limiter).delete(this.lockKey(key));
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// no-op fail-safe
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/** Fabrica o helper de lockout a partir da config resolvida. */
|
|
183
|
+
export function createAccountLockout(cfg) {
|
|
184
|
+
return new AccountLockout(cfg);
|
|
185
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { ActiveOrgInfo } from '../accounts/account_store.js';
|
|
2
|
+
/** Nome do cookie da org ativa. HttpOnly, SameSite=Lax, Secure em prod. */
|
|
3
|
+
export declare const ACTIVE_ORG_COOKIE = "authkit_active_org";
|
|
4
|
+
/** TTL máximo do cookie da org ativa (30 dias em segundos). */
|
|
5
|
+
export declare const ACTIVE_ORG_COOKIE_TTL: number;
|
|
6
|
+
/**
|
|
7
|
+
* Codifica as informações da org ativa num valor de cookie (plaintext, sem
|
|
8
|
+
* assinatura — a assinatura fica a cargo do jar de cookies do AdonisJS/Keygrip
|
|
9
|
+
* via o próprio cookie signed). Formato: `orgId\torgSlug\torgRole`.
|
|
10
|
+
* TAB é escolhido pois IDs e slugs não o contêm.
|
|
11
|
+
*/
|
|
12
|
+
export declare function encodeActiveOrgCookie(info: ActiveOrgInfo): string;
|
|
13
|
+
/**
|
|
14
|
+
* Decodifica o valor cru do cookie. Retorna `null` se o formato for inválido.
|
|
15
|
+
* Não valida assinatura — assume que o caller já verificou (AdonisJS request.cookiesList).
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeActiveOrgCookie(value: string | null | undefined): ActiveOrgInfo | null;
|
|
18
|
+
/**
|
|
19
|
+
* Lê a org ativa de um contexto Koa (oidc-provider). O oidc-provider usa o Keygrip
|
|
20
|
+
* das `cookieKeys` para assinar os cookies — lemos via `ctx.cookies.get(name, { signed: false })`
|
|
21
|
+
* (o oidc-provider não assina cookies da aplicação; apenas verifica os seus). A
|
|
22
|
+
* validação de assinatura para este cookie de aplicação é feita no controller AdonisJS
|
|
23
|
+
* ao gravar (via `ctx.response.cookie` com `signed: true`). Aqui fazemos best-effort:
|
|
24
|
+
* se o valor estiver presente e parseable, usamos; caso contrário retorna null.
|
|
25
|
+
*
|
|
26
|
+
* NOTA: o oidc-provider ctx.cookies.get() nunca lança — retorna null se ausente.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readActiveOrgFromKoaCtx(koaCtx: any): ActiveOrgInfo | null;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/** Nome do cookie da org ativa. HttpOnly, SameSite=Lax, Secure em prod. */
|
|
2
|
+
export const ACTIVE_ORG_COOKIE = 'authkit_active_org';
|
|
3
|
+
/** TTL máximo do cookie da org ativa (30 dias em segundos). */
|
|
4
|
+
export const ACTIVE_ORG_COOKIE_TTL = 60 * 60 * 24 * 30;
|
|
5
|
+
/**
|
|
6
|
+
* Codifica as informações da org ativa num valor de cookie (plaintext, sem
|
|
7
|
+
* assinatura — a assinatura fica a cargo do jar de cookies do AdonisJS/Keygrip
|
|
8
|
+
* via o próprio cookie signed). Formato: `orgId\torgSlug\torgRole`.
|
|
9
|
+
* TAB é escolhido pois IDs e slugs não o contêm.
|
|
10
|
+
*/
|
|
11
|
+
export function encodeActiveOrgCookie(info) {
|
|
12
|
+
return `${info.orgId}\t${info.orgSlug}\t${info.orgRole}`;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Decodifica o valor cru do cookie. Retorna `null` se o formato for inválido.
|
|
16
|
+
* Não valida assinatura — assume que o caller já verificou (AdonisJS request.cookiesList).
|
|
17
|
+
*/
|
|
18
|
+
export function decodeActiveOrgCookie(value) {
|
|
19
|
+
if (!value)
|
|
20
|
+
return null;
|
|
21
|
+
const parts = value.split('\t');
|
|
22
|
+
if (parts.length !== 3)
|
|
23
|
+
return null;
|
|
24
|
+
const [orgId, orgSlug, orgRole] = parts;
|
|
25
|
+
if (!orgId || !orgSlug || !orgRole)
|
|
26
|
+
return null;
|
|
27
|
+
return { orgId, orgSlug, orgRole };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Lê a org ativa de um contexto Koa (oidc-provider). O oidc-provider usa o Keygrip
|
|
31
|
+
* das `cookieKeys` para assinar os cookies — lemos via `ctx.cookies.get(name, { signed: false })`
|
|
32
|
+
* (o oidc-provider não assina cookies da aplicação; apenas verifica os seus). A
|
|
33
|
+
* validação de assinatura para este cookie de aplicação é feita no controller AdonisJS
|
|
34
|
+
* ao gravar (via `ctx.response.cookie` com `signed: true`). Aqui fazemos best-effort:
|
|
35
|
+
* se o valor estiver presente e parseable, usamos; caso contrário retorna null.
|
|
36
|
+
*
|
|
37
|
+
* NOTA: o oidc-provider ctx.cookies.get() nunca lança — retorna null se ausente.
|
|
38
|
+
*/
|
|
39
|
+
export function readActiveOrgFromKoaCtx(koaCtx) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = koaCtx?.cookies?.get?.(ACTIVE_ORG_COOKIE, { signed: false });
|
|
42
|
+
return decodeActiveOrgCookie(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identificador NÃO-SENSÍVEL e estável da API key que autenticou a request, para
|
|
3
|
+
* trilha de auditoria (M9). Deriva de um SHA-256 da key e expõe só um prefixo
|
|
4
|
+
* curto do hash — NUNCA a key em si nem qualquer pedaço dela. Estável entre
|
|
5
|
+
* requests (mesma key → mesmo id), permitindo saber QUAL key agiu sem vazar o
|
|
6
|
+
* segredo. Formato: `admin-key:<8 hex>`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function adminKeyId(key: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Guard da Admin REST API (R6). Espelha o `adminGuard` do console (B6):
|
|
11
|
+
* 0. `config.adminApi.enabled` desligado → 404 (não vaza a existência da API);
|
|
12
|
+
* 1. `Authorization: Bearer <key>` ausente/inválido → 401 JSON.
|
|
13
|
+
* Sem nenhuma API key configurada, qualquer request é 401 (fail-safe). As respostas
|
|
14
|
+
* de erro seguem o envelope `{ error: { code, message } }`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const adminApiGuard: (ctx: any, next: () => Promise<void>) => Promise<any>;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createHash, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Identificador NÃO-SENSÍVEL e estável da API key que autenticou a request, para
|
|
4
|
+
* trilha de auditoria (M9). Deriva de um SHA-256 da key e expõe só um prefixo
|
|
5
|
+
* curto do hash — NUNCA a key em si nem qualquer pedaço dela. Estável entre
|
|
6
|
+
* requests (mesma key → mesmo id), permitindo saber QUAL key agiu sem vazar o
|
|
7
|
+
* segredo. Formato: `admin-key:<8 hex>`.
|
|
8
|
+
*/
|
|
9
|
+
export function adminKeyId(key) {
|
|
10
|
+
const digest = createHash('sha256').update(key).digest('hex').slice(0, 8);
|
|
11
|
+
return `admin-key:${digest}`;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Compara o Bearer recebido contra a lista de API keys em tempo constante. Cada
|
|
15
|
+
* comparação só roda quando os comprimentos batem (o `timingSafeEqual` lança em
|
|
16
|
+
* tamanhos diferentes); o curto-circuito por comprimento NÃO vaza a key (só o seu
|
|
17
|
+
* tamanho), aceitável para um segredo de alta entropia gerado pelo operador.
|
|
18
|
+
*
|
|
19
|
+
* Retorna a key que casou (para derivar o id de auditoria) ou `null`. A varredura
|
|
20
|
+
* NÃO faz short-circuit no primeiro match — mantém o tempo independente de QUAL
|
|
21
|
+
* key casou.
|
|
22
|
+
*/
|
|
23
|
+
function matchKey(header, keys) {
|
|
24
|
+
if (!header || !header.startsWith('Bearer '))
|
|
25
|
+
return null;
|
|
26
|
+
const provided = Buffer.from(header.slice(7));
|
|
27
|
+
let matched = null;
|
|
28
|
+
for (const key of keys) {
|
|
29
|
+
const expected = Buffer.from(key);
|
|
30
|
+
if (provided.length === expected.length && timingSafeEqual(provided, expected)) {
|
|
31
|
+
matched = key;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return matched;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Guard da Admin REST API (R6). Espelha o `adminGuard` do console (B6):
|
|
38
|
+
* 0. `config.adminApi.enabled` desligado → 404 (não vaza a existência da API);
|
|
39
|
+
* 1. `Authorization: Bearer <key>` ausente/inválido → 401 JSON.
|
|
40
|
+
* Sem nenhuma API key configurada, qualquer request é 401 (fail-safe). As respostas
|
|
41
|
+
* de erro seguem o envelope `{ error: { code, message } }`.
|
|
42
|
+
*/
|
|
43
|
+
export const adminApiGuard = async (ctx, next) => {
|
|
44
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
45
|
+
const cfg = service.config;
|
|
46
|
+
if (!cfg.adminApi.enabled) {
|
|
47
|
+
return ctx.response.notFound();
|
|
48
|
+
}
|
|
49
|
+
const keys = cfg.adminApi.apiKeys;
|
|
50
|
+
const matched = keys.length === 0 ? null : matchKey(ctx.request.header('authorization'), keys);
|
|
51
|
+
if (!matched) {
|
|
52
|
+
return ctx.response.unauthorized({
|
|
53
|
+
error: { code: 'unauthorized', message: 'API key ausente ou inválida.' },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
// Anexa um id NÃO-SENSÍVEL da key ao contexto para a trilha de auditoria (M9).
|
|
57
|
+
ctx.adminApiKeyId = adminKeyId(matched);
|
|
58
|
+
return next();
|
|
59
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { ResolvedServerConfig } from '../../define_config.js';
|
|
2
|
+
import type { OrgSummary, OrgMember, OrgInvitation } from '../../accounts/account_store.js';
|
|
3
|
+
import type { AdminActor } from './admin_users_service.js';
|
|
4
|
+
import type { SettingsCapability } from '../runtime_settings.js';
|
|
5
|
+
export interface OrgWithMemberCount extends OrgSummary {
|
|
6
|
+
memberCount: number;
|
|
7
|
+
}
|
|
8
|
+
export interface OrgDetail extends OrgSummary {
|
|
9
|
+
members: OrgMember[];
|
|
10
|
+
pendingInvitations: OrgInvitation[];
|
|
11
|
+
}
|
|
12
|
+
export interface CreateOrgInput {
|
|
13
|
+
name: string;
|
|
14
|
+
slug: string;
|
|
15
|
+
logoUrl?: string | null;
|
|
16
|
+
/** accountId do owner inicial da org. */
|
|
17
|
+
ownerAccountId: string;
|
|
18
|
+
}
|
|
19
|
+
export interface UpdateOrgInput {
|
|
20
|
+
name?: string;
|
|
21
|
+
logoUrl?: string | null;
|
|
22
|
+
}
|
|
23
|
+
export interface AddMemberInput {
|
|
24
|
+
accountId: string;
|
|
25
|
+
role: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CreateInvitationInput {
|
|
28
|
+
email: string;
|
|
29
|
+
role: string;
|
|
30
|
+
}
|
|
31
|
+
export type OrgNotSupportedResult = {
|
|
32
|
+
ok: false;
|
|
33
|
+
reason: 'not_supported';
|
|
34
|
+
};
|
|
35
|
+
export type OrgNotFoundResult = {
|
|
36
|
+
ok: false;
|
|
37
|
+
reason: 'not_found';
|
|
38
|
+
};
|
|
39
|
+
export type LastOwnerResult = {
|
|
40
|
+
ok: false;
|
|
41
|
+
reason: 'last_owner';
|
|
42
|
+
};
|
|
43
|
+
export type InvalidRoleResult = {
|
|
44
|
+
ok: false;
|
|
45
|
+
reason: 'invalid_role';
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Lógica de gestão de organizações compartilhada entre o console admin (HTML)
|
|
49
|
+
* e a Admin REST API (JSON). Usada pelos `AdminOrgsController` e `ApiOrgsController`.
|
|
50
|
+
* Todos os métodos de escrita auditam com o `actor` informado.
|
|
51
|
+
*/
|
|
52
|
+
export declare class AdminOrgsService {
|
|
53
|
+
private cfg;
|
|
54
|
+
constructor(cfg: ResolvedServerConfig);
|
|
55
|
+
get supported(): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Resolve o catálogo efetivo de roles de org (runtime settings → config →
|
|
58
|
+
* defaults da lib), garantindo sempre a presença de `owner`. Fail-safe: sem
|
|
59
|
+
* `settings`, usa o catálogo do config estático (`cfg.organizations`).
|
|
60
|
+
*/
|
|
61
|
+
resolveRoleCatalog(settings: SettingsCapability | null, orgId?: string | null): Promise<string[]>;
|
|
62
|
+
/** Defaults estáticos de policy de org derivados do config do host. */
|
|
63
|
+
private orgPolicyConfigDefaults;
|
|
64
|
+
/**
|
|
65
|
+
* Valida um `role` de org contra o catálogo efetivo. Retorna `true` quando o
|
|
66
|
+
* role faz parte do catálogo. H4: bloqueia roles fora do catálogo (incluindo
|
|
67
|
+
* roles arbitrárias e, quando não catalogadas, `owner`).
|
|
68
|
+
*
|
|
69
|
+
* Delega ao helper PURO `isRoleInCatalog` de `runtime_toggles` — mesmo ponto de
|
|
70
|
+
* verdade usado pelo caminho member-facing (`account_orgs_controller`).
|
|
71
|
+
*/
|
|
72
|
+
isRoleInCatalog(role: string, settings: SettingsCapability | null, orgId?: string | null): Promise<boolean>;
|
|
73
|
+
/** Lista todas as orgs com contagem de membros. */
|
|
74
|
+
listOrgs(): Promise<OrgWithMemberCount[] | OrgNotSupportedResult>;
|
|
75
|
+
/** Obtém uma org pelo id, com membros e convites pendentes. */
|
|
76
|
+
getOrg(orgId: string): Promise<OrgDetail | OrgNotSupportedResult | OrgNotFoundResult>;
|
|
77
|
+
/** Cria uma org. */
|
|
78
|
+
createOrg(input: CreateOrgInput, actor: AdminActor): Promise<OrgSummary | OrgNotSupportedResult | {
|
|
79
|
+
ok: false;
|
|
80
|
+
reason: 'slug_taken';
|
|
81
|
+
}>;
|
|
82
|
+
/** Atualiza nome/logo de uma org. */
|
|
83
|
+
updateOrg(orgId: string, patch: UpdateOrgInput, actor: AdminActor): Promise<OrgSummary | OrgNotSupportedResult | OrgNotFoundResult | {
|
|
84
|
+
ok: false;
|
|
85
|
+
reason: 'slug_taken';
|
|
86
|
+
}>;
|
|
87
|
+
/** Deleta uma org e todos os seus dados (membros + convites). */
|
|
88
|
+
deleteOrg(orgId: string, actor: AdminActor): Promise<{
|
|
89
|
+
ok: true;
|
|
90
|
+
} | OrgNotSupportedResult | OrgNotFoundResult>;
|
|
91
|
+
/** Adiciona um membro a uma org. */
|
|
92
|
+
addMember(orgId: string, input: AddMemberInput, actor: AdminActor, settings?: SettingsCapability | null): Promise<{
|
|
93
|
+
ok: true;
|
|
94
|
+
} | OrgNotSupportedResult | OrgNotFoundResult | InvalidRoleResult | {
|
|
95
|
+
ok: false;
|
|
96
|
+
reason: 'account_not_found';
|
|
97
|
+
}>;
|
|
98
|
+
/** Remove um membro de uma org. Respeita invariante last_owner. */
|
|
99
|
+
removeMember(orgId: string, accountId: string, actor: AdminActor): Promise<{
|
|
100
|
+
ok: true;
|
|
101
|
+
} | OrgNotSupportedResult | OrgNotFoundResult | LastOwnerResult | {
|
|
102
|
+
ok: false;
|
|
103
|
+
reason: 'member_not_found';
|
|
104
|
+
}>;
|
|
105
|
+
/** Troca o papel de um membro. Respeita invariante last_owner. */
|
|
106
|
+
updateMemberRole(orgId: string, accountId: string, newRole: string, actor: AdminActor, settings?: SettingsCapability | null): Promise<{
|
|
107
|
+
ok: true;
|
|
108
|
+
} | OrgNotSupportedResult | OrgNotFoundResult | LastOwnerResult | InvalidRoleResult | {
|
|
109
|
+
ok: false;
|
|
110
|
+
reason: 'member_not_found';
|
|
111
|
+
}>;
|
|
112
|
+
/** Cria um convite por e-mail. Dispara o mail hook quando configurado. */
|
|
113
|
+
createInvitation(orgId: string, input: CreateInvitationInput, actor: AdminActor, origin: string, settings?: SettingsCapability | null): Promise<{
|
|
114
|
+
ok: true;
|
|
115
|
+
invitation: OrgInvitation;
|
|
116
|
+
token: string;
|
|
117
|
+
} | OrgNotSupportedResult | OrgNotFoundResult | InvalidRoleResult>;
|
|
118
|
+
/** Revoga um convite. */
|
|
119
|
+
revokeInvitation(orgId: string, invitationId: string, actor: AdminActor): Promise<{
|
|
120
|
+
ok: true;
|
|
121
|
+
} | OrgNotSupportedResult | OrgNotFoundResult | {
|
|
122
|
+
ok: false;
|
|
123
|
+
reason: 'invitation_not_found';
|
|
124
|
+
}>;
|
|
125
|
+
}
|