@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,308 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { supportsAccountDeletion, supportsAccountStatus, supportsProfile, supportsCountByGlobalRole, } from "../../accounts/account_store.js";
|
|
3
|
+
import { sendPasswordResetEmail } from "../default_mailer.js";
|
|
4
|
+
import { AccountDeletionService, } from "../account_deletion_service.js";
|
|
5
|
+
import { PasswordPolicyError } from "../../password/password_manager.js";
|
|
6
|
+
import { resolveEffectiveRolesCatalog } from "../runtime_toggles.js";
|
|
7
|
+
/**
|
|
8
|
+
* Lógica de gestão de usuários compartilhada entre o console admin (B6, HTML) e a
|
|
9
|
+
* Admin REST API (R6, JSON). Encapsula o fluxo "create + invite", reset de senha,
|
|
10
|
+
* troca de status e atualização de perfil/roles — todos auditando com o `actor`
|
|
11
|
+
* informado (`admin-api` nas chamadas REST).
|
|
12
|
+
*/
|
|
13
|
+
export class AdminUsersService {
|
|
14
|
+
cfg;
|
|
15
|
+
constructor(cfg) {
|
|
16
|
+
this.cfg = cfg;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Cria uma conta. Com `password`: já nasce com a senha. Sem `password` (ou
|
|
20
|
+
* `invite: true`): cria com senha aleatória forte e dispara o e-mail de reset
|
|
21
|
+
* (o usuário define a própria). Audita `user.created`.
|
|
22
|
+
*/
|
|
23
|
+
async create(ctx, input, actor) {
|
|
24
|
+
const store = this.cfg.accountStore;
|
|
25
|
+
const existing = await store.findByEmail(input.email);
|
|
26
|
+
if (existing)
|
|
27
|
+
return { ok: false, reason: "email_taken" };
|
|
28
|
+
const hasPassword = !!input.password;
|
|
29
|
+
const initialPassword = input.password ?? randomBytes(24).toString("hex");
|
|
30
|
+
let account;
|
|
31
|
+
try {
|
|
32
|
+
account = await store.create({
|
|
33
|
+
email: input.email,
|
|
34
|
+
password: initialPassword,
|
|
35
|
+
fullName: input.name ?? null,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
// Política de senha aplicada também na criação por admin (quando há senha).
|
|
40
|
+
if (error instanceof PasswordPolicyError) {
|
|
41
|
+
return {
|
|
42
|
+
ok: false,
|
|
43
|
+
reason: "password_policy",
|
|
44
|
+
messageKey: error.key,
|
|
45
|
+
messageParams: error.params,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
await this.cfg.audit?.record({
|
|
51
|
+
type: "user.created",
|
|
52
|
+
accountId: account.id,
|
|
53
|
+
email: input.email,
|
|
54
|
+
actorId: actor.actorId,
|
|
55
|
+
ip: actor.ip,
|
|
56
|
+
metadata: {
|
|
57
|
+
invited: !hasPassword,
|
|
58
|
+
...(actor.source ? { actor: actor.source } : {}),
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
if (!hasPassword) {
|
|
62
|
+
await this.sendResetEmail(ctx, account.email);
|
|
63
|
+
}
|
|
64
|
+
return { ok: true, account, invited: !hasPassword };
|
|
65
|
+
}
|
|
66
|
+
/** Emite token de reset + envia e-mail. Retorna a conta (ou null se inexistente). */
|
|
67
|
+
async resetPassword(ctx, accountId, actor) {
|
|
68
|
+
const account = await this.cfg.accountStore.findById(accountId);
|
|
69
|
+
if (!account)
|
|
70
|
+
return null;
|
|
71
|
+
await this.sendResetEmail(ctx, account.email);
|
|
72
|
+
await this.cfg.audit?.record({
|
|
73
|
+
type: "user.password_reset_sent",
|
|
74
|
+
accountId,
|
|
75
|
+
email: account.email,
|
|
76
|
+
actorId: actor.actorId,
|
|
77
|
+
ip: actor.ip,
|
|
78
|
+
...(actor.source ? { metadata: { actor: actor.source } } : {}),
|
|
79
|
+
});
|
|
80
|
+
return account;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Habilita/desabilita uma conta. Retorna false quando o store não suporta a
|
|
84
|
+
* capacidade (o caller responde 409). Audita `user.disabled`/`user.enabled`.
|
|
85
|
+
*/
|
|
86
|
+
async setStatus(accountId, disable, actor) {
|
|
87
|
+
const store = this.cfg.accountStore;
|
|
88
|
+
if (!supportsAccountStatus(store))
|
|
89
|
+
return false;
|
|
90
|
+
if (disable)
|
|
91
|
+
await store.disableAccount(accountId);
|
|
92
|
+
else
|
|
93
|
+
await store.enableAccount(accountId);
|
|
94
|
+
await this.cfg.audit?.record({
|
|
95
|
+
type: disable ? "user.disabled" : "user.enabled",
|
|
96
|
+
accountId,
|
|
97
|
+
actorId: actor.actorId,
|
|
98
|
+
ip: actor.ip,
|
|
99
|
+
...(actor.source ? { metadata: { actor: actor.source } } : {}),
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Substitui as roles globais de uma conta validando contra o catálogo runtime.
|
|
105
|
+
*
|
|
106
|
+
* Regras:
|
|
107
|
+
* - Roles presentes no catálogo são aceitas.
|
|
108
|
+
* - Roles que o usuário JÁ TEM mas que não estão no catálogo ("fora do catálogo")
|
|
109
|
+
* podem ser REMOVIDAS (não incluir no array roles = remoção normal).
|
|
110
|
+
* - Roles desconhecidas que não estão no catálogo E que o usuário não tinha
|
|
111
|
+
* são REJEITADAS (retorna erro i18n).
|
|
112
|
+
* - Quando settings não disponível, aceita tudo (fail-safe).
|
|
113
|
+
*
|
|
114
|
+
* @returns `null` quando OK; string i18n key quando há role inválida nova.
|
|
115
|
+
*/
|
|
116
|
+
async setGlobalRolesValidated(accountId, roles, settings) {
|
|
117
|
+
if (settings) {
|
|
118
|
+
const catalog = await resolveEffectiveRolesCatalog(settings);
|
|
119
|
+
const catalogNames = new Set(catalog.roles.map((r) => r.name));
|
|
120
|
+
// Carrega as roles atuais do usuário para saber quais são "fora do catálogo".
|
|
121
|
+
const account = await this.cfg.accountStore.findById(accountId);
|
|
122
|
+
const currentRoles = new Set(account?.globalRoles ?? []);
|
|
123
|
+
// Roles que o usuário não tinha E que não estão no catálogo = inválidas.
|
|
124
|
+
for (const role of roles) {
|
|
125
|
+
if (!catalogNames.has(role) && !currentRoles.has(role)) {
|
|
126
|
+
return "admin.roles.unknown_role";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
await this.cfg.accountStore.setGlobalRoles(accountId, roles);
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/** Substitui as roles globais de uma conta (normaliza nada — recebe array pronto). */
|
|
134
|
+
async setGlobalRoles(accountId, roles) {
|
|
135
|
+
await this.cfg.accountStore.setGlobalRoles(accountId, roles);
|
|
136
|
+
}
|
|
137
|
+
/** Roles globais que conferem acesso de admin (default ['ADMIN']). */
|
|
138
|
+
adminRoles() {
|
|
139
|
+
const roles = this.cfg.admin?.roles;
|
|
140
|
+
return roles && roles.length > 0 ? roles : ["ADMIN"];
|
|
141
|
+
}
|
|
142
|
+
/** Um conjunto de roles contém ao menos uma role de admin? */
|
|
143
|
+
hasAdminRole(roles) {
|
|
144
|
+
const adminRoles = new Set(this.adminRoles());
|
|
145
|
+
for (const r of roles) {
|
|
146
|
+
if (adminRoles.has(r))
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Conta quantas contas possuem ao menos uma role de admin. Usado para a
|
|
153
|
+
* invariante de "último admin" — chamado apenas em mudanças de role.
|
|
154
|
+
*
|
|
155
|
+
* CAMINHO EFICIENTE: quando o store expõe `countByGlobalRole` E há exatamente
|
|
156
|
+
* UMA role de admin, conta direto no DB sem paginar a base. Com MÚLTIPLAS roles
|
|
157
|
+
* de admin NÃO usamos a capability: somar `countByGlobalRole` por role
|
|
158
|
+
* sobrecontaria contas que possuem 2+ roles de admin, e overcount aqui é uma
|
|
159
|
+
* regressão de SEGURANÇA (relaxaria o guard de last-admin). Nesse caso caímos
|
|
160
|
+
* no scan paginado, que deduplica naturalmente (conta cada conta uma vez).
|
|
161
|
+
*
|
|
162
|
+
* FALLBACK (sem capability ou múltiplas roles): pagina via `listAccounts`
|
|
163
|
+
* (store-agnóstico, não exige query SQL específica de dialeto sobre a coluna
|
|
164
|
+
* JSON `globalRoles`) e conta in-memory deduplicado.
|
|
165
|
+
*/
|
|
166
|
+
async countAdmins() {
|
|
167
|
+
const store = this.cfg.accountStore;
|
|
168
|
+
const adminRoles = this.adminRoles();
|
|
169
|
+
// Caminho eficiente: capability presente + exatamente uma role de admin
|
|
170
|
+
// (evita double-count que sobrecontaria com múltiplas roles).
|
|
171
|
+
if (adminRoles.length === 1 && supportsCountByGlobalRole(store)) {
|
|
172
|
+
return store.countByGlobalRole(adminRoles[0]);
|
|
173
|
+
}
|
|
174
|
+
return this.countAdminsByScan();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Fallback de contagem por paginação de `listAccounts` (deduplicado: cada
|
|
178
|
+
* conta é contada no máximo uma vez via {@link hasAdminRole}).
|
|
179
|
+
*
|
|
180
|
+
* Terminadores honestos (sem número mágico): a paginação para quando
|
|
181
|
+
* - a página veio incompleta (`data.length < pageSize`) → última página, OU
|
|
182
|
+
* - a página veio vazia (`data.length === 0`) → sem mais dados, OU
|
|
183
|
+
* - já cobrimos o total reportado (`page * pageSize >= total`).
|
|
184
|
+
* Uma página COMPLETA com `total` ainda maior continua paginando. Isto encerra
|
|
185
|
+
* de forma garantida para qualquer store que devolva páginas finitas (a última
|
|
186
|
+
* página é, por definição, menor que `pageSize` ou vazia).
|
|
187
|
+
*/
|
|
188
|
+
async countAdminsByScan() {
|
|
189
|
+
const store = this.cfg.accountStore;
|
|
190
|
+
const pageSize = 100;
|
|
191
|
+
let page = 1;
|
|
192
|
+
let count = 0;
|
|
193
|
+
while (true) {
|
|
194
|
+
const { data, total } = await store.listAccounts({
|
|
195
|
+
page,
|
|
196
|
+
limit: pageSize,
|
|
197
|
+
});
|
|
198
|
+
for (const acc of data) {
|
|
199
|
+
if (this.hasAdminRole(acc.globalRoles ?? []))
|
|
200
|
+
count++;
|
|
201
|
+
}
|
|
202
|
+
if (data.length < pageSize || page * pageSize >= total)
|
|
203
|
+
break;
|
|
204
|
+
page++;
|
|
205
|
+
}
|
|
206
|
+
return count;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Aplica proteções de segurança a uma troca de roles globais ANTES de gravar:
|
|
210
|
+
*
|
|
211
|
+
* - `last_admin`: bloqueia remover a role de admin da ÚLTIMA conta que a possui
|
|
212
|
+
* (evita lockout permanente do console). Só dispara quando o target ATUALMENTE
|
|
213
|
+
* é admin, o novo conjunto NÃO é admin, e ele é o único admin.
|
|
214
|
+
* - `cannot_self_demote`: bloqueia o ator remover a própria role de admin
|
|
215
|
+
* (`actorId === targetId` e o novo conjunto não tem admin). Só aplica quando
|
|
216
|
+
* há um `actorId` identificável.
|
|
217
|
+
*
|
|
218
|
+
* @returns código i18n/erro quando a operação deve ser bloqueada; `null` quando OK.
|
|
219
|
+
*/
|
|
220
|
+
async guardGlobalRolesChange(targetId, newRoles, actorId) {
|
|
221
|
+
const target = await this.cfg.accountStore.findById(targetId);
|
|
222
|
+
const currentlyAdmin = this.hasAdminRole(target?.globalRoles ?? []);
|
|
223
|
+
const willBeAdmin = this.hasAdminRole(newRoles);
|
|
224
|
+
// Só há risco quando a operação REMOVE o status de admin de quem o tinha.
|
|
225
|
+
if (!currentlyAdmin || willBeAdmin)
|
|
226
|
+
return null;
|
|
227
|
+
// Auto-rebaixamento: o ator removendo a própria role de admin.
|
|
228
|
+
if (actorId && actorId === targetId)
|
|
229
|
+
return "cannot_self_demote";
|
|
230
|
+
// Último admin: se o target é o único admin, a remoção causaria lockout.
|
|
231
|
+
const admins = await this.countAdmins();
|
|
232
|
+
if (admins <= 1)
|
|
233
|
+
return "last_admin";
|
|
234
|
+
return null;
|
|
235
|
+
}
|
|
236
|
+
/** Atualiza nome/avatar (capacidade opcional). Retorna a conta ou null. */
|
|
237
|
+
async updateProfile(accountId, patch) {
|
|
238
|
+
const store = this.cfg.accountStore;
|
|
239
|
+
if (!supportsProfile(store))
|
|
240
|
+
return null;
|
|
241
|
+
return store.updateProfile(accountId, patch);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Deleta uma conta pelo admin, usando o MESMO cascade do self-service
|
|
245
|
+
* ({@link AccountDeletionService}). Precisa do {@link OidcService} (para revogar
|
|
246
|
+
* sessões/grants). Retorna `{ ok: false, reason }` se a conta não existe ou o
|
|
247
|
+
* store não suporta hard delete (o caller responde 404/409). Audita
|
|
248
|
+
* `account.deleted` (ator admin) dentro do cascade + `user.deleted` aqui (trilha
|
|
249
|
+
* administrativa).
|
|
250
|
+
*/
|
|
251
|
+
async delete(oidc, accountId, actor, enqueue) {
|
|
252
|
+
const store = this.cfg.accountStore;
|
|
253
|
+
if (!supportsAccountDeletion(store))
|
|
254
|
+
return { ok: false, reason: "unsupported" };
|
|
255
|
+
const account = await store.findById(accountId);
|
|
256
|
+
if (!account)
|
|
257
|
+
return { ok: false, reason: "not_found" };
|
|
258
|
+
const source = actor.source === "admin" ? "admin" : "admin-api";
|
|
259
|
+
// Trilha administrativa ANTES do cascade — assim esta linha também é
|
|
260
|
+
// anonimizada na etapa de anonimização do audit (não reintroduz PII).
|
|
261
|
+
await this.cfg.audit?.record({
|
|
262
|
+
type: "user.deleted",
|
|
263
|
+
accountId,
|
|
264
|
+
email: account.email,
|
|
265
|
+
actorId: actor.actorId,
|
|
266
|
+
ip: actor.ip,
|
|
267
|
+
metadata: { actor: source },
|
|
268
|
+
});
|
|
269
|
+
// OPT-IN durável (admin/admin-api): enfileira o cascade COMPLETO (fully async).
|
|
270
|
+
if (this.cfg.accountLifecycle?.durable && enqueue) {
|
|
271
|
+
const runId = await enqueue({
|
|
272
|
+
accountId,
|
|
273
|
+
actor: { actorId: actor.actorId, ip: actor.ip, source },
|
|
274
|
+
});
|
|
275
|
+
return { ok: true, enqueued: true, runId };
|
|
276
|
+
}
|
|
277
|
+
// Caminho SÍNCRONO de sempre (byte-idêntico): cascade in-process.
|
|
278
|
+
const result = await new AccountDeletionService(oidc).delete(accountId, {
|
|
279
|
+
actorId: actor.actorId,
|
|
280
|
+
ip: actor.ip,
|
|
281
|
+
source,
|
|
282
|
+
});
|
|
283
|
+
return { ok: true, result };
|
|
284
|
+
}
|
|
285
|
+
/** Indica se a conta está desabilitada (false quando a capacidade não existe). */
|
|
286
|
+
async isDisabled(accountId) {
|
|
287
|
+
const store = this.cfg.accountStore;
|
|
288
|
+
return supportsAccountStatus(store) ? store.isDisabled(accountId) : false;
|
|
289
|
+
}
|
|
290
|
+
/** Emite o token de reset e dispara o e-mail (hook do config tem prioridade). */
|
|
291
|
+
async sendResetEmail(ctx, email) {
|
|
292
|
+
const issued = await this.cfg.accountStore.issuePasswordResetToken(email);
|
|
293
|
+
if (!issued)
|
|
294
|
+
return;
|
|
295
|
+
const origin = `${ctx.request.protocol()}://${ctx.request.host()}`;
|
|
296
|
+
const resetUrl = `${origin}/auth/reset-password?token=${encodeURIComponent(issued.token)}`;
|
|
297
|
+
if (this.cfg.mail?.onPasswordReset) {
|
|
298
|
+
await this.cfg.mail.onPasswordReset({
|
|
299
|
+
email,
|
|
300
|
+
resetUrl,
|
|
301
|
+
token: issued.token,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
await sendPasswordResetEmail(ctx, { email, resetUrl });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Recurso de clients OIDC da Admin REST API (R6). Reaproveita o
|
|
5
|
+
* {@link AdminClientsService} (mesmo que o console B6). O secret é retornado UMA vez
|
|
6
|
+
* em create/regenerate. Audita create/update/delete.
|
|
7
|
+
*/
|
|
8
|
+
export default class ApiClientsController {
|
|
9
|
+
index(ctx: HttpContext): Promise<{
|
|
10
|
+
data: {
|
|
11
|
+
clientId: string;
|
|
12
|
+
confidential: boolean;
|
|
13
|
+
grants: string[];
|
|
14
|
+
redirectUris: string[];
|
|
15
|
+
postLogoutRedirectUris: string[];
|
|
16
|
+
tokenEndpointAuthMethod: string;
|
|
17
|
+
backchannelLogoutUri: string | null;
|
|
18
|
+
backchannelLogoutSessionRequired: boolean;
|
|
19
|
+
}[];
|
|
20
|
+
canList: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
show(ctx: HttpContext): Promise<void | {
|
|
23
|
+
clientId: string;
|
|
24
|
+
confidential: boolean;
|
|
25
|
+
grants: string[];
|
|
26
|
+
redirectUris: string[];
|
|
27
|
+
postLogoutRedirectUris: string[];
|
|
28
|
+
tokenEndpointAuthMethod: string;
|
|
29
|
+
backchannelLogoutUri: string | null;
|
|
30
|
+
backchannelLogoutSessionRequired: boolean;
|
|
31
|
+
}>;
|
|
32
|
+
store(ctx: HttpContext): Promise<{
|
|
33
|
+
clientId: string;
|
|
34
|
+
clientSecret: string | null;
|
|
35
|
+
}>;
|
|
36
|
+
update(ctx: HttpContext): Promise<void | {
|
|
37
|
+
clientId: string;
|
|
38
|
+
confidential: boolean;
|
|
39
|
+
grants: string[];
|
|
40
|
+
redirectUris: string[];
|
|
41
|
+
postLogoutRedirectUris: string[];
|
|
42
|
+
tokenEndpointAuthMethod: string;
|
|
43
|
+
backchannelLogoutUri: string | null;
|
|
44
|
+
backchannelLogoutSessionRequired: boolean;
|
|
45
|
+
}>;
|
|
46
|
+
regenerateSecret(ctx: HttpContext): Promise<void | {
|
|
47
|
+
clientId: any;
|
|
48
|
+
clientSecret: string;
|
|
49
|
+
}>;
|
|
50
|
+
destroy(ctx: HttpContext): Promise<{
|
|
51
|
+
clientId: any;
|
|
52
|
+
deleted: boolean;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import { AdminClientsService } from '../admin_clients_service.js';
|
|
3
|
+
import { clientDto, createdClientDto, apiError } from './dto.js';
|
|
4
|
+
import { clientInputValidator, clientCreateInput, clientPartialInput } from '../admin_validators.js';
|
|
5
|
+
/** Resolve o serviço (== OidcService) + o AdminClientsService. */
|
|
6
|
+
async function clientsService(ctx) {
|
|
7
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
8
|
+
return { service, svc: new AdminClientsService(service) };
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Recurso de clients OIDC da Admin REST API (R6). Reaproveita o
|
|
12
|
+
* {@link AdminClientsService} (mesmo que o console B6). O secret é retornado UMA vez
|
|
13
|
+
* em create/regenerate. Audita create/update/delete.
|
|
14
|
+
*/
|
|
15
|
+
export default class ApiClientsController {
|
|
16
|
+
async index(ctx) {
|
|
17
|
+
const { svc } = await clientsService(ctx);
|
|
18
|
+
if (!svc.canList) {
|
|
19
|
+
return { data: [], canList: false };
|
|
20
|
+
}
|
|
21
|
+
const clients = await svc.list();
|
|
22
|
+
return { data: clients.map(clientDto), canList: true };
|
|
23
|
+
}
|
|
24
|
+
async show(ctx) {
|
|
25
|
+
const { svc } = await clientsService(ctx);
|
|
26
|
+
const client = await svc.find(ctx.request.param('id'));
|
|
27
|
+
if (!client)
|
|
28
|
+
return ctx.response.notFound(apiError('not_found', 'Client não encontrado.'));
|
|
29
|
+
return clientDto(client);
|
|
30
|
+
}
|
|
31
|
+
async store(ctx) {
|
|
32
|
+
const { service, svc } = await clientsService(ctx);
|
|
33
|
+
const input = clientCreateInput(await ctx.request.validateUsing(clientInputValidator));
|
|
34
|
+
const created = await svc.create(input);
|
|
35
|
+
await service.config.audit?.record({
|
|
36
|
+
type: 'client.created',
|
|
37
|
+
clientId: created.clientId,
|
|
38
|
+
ip: ctx.request.ip?.() ?? null,
|
|
39
|
+
metadata: { actor: 'admin-api' },
|
|
40
|
+
});
|
|
41
|
+
ctx.response.status(201);
|
|
42
|
+
return createdClientDto(created);
|
|
43
|
+
}
|
|
44
|
+
async update(ctx) {
|
|
45
|
+
const { service, svc } = await clientsService(ctx);
|
|
46
|
+
const id = ctx.request.param('id');
|
|
47
|
+
const existing = await svc.find(id);
|
|
48
|
+
if (!existing)
|
|
49
|
+
return ctx.response.notFound(apiError('not_found', 'Client não encontrado.'));
|
|
50
|
+
await svc.update(id, clientPartialInput(await ctx.request.validateUsing(clientInputValidator)));
|
|
51
|
+
await service.config.audit?.record({
|
|
52
|
+
type: 'client.updated',
|
|
53
|
+
clientId: id,
|
|
54
|
+
ip: ctx.request.ip?.() ?? null,
|
|
55
|
+
metadata: { actor: 'admin-api' },
|
|
56
|
+
});
|
|
57
|
+
const updated = await svc.find(id);
|
|
58
|
+
return clientDto(updated);
|
|
59
|
+
}
|
|
60
|
+
async regenerateSecret(ctx) {
|
|
61
|
+
const { service, svc } = await clientsService(ctx);
|
|
62
|
+
const id = ctx.request.param('id');
|
|
63
|
+
try {
|
|
64
|
+
const secret = await svc.regenerateSecret(id);
|
|
65
|
+
await service.config.audit?.record({
|
|
66
|
+
type: 'client.updated',
|
|
67
|
+
clientId: id,
|
|
68
|
+
ip: ctx.request.ip?.() ?? null,
|
|
69
|
+
metadata: { actor: 'admin-api', action: 'regenerate_secret' },
|
|
70
|
+
});
|
|
71
|
+
return { clientId: id, clientSecret: secret };
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
return ctx.response.conflict(apiError('cannot_regenerate', e.message));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async destroy(ctx) {
|
|
78
|
+
const { service, svc } = await clientsService(ctx);
|
|
79
|
+
const id = ctx.request.param('id');
|
|
80
|
+
await svc.delete(id);
|
|
81
|
+
await service.config.audit?.record({
|
|
82
|
+
type: 'client.deleted',
|
|
83
|
+
clientId: id,
|
|
84
|
+
ip: ctx.request.ip?.() ?? null,
|
|
85
|
+
metadata: { actor: 'admin-api' },
|
|
86
|
+
});
|
|
87
|
+
return { clientId: id, deleted: true };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Gestão da chave de assinatura managed via Admin REST API (Bearer).
|
|
5
|
+
* Sob `/api/authkit/v1/keys`. Retorna 501 (`not_implemented`) quando o jwks não é
|
|
6
|
+
* managed+store (sem keystore gerenciável). A rotação aplica ao vivo e audita
|
|
7
|
+
* (`keys.rotated`) dentro de `svc.rotateKeys`.
|
|
8
|
+
*/
|
|
9
|
+
export default class ApiKeysController {
|
|
10
|
+
/** GET /keys — status da chave de assinatura managed (idade + política + ETA). */
|
|
11
|
+
status(ctx: HttpContext): Promise<void | import("../key_rotation_actions.js").KeysStatus>;
|
|
12
|
+
/** POST /keys/rotate — { retire?, keep? } → rotaciona agora. */
|
|
13
|
+
rotate(ctx: HttpContext): Promise<void | {
|
|
14
|
+
rotated: true;
|
|
15
|
+
newKid: string;
|
|
16
|
+
retiredKids: string[];
|
|
17
|
+
keptKids: string[];
|
|
18
|
+
}>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import { buildKeysStatus, rotateNow } from '../key_rotation_actions.js';
|
|
3
|
+
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
4
|
+
import { apiError } from './dto.js';
|
|
5
|
+
/**
|
|
6
|
+
* Gestão da chave de assinatura managed via Admin REST API (Bearer).
|
|
7
|
+
* Sob `/api/authkit/v1/keys`. Retorna 501 (`not_implemented`) quando o jwks não é
|
|
8
|
+
* managed+store (sem keystore gerenciável). A rotação aplica ao vivo e audita
|
|
9
|
+
* (`keys.rotated`) dentro de `svc.rotateKeys`.
|
|
10
|
+
*/
|
|
11
|
+
export default class ApiKeysController {
|
|
12
|
+
/** GET /keys — status da chave de assinatura managed (idade + política + ETA). */
|
|
13
|
+
async status(ctx) {
|
|
14
|
+
const svc = await ctx.containerResolver.make('authkit.server');
|
|
15
|
+
// RuntimeSettings é opcional (tabela auth_settings ausente → política default).
|
|
16
|
+
const settings = await resolveRuntimeSettings(ctx);
|
|
17
|
+
const status = await buildKeysStatus(svc, settings);
|
|
18
|
+
if (!status) {
|
|
19
|
+
return ctx.response
|
|
20
|
+
.status(501)
|
|
21
|
+
.send(apiError('not_implemented', 'jwks não é managed+store.'));
|
|
22
|
+
}
|
|
23
|
+
return status;
|
|
24
|
+
}
|
|
25
|
+
/** POST /keys/rotate — { retire?, keep? } → rotaciona agora. */
|
|
26
|
+
async rotate(ctx) {
|
|
27
|
+
const svc = await ctx.containerResolver.make('authkit.server');
|
|
28
|
+
if (typeof svc.rotateKeys !== 'function' ||
|
|
29
|
+
(await svc.keystoreAgeDays?.().catch(() => null)) === null) {
|
|
30
|
+
return ctx.response
|
|
31
|
+
.status(501)
|
|
32
|
+
.send(apiError('not_implemented', 'rotação indisponível (jwks não é managed+store).'));
|
|
33
|
+
}
|
|
34
|
+
return rotateNow(svc, ctx.request.body());
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* Endpoints utilitários da Admin REST API: log de auditoria (`GET /audit`) e
|
|
5
|
+
* introspecção genérica de token (`POST /tokens/verify`).
|
|
6
|
+
*/
|
|
7
|
+
export default class ApiMiscController {
|
|
8
|
+
/** GET /audit — listagem paginada (501 JSON quando o sink não consulta). */
|
|
9
|
+
audit(ctx: HttpContext): Promise<void | {
|
|
10
|
+
data: any;
|
|
11
|
+
total: any;
|
|
12
|
+
page: number;
|
|
13
|
+
limit: number;
|
|
14
|
+
}>;
|
|
15
|
+
/** GET /stats — métricas-resumo do IdP (totais + MAU + séries de 30 dias). */
|
|
16
|
+
stats(ctx: HttpContext): Promise<import("../admin_stats_service.js").AdminStats>;
|
|
17
|
+
/** POST /tokens/verify — { token } → resultado de introspecção (PAT ou opaque AT). */
|
|
18
|
+
verify(ctx: HttpContext): Promise<import("./token_verify_service.js").VerifyResult>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import { TokenVerifyService } from './token_verify_service.js';
|
|
3
|
+
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
4
|
+
import { computeAdminStats } from '../admin_stats_service.js';
|
|
5
|
+
import { auditDto, apiError } from './dto.js';
|
|
6
|
+
import { tokenVerifyValidator } from '../admin_validators.js';
|
|
7
|
+
/**
|
|
8
|
+
* Endpoints utilitários da Admin REST API: log de auditoria (`GET /audit`) e
|
|
9
|
+
* introspecção genérica de token (`POST /tokens/verify`).
|
|
10
|
+
*/
|
|
11
|
+
export default class ApiMiscController {
|
|
12
|
+
/** GET /audit — listagem paginada (501 JSON quando o sink não consulta). */
|
|
13
|
+
async audit(ctx) {
|
|
14
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
15
|
+
const cfg = service.config;
|
|
16
|
+
const sink = cfg.audit;
|
|
17
|
+
if (!sink || typeof sink.list !== 'function') {
|
|
18
|
+
return ctx.response
|
|
19
|
+
.status(501)
|
|
20
|
+
.send(apiError('not_implemented', 'O sink de auditoria configurado não suporta consulta.'));
|
|
21
|
+
}
|
|
22
|
+
const page = Math.max(1, Number.parseInt(ctx.request.input('page', '1'), 10) || 1);
|
|
23
|
+
const limit = Math.max(1, Math.min(100, Number.parseInt(ctx.request.input('limit', '20'), 10) || 20));
|
|
24
|
+
const type = ctx.request.input('type')?.trim() || undefined;
|
|
25
|
+
const subject = ctx.request.input('subject')?.trim() || undefined;
|
|
26
|
+
const result = await sink.list({ page, limit, type, subject });
|
|
27
|
+
return { data: result.data.map(auditDto), total: result.total, page, limit };
|
|
28
|
+
}
|
|
29
|
+
/** GET /stats — métricas-resumo do IdP (totais + MAU + séries de 30 dias). */
|
|
30
|
+
async stats(ctx) {
|
|
31
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
32
|
+
const cfg = service.config;
|
|
33
|
+
const sessions = new AdminSessionsService(service);
|
|
34
|
+
return computeAdminStats(cfg, sessions);
|
|
35
|
+
}
|
|
36
|
+
/** POST /tokens/verify — { token } → resultado de introspecção (PAT ou opaque AT). */
|
|
37
|
+
async verify(ctx) {
|
|
38
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
39
|
+
const cfg = service.config;
|
|
40
|
+
const { token } = await ctx.request.validateUsing(tokenVerifyValidator);
|
|
41
|
+
const verifier = new TokenVerifyService(cfg, service.provider);
|
|
42
|
+
return verifier.verify(token);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
|
+
/**
|
|
4
|
+
* CRUD de organizações da Admin REST API.
|
|
5
|
+
* Todas as rotas ficam sob `/api/authkit/v1/organizations`.
|
|
6
|
+
*/
|
|
7
|
+
export default class ApiOrgsController {
|
|
8
|
+
/** GET /organizations — lista todas as orgs com contagem de membros. */
|
|
9
|
+
index(ctx: HttpContext): Promise<void | {
|
|
10
|
+
data: {
|
|
11
|
+
memberCount?: number | undefined;
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
slug: string;
|
|
15
|
+
logoUrl: string | null;
|
|
16
|
+
metadata: Record<string, unknown> | null;
|
|
17
|
+
createdAt: string;
|
|
18
|
+
}[];
|
|
19
|
+
}>;
|
|
20
|
+
/** POST /organizations — cria uma org. Body: { name, slug, ownerAccountId, logoUrl? } */
|
|
21
|
+
store(ctx: HttpContext): Promise<void | {
|
|
22
|
+
memberCount?: number | undefined;
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
slug: string;
|
|
26
|
+
logoUrl: string | null;
|
|
27
|
+
metadata: Record<string, unknown> | null;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
}>;
|
|
30
|
+
/** GET /organizations/:id — obtém uma org com membros e convites. */
|
|
31
|
+
show(ctx: HttpContext): Promise<void | {
|
|
32
|
+
members: {
|
|
33
|
+
accountId: string;
|
|
34
|
+
email: string | null;
|
|
35
|
+
role: string;
|
|
36
|
+
joinedAt: string;
|
|
37
|
+
}[];
|
|
38
|
+
pendingInvitations: {
|
|
39
|
+
id: string;
|
|
40
|
+
organizationId: string;
|
|
41
|
+
email: string;
|
|
42
|
+
role: string;
|
|
43
|
+
invitedBy: string;
|
|
44
|
+
expiresAt: string;
|
|
45
|
+
acceptedAt: string | null;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
}[];
|
|
48
|
+
memberCount?: number | undefined;
|
|
49
|
+
id: string;
|
|
50
|
+
name: string;
|
|
51
|
+
slug: string;
|
|
52
|
+
logoUrl: string | null;
|
|
53
|
+
metadata: Record<string, unknown> | null;
|
|
54
|
+
createdAt: string;
|
|
55
|
+
}>;
|
|
56
|
+
/** PATCH /organizations/:id — atualiza nome e/ou logo. */
|
|
57
|
+
update(ctx: HttpContext): Promise<void | {
|
|
58
|
+
memberCount?: number | undefined;
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
slug: string;
|
|
62
|
+
logoUrl: string | null;
|
|
63
|
+
metadata: Record<string, unknown> | null;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
}>;
|
|
66
|
+
/** DELETE /organizations/:id */
|
|
67
|
+
destroy(ctx: HttpContext): Promise<void | {
|
|
68
|
+
id: any;
|
|
69
|
+
deleted: boolean;
|
|
70
|
+
}>;
|
|
71
|
+
/** POST /organizations/:id/members — adiciona membro. Body: { accountId, role } */
|
|
72
|
+
addMember(ctx: HttpContext): Promise<void | {
|
|
73
|
+
orgId: any;
|
|
74
|
+
accountId: string;
|
|
75
|
+
role: string | undefined;
|
|
76
|
+
added: boolean;
|
|
77
|
+
}>;
|
|
78
|
+
/** DELETE /organizations/:id/members/:accountId */
|
|
79
|
+
removeMember(ctx: HttpContext): Promise<void | {
|
|
80
|
+
orgId: any;
|
|
81
|
+
accountId: any;
|
|
82
|
+
removed: boolean;
|
|
83
|
+
}>;
|
|
84
|
+
/** PATCH /organizations/:id/members/:accountId — troca o papel. Body: { role } */
|
|
85
|
+
updateMemberRole(ctx: HttpContext): Promise<void | {
|
|
86
|
+
orgId: any;
|
|
87
|
+
accountId: any;
|
|
88
|
+
role: string;
|
|
89
|
+
updated: boolean;
|
|
90
|
+
}>;
|
|
91
|
+
/** POST /organizations/:id/invitations — cria convite. Body: { email, role } */
|
|
92
|
+
createInvitation(ctx: HttpContext): Promise<void | {
|
|
93
|
+
id: string;
|
|
94
|
+
organizationId: string;
|
|
95
|
+
email: string;
|
|
96
|
+
role: string;
|
|
97
|
+
invitedBy: string;
|
|
98
|
+
expiresAt: string;
|
|
99
|
+
acceptedAt: string | null;
|
|
100
|
+
createdAt: string;
|
|
101
|
+
}>;
|
|
102
|
+
/** DELETE /organizations/:id/invitations/:invitationId */
|
|
103
|
+
revokeInvitation(ctx: HttpContext): Promise<void | {
|
|
104
|
+
orgId: any;
|
|
105
|
+
invitationId: any;
|
|
106
|
+
revoked: boolean;
|
|
107
|
+
}>;
|
|
108
|
+
}
|