@adonis-agora/authkit-server 0.47.0 → 0.48.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/build/commands/clients_create.js +4 -4
- package/build/commands/configure.js +3 -2
- package/build/commands/doctor.js +8 -5
- package/build/commands/eject.js +1 -1
- package/build/commands/expire_scan.js +7 -5
- package/build/commands/import_users.js +1 -1
- package/build/commands/keys_rotate.js +7 -4
- package/build/host/views/account/apps.edge +5 -5
- package/build/host/views/account/login.edge +1 -1
- package/build/host/views/account/mfa.edge +7 -7
- package/build/host/views/account/orgs.edge +5 -5
- package/build/host/views/account/security.edge +10 -10
- package/build/host/views/account/tokens.edge +5 -5
- package/build/index.d.ts +7 -7
- package/build/index.js +5 -5
- package/build/providers/authkit_server_provider.d.ts +2 -2
- package/build/providers/authkit_server_provider.js +20 -11
- package/build/services/main.d.ts +1 -1
- package/build/services/main.js +2 -2
- package/build/src/accounts/lucid_account_store.d.ts +3 -3
- package/build/src/accounts/lucid_account_store.js +6 -8
- package/build/src/accounts/lucid_store/core.js +1 -1
- package/build/src/accounts/lucid_store/mfa.js +1 -1
- package/build/src/accounts/lucid_store/organizations.js +2 -4
- package/build/src/accounts/lucid_store/password_hygiene.js +1 -4
- package/build/src/accounts/lucid_store/shared.d.ts +1 -1
- package/build/src/accounts/lucid_stores.d.ts +3 -3
- package/build/src/accounts/lucid_stores.js +2 -2
- package/build/src/adapters/database_adapter.js +3 -1
- package/build/src/adapters/factory.js +1 -1
- package/build/src/adapters/redis_adapter.js +2 -1
- package/build/src/commands/expire_scan_command.js +19 -5
- package/build/src/commands/settings_commands.js +22 -5
- package/build/src/define_config.d.ts +20 -20
- package/build/src/define_config.js +62 -67
- package/build/src/doctor/checks.js +45 -20
- package/build/src/events/dispatcher.d.ts +1 -1
- package/build/src/events/dispatcher.js +10 -10
- package/build/src/host/account_api/account_api_controller.js +95 -39
- package/build/src/host/account_deletion_ops.d.ts +4 -4
- package/build/src/host/account_deletion_ops.js +5 -5
- package/build/src/host/account_deletion_service.d.ts +2 -2
- package/build/src/host/account_deletion_service.js +2 -2
- package/build/src/host/account_export_service.d.ts +1 -1
- package/build/src/host/account_export_service.js +4 -6
- package/build/src/host/account_home.d.ts +0 -8
- package/build/src/host/account_home.js +6 -4
- package/build/src/host/account_login_url.d.ts +6 -3
- package/build/src/host/account_login_url.js +16 -8
- package/build/src/host/account_paths.d.ts +120 -0
- package/build/src/host/account_paths.js +161 -0
- package/build/src/host/admin_api/admin_orgs_service.d.ts +2 -2
- package/build/src/host/admin_api/admin_orgs_service.js +3 -2
- package/build/src/host/admin_api/admin_users_service.d.ts +12 -12
- package/build/src/host/admin_api/admin_users_service.js +20 -20
- package/build/src/host/admin_api/api_clients_controller.js +2 -2
- package/build/src/host/admin_api/api_misc_controller.js +2 -2
- package/build/src/host/admin_api/api_orgs_controller.js +3 -3
- package/build/src/host/admin_api/api_settings_controller.js +11 -5
- package/build/src/host/admin_api/api_users_controller.d.ts +2 -2
- package/build/src/host/admin_api/api_users_controller.js +37 -38
- package/build/src/host/admin_api/dto.d.ts +4 -4
- package/build/src/host/admin_clients_service.js +5 -4
- package/build/src/host/admin_console/admin_shell_controller.js +4 -2
- package/build/src/host/admin_console/console_audit_controller.js +1 -1
- package/build/src/host/admin_console/console_clients_controller.js +5 -3
- package/build/src/host/admin_console/console_impersonation_controller.js +1 -1
- package/build/src/host/admin_console/console_keys_controller.js +2 -4
- package/build/src/host/admin_console/console_orgs_controller.js +3 -3
- package/build/src/host/admin_console/console_roles_controller.js +30 -12
- package/build/src/host/admin_console/console_sessions_controller.js +3 -3
- package/build/src/host/admin_console/console_settings_controller.js +11 -5
- package/build/src/host/admin_console/console_users_controller.d.ts +2 -2
- package/build/src/host/admin_console/console_users_controller.js +46 -47
- package/build/src/host/admin_prefix.js +2 -2
- package/build/src/host/admin_sessions_service.js +1 -3
- package/build/src/host/adonis_auth_sync.d.ts +1 -1
- package/build/src/host/adonis_auth_user_provider.d.ts +1 -1
- package/build/src/host/app_key.js +1 -1
- package/build/src/host/avatar_storage.js +1 -3
- package/build/src/host/config_locks.js +1 -3
- package/build/src/host/console_session.js +1 -1
- package/build/src/host/controllers/account_apps_controller.js +3 -2
- package/build/src/host/controllers/account_confirm_controller.js +2 -6
- package/build/src/host/controllers/account_mfa_controller.js +63 -12
- package/build/src/host/controllers/account_orgs_controller.js +47 -27
- package/build/src/host/controllers/account_security_controller.d.ts +2 -2
- package/build/src/host/controllers/account_security_controller.js +114 -115
- package/build/src/host/controllers/account_session_controller.js +5 -5
- package/build/src/host/controllers/account_tokens_controller.js +3 -2
- package/build/src/host/controllers/interaction_controller.js +59 -22
- package/build/src/host/controllers/registration_controller.js +28 -13
- package/build/src/host/default_mailer.js +11 -7
- package/build/src/host/durable/account_deletion_workflow.d.ts +2 -2
- package/build/src/host/durable/account_deletion_workflow.js +14 -14
- package/build/src/host/durable/account_export_workflow.d.ts +2 -2
- package/build/src/host/durable/account_export_workflow.js +12 -12
- package/build/src/host/durable/index.d.ts +5 -5
- package/build/src/host/durable/index.js +6 -6
- package/build/src/host/i18n.d.ts +1376 -1376
- package/build/src/host/i18n.js +1380 -1380
- package/build/src/host/impersonation.js +1 -7
- package/build/src/host/key_rotation_actions.d.ts +1 -1
- package/build/src/host/login_attempt.js +35 -6
- package/build/src/host/login_notify.js +1 -1
- package/build/src/host/otp_lockout.js +1 -3
- package/build/src/host/rate_limit.js +1 -1
- package/build/src/host/register_auth_host.d.ts +30 -0
- package/build/src/host/register_auth_host.js +120 -74
- package/build/src/host/renderers/edge_renderer.js +15 -2
- package/build/src/host/renderers/inertia_renderer.d.ts +9 -1
- package/build/src/host/renderers/inertia_renderer.js +9 -1
- package/build/src/host/runtime_settings.js +1 -1
- package/build/src/host/runtime_toggles.d.ts +3 -3
- package/build/src/host/runtime_toggles.js +69 -24
- package/build/src/host/security_notice_service.d.ts +1 -1
- package/build/src/host/security_notice_service.js +1 -1
- package/build/src/host/session_context.js +1 -1
- package/build/src/host/sudo/index.d.ts +3 -3
- package/build/src/host/sudo/index.js +3 -3
- package/build/src/host/sudo/methods/magic_link.js +6 -5
- package/build/src/host/sudo/methods/passkey.js +7 -4
- package/build/src/host/sudo/methods/password.js +8 -3
- package/build/src/host/sudo/runtime.js +6 -13
- package/build/src/host/sudo_mode.js +3 -2
- package/build/src/host/ui-dist/assets/index-BQoTDiLV.js +137 -0
- package/build/src/host/ui-dist/index.html +1 -1
- package/build/src/keys/jwks_manager.js +1 -1
- package/build/src/keys/keystore.js +1 -1
- package/build/src/keys/keystore_manager.d.ts +2 -2
- package/build/src/keys/keystore_manager.js +2 -2
- package/build/src/keys/keystore_vault.js +8 -4
- package/build/src/mixins/with_audit_log.d.ts +1 -1
- package/build/src/mixins/with_auth_user.d.ts +1 -1
- package/build/src/mixins/with_auth_user.js +1 -1
- package/build/src/mixins/with_credentials.d.ts +1 -1
- package/build/src/mixins/with_mfa.d.ts +1 -1
- package/build/src/mixins/with_personal_access_token.d.ts +1 -1
- package/build/src/mixins/with_provider_identity.d.ts +1 -1
- package/build/src/mixins/with_webauthn_credential.d.ts +1 -1
- package/build/src/observability/diagnostics_bridge.js +2 -2
- package/build/src/observability/metrics_service.js +1 -1
- package/build/src/observability/telescope/data_providers.d.ts +1 -1
- package/build/src/observability/telescope/data_providers.js +29 -43
- package/build/src/observability/telescope/extension.d.ts +1 -1
- package/build/src/observability/telescope/extension.js +58 -58
- package/build/src/observability/telescope/index.d.ts +2 -2
- package/build/src/observability/telescope/index.js +2 -2
- package/build/src/observability/wire_provider_events.d.ts +1 -1
- package/build/src/observability/wire_provider_events.js +11 -11
- package/build/src/password/common_passwords.js +1 -1
- package/build/src/password/password_manager.d.ts +1 -1
- package/build/src/password/password_manager.js +4 -4
- package/build/src/pat/lucid_pat_store.js +2 -6
- package/build/src/pat/pat_tokens.js +1 -1
- package/build/src/provider/interaction_actions.js +1 -1
- package/build/src/provider/key_rotation_scheduler.js +3 -1
- package/build/src/provider/logout_sources.js +2 -8
- package/build/src/provider/oidc_service.d.ts +2 -2
- package/build/src/provider/oidc_service.js +22 -14
- package/build/src/provider/single_flight_lock.js +1 -3
- package/build/src/provider/token_exchange.d.ts +1 -1
- package/build/src/schema/ensure.js +3 -1
- package/build/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/build/stubs/ui/react/pages/account/login.tsx +6 -7
- package/build/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/build/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/build/stubs/ui/react/pages/consent.tsx +12 -12
- package/build/stubs/ui/react/pages/forgot.tsx +7 -7
- package/build/stubs/ui/react/pages/login.tsx +22 -24
- package/build/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/build/stubs/ui/react/pages/reset.tsx +10 -10
- package/build/stubs/ui/react/pages/signup.tsx +30 -16
- package/build/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/package.json +1 -1
- package/stubs/main.ts +2 -2
- package/stubs/ui/react/components/auth_shell.tsx +19 -19
- package/stubs/ui/react/pages/account/login.tsx +6 -7
- package/stubs/ui/react/pages/account/mfa.tsx +16 -15
- package/stubs/ui/react/pages/account/tokens.tsx +27 -17
- package/stubs/ui/react/pages/consent.tsx +12 -12
- package/stubs/ui/react/pages/forgot.tsx +7 -7
- package/stubs/ui/react/pages/login.tsx +22 -24
- package/stubs/ui/react/pages/mfa-challenge.tsx +9 -10
- package/stubs/ui/react/pages/reset.tsx +10 -10
- package/stubs/ui/react/pages/signup.tsx +30 -16
- package/stubs/ui/react/pages/verify-email.tsx +6 -6
- package/build/src/host/ui-dist/assets/index-BmI_J87g.js +0 -137
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefixo e segmentos de tela do console de conta (`/account/*`) — singleton de
|
|
3
|
+
* processo, configurável/localizável.
|
|
4
|
+
*
|
|
5
|
+
* Definidos em tempo de registro das rotas (`registerAuthHost`, via a opção
|
|
6
|
+
* `accountRoutes`) e lidos em runtime por TODOS os pontos que constroem uma URL
|
|
7
|
+
* do console de conta: o registro de rotas, os redirects dos controllers, as
|
|
8
|
+
* URLs absolutas dos e-mails transacionais, os guards de sudo e as views Edge
|
|
9
|
+
* (injetados como prop global `accountPaths` pelo renderer).
|
|
10
|
+
*
|
|
11
|
+
* Por que módulo singleton e não binding do container? Mesma razão do
|
|
12
|
+
* `admin_prefix` e do `account_login_url`: os guards e o registro de rotas são
|
|
13
|
+
* closures construídas em tempo de registro, ANTES da primeira request, quando
|
|
14
|
+
* o container do AdonisJS ainda não existe. Um módulo ESM é inicializado uma vez
|
|
15
|
+
* por processo — ideal para configuração imutável de boot.
|
|
16
|
+
*
|
|
17
|
+
* ── O que é configurável e o que NÃO é ──────────────────────────────────────
|
|
18
|
+
* Configuráveis: o PREFIXO (`/account` → `/conta`) e o SEGMENTO de cada TELA
|
|
19
|
+
* navegável (`security` → `seguranca`, `confirm` → `confirmar`, ...). São paths
|
|
20
|
+
* que o usuário vê na barra de endereço e que um host pode querer no idioma dele.
|
|
21
|
+
*
|
|
22
|
+
* NÃO configuráveis (de propósito): os action-subpaths dos POSTs de dentro de
|
|
23
|
+
* cada tela — `/password`, `/email`, `/enroll`, `/confirm`, `/disable`,
|
|
24
|
+
* `/passkeys/options`, `/passkeys/verify`, `/delete`, `/export`, o `/magic-link`
|
|
25
|
+
* e o `/passkey` das rotas de sudo, etc. São endpoints de formulário invisíveis
|
|
26
|
+
* ao usuário (ele nunca digita `/account/security/password`), servem apenas de
|
|
27
|
+
* alvo de `<form action>`/`fetch`, e mantê-los fixos evita multiplicar a
|
|
28
|
+
* superfície de configuração (e a de bugs) sem nenhum ganho de UX. Renomear a
|
|
29
|
+
* tela já cobre o caso real "quero /conta/seguranca". Idem para o segmento fixo
|
|
30
|
+
* `api` da JSON API (`{prefix}/api/*`): ele segue o prefixo mas nunca é
|
|
31
|
+
* traduzido — é contrato de máquina (authkit-react), não de humano.
|
|
32
|
+
*/
|
|
33
|
+
/** Prefixo default do console de conta. */
|
|
34
|
+
const DEFAULT_ACCOUNT_PREFIX = '/account';
|
|
35
|
+
/**
|
|
36
|
+
* Segmentos default de cada TELA navegável do console de conta. A chave é o
|
|
37
|
+
* identificador estável usado no código (`accountPath('security')`); o valor é
|
|
38
|
+
* o segmento de URL, sobrescrevível por `setAccountPaths`.
|
|
39
|
+
*/
|
|
40
|
+
const DEFAULT_ACCOUNT_SEGMENTS = {
|
|
41
|
+
login: 'login',
|
|
42
|
+
logout: 'logout',
|
|
43
|
+
security: 'security',
|
|
44
|
+
mfa: 'mfa',
|
|
45
|
+
confirm: 'confirm',
|
|
46
|
+
tokens: 'tokens',
|
|
47
|
+
apps: 'apps',
|
|
48
|
+
orgs: 'orgs',
|
|
49
|
+
emailConfirm: 'email/confirm',
|
|
50
|
+
};
|
|
51
|
+
let _prefix = DEFAULT_ACCOUNT_PREFIX;
|
|
52
|
+
let _segments = { ...DEFAULT_ACCOUNT_SEGMENTS };
|
|
53
|
+
/**
|
|
54
|
+
* Normaliza o prefixo: começa com '/', sem trailing slash. Mesma semântica de
|
|
55
|
+
* `normalizeAdminPrefix`. `'/'` → `'/'` (raiz) é improvável mas seguro.
|
|
56
|
+
*/
|
|
57
|
+
export function normalizeAccountPrefix(raw) {
|
|
58
|
+
let p = raw.trim();
|
|
59
|
+
if (!p.startsWith('/'))
|
|
60
|
+
p = `/${p}`;
|
|
61
|
+
p = p.replace(/\/+$/, '');
|
|
62
|
+
return p || '/';
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Normaliza um segmento de tela: sem barras nas pontas (mas preserva barras
|
|
66
|
+
* internas, ex.: `'email/confirm'`). Valor vazio é ignorado pelo caller.
|
|
67
|
+
*/
|
|
68
|
+
function normalizeSegment(raw) {
|
|
69
|
+
return raw.trim().replace(/^\/+/, '').replace(/\/+$/, '');
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Define o prefixo e/ou os segmentos de tela do console de conta para este
|
|
73
|
+
* processo. Chamado UMA VEZ por `registerAuthHost` no boot da aplicação.
|
|
74
|
+
*
|
|
75
|
+
* Só sobrescreve o que foi passado — chamada sem `prefix` mantém o prefixo
|
|
76
|
+
* atual, e segmentos ausentes de `paths` mantêm o default. Valores
|
|
77
|
+
* vazios/whitespace são ignorados (caem no default), nunca produzem um path
|
|
78
|
+
* quebrado.
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* setAccountPaths({ prefix: '/conta', paths: { security: 'seguranca', confirm: 'confirmar' } })
|
|
82
|
+
* accountPath('security') // → '/conta/seguranca'
|
|
83
|
+
* accountPath('confirm') // → '/conta/confirmar'
|
|
84
|
+
*/
|
|
85
|
+
export function setAccountPaths(opts) {
|
|
86
|
+
if (!opts)
|
|
87
|
+
return;
|
|
88
|
+
if (opts.prefix !== undefined) {
|
|
89
|
+
const trimmed = opts.prefix.trim();
|
|
90
|
+
if (trimmed)
|
|
91
|
+
_prefix = normalizeAccountPrefix(trimmed);
|
|
92
|
+
}
|
|
93
|
+
if (opts.paths) {
|
|
94
|
+
for (const key of Object.keys(opts.paths)) {
|
|
95
|
+
const value = opts.paths[key];
|
|
96
|
+
if (value === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
const seg = normalizeSegment(value);
|
|
99
|
+
if (seg)
|
|
100
|
+
_segments[key] = seg;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Junta o prefixo do console de conta com um subpath arbitrário, tratando o
|
|
106
|
+
* prefixo raiz (`'/'`) como `''` na composição: sem isso o resultado seria
|
|
107
|
+
* `'//<sub>'`, que num `<form action>`/`fetch()` é URL protocol-relative (o
|
|
108
|
+
* browser lê `<sub>` como HOST, não como path).
|
|
109
|
+
*
|
|
110
|
+
* Base canônica de `accountPath()` e de QUALQUER outra composição
|
|
111
|
+
* prefixo+subpath fora das TELAS fixas (ex.: o `{prefix}/api` da JSON API em
|
|
112
|
+
* `register_auth_host`). Novas composições devem passar por aqui em vez de
|
|
113
|
+
* reimplementar `${accountPrefix()}/algo` — é exatamente essa duplicação que
|
|
114
|
+
* reproduz o bug do `//` quando o prefixo normaliza pra raiz.
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* joinAccountPath('api') // default → '/account/api'
|
|
118
|
+
*/
|
|
119
|
+
export function joinAccountPath(sub) {
|
|
120
|
+
const base = _prefix === '/' ? '' : _prefix;
|
|
121
|
+
return `${base}/${sub}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Retorna o path completo de uma TELA do console de conta (prefixo + segmento).
|
|
125
|
+
* Para os action-subpaths (POSTs de dentro da tela) concatene o subpath fixo:
|
|
126
|
+
* `accountPath('security') + '/password'`.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* accountPath('security') // default → '/account/security'
|
|
130
|
+
* accountPath('confirm') // default → '/account/confirm'
|
|
131
|
+
*/
|
|
132
|
+
export function accountPath(key) {
|
|
133
|
+
return joinAccountPath(_segments[key]);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Retorna o prefixo BRUTO do console de conta (default `'/account'`; pode ser
|
|
137
|
+
* `'/'` na raiz). Para compor `{prefix}/algo` (ex.: a JSON API, cujo segmento
|
|
138
|
+
* `api` é fixo — contrato de máquina), use `joinAccountPath('algo')`: ela
|
|
139
|
+
* colapsa a raiz e evita o `//`. Este getter existe para os casos (ex.: os
|
|
140
|
+
* asserts de teste) que precisam do valor cru do prefixo.
|
|
141
|
+
*/
|
|
142
|
+
export function accountPrefix() {
|
|
143
|
+
return _prefix;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Mapa `{ chave → path completo }` de TODAS as telas. Injetado como prop global
|
|
147
|
+
* `accountPaths` nas views Edge, para que os `<form action>`/`fetch()` respeitem
|
|
148
|
+
* os overrides sem cada view conhecer o prefixo.
|
|
149
|
+
*/
|
|
150
|
+
export function accountPathsMap() {
|
|
151
|
+
const out = {};
|
|
152
|
+
for (const key of Object.keys(_segments)) {
|
|
153
|
+
out[key] = accountPath(key);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
/** Restaura os defaults — uso em testes (isola o singleton entre casos). */
|
|
158
|
+
export function resetAccountPaths() {
|
|
159
|
+
_prefix = DEFAULT_ACCOUNT_PREFIX;
|
|
160
|
+
_segments = { ...DEFAULT_ACCOUNT_SEGMENTS };
|
|
161
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { OrgInvitation, OrgMember, OrgSummary } from '../../accounts/account_store.js';
|
|
1
2
|
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
3
|
import type { SettingsCapability } from '../runtime_settings.js';
|
|
4
|
+
import type { AdminActor } from './admin_users_service.js';
|
|
5
5
|
export interface OrgWithMemberCount extends OrgSummary {
|
|
6
6
|
memberCount: number;
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { supportsOrganizations } from '../../accounts/account_store.js';
|
|
2
|
-
import {
|
|
2
|
+
import { accountPath } from '../account_paths.js';
|
|
3
|
+
import { isRoleInCatalog, resolveRoleCatalogList } from '../runtime_toggles.js';
|
|
3
4
|
/**
|
|
4
5
|
* Lógica de gestão de organizações compartilhada entre o console admin (HTML)
|
|
5
6
|
* e a Admin REST API (JSON). Usada pelos `AdminOrgsController` e `ApiOrgsController`.
|
|
@@ -267,7 +268,7 @@ export class AdminOrgsService {
|
|
|
267
268
|
});
|
|
268
269
|
// Dispara mail hook (best-effort)
|
|
269
270
|
if (this.cfg.mail?.onOrgInvitation) {
|
|
270
|
-
const acceptUrl = `${origin}
|
|
271
|
+
const acceptUrl = `${origin}${accountPath('orgs')}/invitations/${token}/accept`;
|
|
271
272
|
try {
|
|
272
273
|
await this.cfg.mail.onOrgInvitation({
|
|
273
274
|
email: input.email,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type { HttpContext } from
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import { type DeletionResult } from
|
|
6
|
-
import type { SettingsCapability } from
|
|
1
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
2
|
+
import type { AuthAccount } from '../../accounts/account_store.js';
|
|
3
|
+
import type { ResolvedServerConfig } from '../../define_config.js';
|
|
4
|
+
import type { OidcService } from '../../provider/oidc_service.js';
|
|
5
|
+
import { type DeletionResult } from '../account_deletion_service.js';
|
|
6
|
+
import type { SettingsCapability } from '../runtime_settings.js';
|
|
7
7
|
/** Quem disparou a operação (para auditoria). `admin-api` quando via REST API. */
|
|
8
8
|
export interface AdminActor {
|
|
9
9
|
actorId: string | null;
|
|
10
10
|
ip: string | null;
|
|
11
11
|
/** Marca metadata da auditoria — 'admin-api' nas escritas via REST, 'admin' no console HTML. */
|
|
12
|
-
source?:
|
|
12
|
+
source?: 'admin-api' | 'admin';
|
|
13
13
|
}
|
|
14
14
|
/** Resultado da deleção via admin: false quando o store não suporta hard delete. */
|
|
15
15
|
export type DeleteUserResult = {
|
|
16
16
|
ok: false;
|
|
17
|
-
reason:
|
|
17
|
+
reason: 'not_found' | 'unsupported';
|
|
18
18
|
} | {
|
|
19
19
|
ok: true;
|
|
20
20
|
result: DeletionResult;
|
|
@@ -34,7 +34,7 @@ export type EnqueueDeletion = (input: {
|
|
|
34
34
|
actor: {
|
|
35
35
|
actorId: string | null;
|
|
36
36
|
ip: string | null;
|
|
37
|
-
source:
|
|
37
|
+
source: 'admin' | 'admin-api';
|
|
38
38
|
};
|
|
39
39
|
}) => Promise<string>;
|
|
40
40
|
export interface CreateUserInput {
|
|
@@ -50,10 +50,10 @@ export type CreateUserResult = {
|
|
|
50
50
|
invited: boolean;
|
|
51
51
|
} | {
|
|
52
52
|
ok: false;
|
|
53
|
-
reason:
|
|
53
|
+
reason: 'email_taken';
|
|
54
54
|
} | {
|
|
55
55
|
ok: false;
|
|
56
|
-
reason:
|
|
56
|
+
reason: 'password_policy';
|
|
57
57
|
/** Chave i18n da regra violada + params para interpolar. */
|
|
58
58
|
messageKey: string;
|
|
59
59
|
messageParams?: Record<string, string | number>;
|
|
@@ -141,7 +141,7 @@ export declare class AdminUsersService {
|
|
|
141
141
|
*
|
|
142
142
|
* @returns código i18n/erro quando a operação deve ser bloqueada; `null` quando OK.
|
|
143
143
|
*/
|
|
144
|
-
guardGlobalRolesChange(targetId: string, newRoles: string[], actorId: string | null): Promise<
|
|
144
|
+
guardGlobalRolesChange(targetId: string, newRoles: string[], actorId: string | null): Promise<'last_admin' | 'cannot_self_demote' | null>;
|
|
145
145
|
/** Atualiza nome/avatar (capacidade opcional). Retorna a conta ou null. */
|
|
146
146
|
updateProfile(accountId: string, patch: {
|
|
147
147
|
name?: string | null;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { randomBytes } from
|
|
2
|
-
import { supportsAccountDeletion, supportsAccountStatus,
|
|
3
|
-
import {
|
|
4
|
-
import { AccountDeletionService
|
|
5
|
-
import {
|
|
6
|
-
import { resolveEffectiveRolesCatalog } from
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { supportsAccountDeletion, supportsAccountStatus, supportsCountByGlobalRole, supportsProfile, } from '../../accounts/account_store.js';
|
|
3
|
+
import { PasswordPolicyError } from '../../password/password_manager.js';
|
|
4
|
+
import { AccountDeletionService } from '../account_deletion_service.js';
|
|
5
|
+
import { sendPasswordResetEmail } from '../default_mailer.js';
|
|
6
|
+
import { resolveEffectiveRolesCatalog } from '../runtime_toggles.js';
|
|
7
7
|
/**
|
|
8
8
|
* Lógica de gestão de usuários compartilhada entre o console admin (B6, HTML) e a
|
|
9
9
|
* Admin REST API (R6, JSON). Encapsula o fluxo "create + invite", reset de senha,
|
|
@@ -24,9 +24,9 @@ export class AdminUsersService {
|
|
|
24
24
|
const store = this.cfg.accountStore;
|
|
25
25
|
const existing = await store.findByEmail(input.email);
|
|
26
26
|
if (existing)
|
|
27
|
-
return { ok: false, reason:
|
|
27
|
+
return { ok: false, reason: 'email_taken' };
|
|
28
28
|
const hasPassword = !!input.password;
|
|
29
|
-
const initialPassword = input.password ?? randomBytes(24).toString(
|
|
29
|
+
const initialPassword = input.password ?? randomBytes(24).toString('hex');
|
|
30
30
|
let account;
|
|
31
31
|
try {
|
|
32
32
|
account = await store.create({
|
|
@@ -40,7 +40,7 @@ export class AdminUsersService {
|
|
|
40
40
|
if (error instanceof PasswordPolicyError) {
|
|
41
41
|
return {
|
|
42
42
|
ok: false,
|
|
43
|
-
reason:
|
|
43
|
+
reason: 'password_policy',
|
|
44
44
|
messageKey: error.key,
|
|
45
45
|
messageParams: error.params,
|
|
46
46
|
};
|
|
@@ -48,7 +48,7 @@ export class AdminUsersService {
|
|
|
48
48
|
throw error;
|
|
49
49
|
}
|
|
50
50
|
await this.cfg.audit?.record({
|
|
51
|
-
type:
|
|
51
|
+
type: 'user.created',
|
|
52
52
|
accountId: account.id,
|
|
53
53
|
email: input.email,
|
|
54
54
|
actorId: actor.actorId,
|
|
@@ -70,7 +70,7 @@ export class AdminUsersService {
|
|
|
70
70
|
return null;
|
|
71
71
|
await this.sendResetEmail(ctx, account.email);
|
|
72
72
|
await this.cfg.audit?.record({
|
|
73
|
-
type:
|
|
73
|
+
type: 'user.password_reset_sent',
|
|
74
74
|
accountId,
|
|
75
75
|
email: account.email,
|
|
76
76
|
actorId: actor.actorId,
|
|
@@ -92,7 +92,7 @@ export class AdminUsersService {
|
|
|
92
92
|
else
|
|
93
93
|
await store.enableAccount(accountId);
|
|
94
94
|
await this.cfg.audit?.record({
|
|
95
|
-
type: disable ?
|
|
95
|
+
type: disable ? 'user.disabled' : 'user.enabled',
|
|
96
96
|
accountId,
|
|
97
97
|
actorId: actor.actorId,
|
|
98
98
|
ip: actor.ip,
|
|
@@ -123,7 +123,7 @@ export class AdminUsersService {
|
|
|
123
123
|
// Roles que o usuário não tinha E que não estão no catálogo = inválidas.
|
|
124
124
|
for (const role of roles) {
|
|
125
125
|
if (!catalogNames.has(role) && !currentRoles.has(role)) {
|
|
126
|
-
return
|
|
126
|
+
return 'admin.roles.unknown_role';
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
}
|
|
@@ -137,7 +137,7 @@ export class AdminUsersService {
|
|
|
137
137
|
/** Roles globais que conferem acesso de admin (default ['ADMIN']). */
|
|
138
138
|
adminRoles() {
|
|
139
139
|
const roles = this.cfg.admin?.roles;
|
|
140
|
-
return roles && roles.length > 0 ? roles : [
|
|
140
|
+
return roles && roles.length > 0 ? roles : ['ADMIN'];
|
|
141
141
|
}
|
|
142
142
|
/** Um conjunto de roles contém ao menos uma role de admin? */
|
|
143
143
|
hasAdminRole(roles) {
|
|
@@ -226,11 +226,11 @@ export class AdminUsersService {
|
|
|
226
226
|
return null;
|
|
227
227
|
// Auto-rebaixamento: o ator removendo a própria role de admin.
|
|
228
228
|
if (actorId && actorId === targetId)
|
|
229
|
-
return
|
|
229
|
+
return 'cannot_self_demote';
|
|
230
230
|
// Último admin: se o target é o único admin, a remoção causaria lockout.
|
|
231
231
|
const admins = await this.countAdmins();
|
|
232
232
|
if (admins <= 1)
|
|
233
|
-
return
|
|
233
|
+
return 'last_admin';
|
|
234
234
|
return null;
|
|
235
235
|
}
|
|
236
236
|
/** Atualiza nome/avatar (capacidade opcional). Retorna a conta ou null. */
|
|
@@ -251,15 +251,15 @@ export class AdminUsersService {
|
|
|
251
251
|
async delete(oidc, accountId, actor, enqueue) {
|
|
252
252
|
const store = this.cfg.accountStore;
|
|
253
253
|
if (!supportsAccountDeletion(store))
|
|
254
|
-
return { ok: false, reason:
|
|
254
|
+
return { ok: false, reason: 'unsupported' };
|
|
255
255
|
const account = await store.findById(accountId);
|
|
256
256
|
if (!account)
|
|
257
|
-
return { ok: false, reason:
|
|
258
|
-
const source = actor.source ===
|
|
257
|
+
return { ok: false, reason: 'not_found' };
|
|
258
|
+
const source = actor.source === 'admin' ? 'admin' : 'admin-api';
|
|
259
259
|
// Trilha administrativa ANTES do cascade — assim esta linha também é
|
|
260
260
|
// anonimizada na etapa de anonimização do audit (não reintroduz PII).
|
|
261
261
|
await this.cfg.audit?.record({
|
|
262
|
-
type:
|
|
262
|
+
type: 'user.deleted',
|
|
263
263
|
accountId,
|
|
264
264
|
email: account.email,
|
|
265
265
|
actorId: actor.actorId,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
2
|
import { AdminClientsService } from '../admin_clients_service.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { clientCreateInput, clientInputValidator, clientPartialInput, } from '../admin_validators.js';
|
|
4
|
+
import { apiError, clientDto, createdClientDto } from './dto.js';
|
|
5
5
|
/** Resolve o serviço (== OidcService) + o AdminClientsService. */
|
|
6
6
|
async function clientsService(ctx) {
|
|
7
7
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { TokenVerifyService } from './token_verify_service.js';
|
|
3
2
|
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
4
3
|
import { computeAdminStats } from '../admin_stats_service.js';
|
|
5
|
-
import { auditDto, apiError } from './dto.js';
|
|
6
4
|
import { tokenVerifyValidator } from '../admin_validators.js';
|
|
5
|
+
import { apiError, auditDto } from './dto.js';
|
|
6
|
+
import { TokenVerifyService } from './token_verify_service.js';
|
|
7
7
|
/**
|
|
8
8
|
* Endpoints utilitários da Admin REST API: log de auditoria (`GET /audit`) e
|
|
9
9
|
* introspecção genérica de token (`POST /tokens/verify`).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import {
|
|
3
|
-
import { orgDto, orgDetailDto, orgInvitationDto, apiError } from './dto.js';
|
|
2
|
+
import { orgAddMemberValidator, orgCreateValidator, orgInvitationValidator, orgMemberRoleValidator, orgUpdateValidator, } from '../admin_validators.js';
|
|
4
3
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
5
|
-
import {
|
|
4
|
+
import { AdminOrgsService } from './admin_orgs_service.js';
|
|
5
|
+
import { apiError, orgDetailDto, orgDto, orgInvitationDto } from './dto.js';
|
|
6
6
|
/** Lê a config + monta o actor `admin-api` para auditoria. */
|
|
7
7
|
async function ctxBits(ctx) {
|
|
8
8
|
const service = await ctx.containerResolver.make('authkit.server');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
+
import { SettingLockedError, isSettingLocked, lockedSettingKeys } from '../config_locks.js';
|
|
2
3
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
3
|
-
import {
|
|
4
|
-
import { isSettingLocked, lockedSettingKeys, SettingLockedError } from '../config_locks.js';
|
|
4
|
+
import { apiError, settingDto } from './dto.js';
|
|
5
5
|
/** 423 Locked: a setting foi travada via defineConfig (não editável em runtime). */
|
|
6
6
|
function lockedResponse(ctx, err) {
|
|
7
7
|
return ctx.response
|
|
@@ -16,8 +16,8 @@ function notSupported(ctx) {
|
|
|
16
16
|
function resolveOrgId(ctx) {
|
|
17
17
|
const qs = ctx.request.qs();
|
|
18
18
|
if ('organizationId' in qs) {
|
|
19
|
-
const v = qs
|
|
20
|
-
return v
|
|
19
|
+
const v = qs.organizationId;
|
|
20
|
+
return v?.trim() ? v.trim() : null;
|
|
21
21
|
}
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
@@ -99,7 +99,13 @@ export default class ApiSettingsController {
|
|
|
99
99
|
metadata: { key, value: body.value, organizationId: orgId },
|
|
100
100
|
});
|
|
101
101
|
const saved = await svc.getSetting(key, orgId);
|
|
102
|
-
return settingDto({
|
|
102
|
+
return settingDto({
|
|
103
|
+
key,
|
|
104
|
+
organizationId: orgId,
|
|
105
|
+
value: saved,
|
|
106
|
+
updatedAt: new Date(),
|
|
107
|
+
updatedBy: null,
|
|
108
|
+
});
|
|
103
109
|
}
|
|
104
110
|
/** DELETE /settings/:key */
|
|
105
111
|
async destroy(ctx) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { HttpContext } from
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import type { HttpContext } from '@adonisjs/core/http';
|
|
3
3
|
/**
|
|
4
4
|
* Recurso de usuários da Admin REST API (R6). JSON puro (camelCase), erros no
|
|
5
5
|
* envelope `{ error: { code, message } }`. Toda escrita audita com `actor: 'admin-api'`.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
1
|
+
import '../augmentations.js';
|
|
2
|
+
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
3
|
+
import { adminUserCreateValidator, adminUserUpdateValidator } from '../admin_validators.js';
|
|
4
|
+
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
5
|
+
import { enrichSessionsWithContext } from '../session_context.js';
|
|
6
|
+
import { AdminUsersService } from './admin_users_service.js';
|
|
7
|
+
import { apiError, grantDto, sessionDto, userDto } from './dto.js';
|
|
8
8
|
const PAGE_SIZE = 20;
|
|
9
9
|
/**
|
|
10
10
|
* Lê a config + monta o actor `admin-api` para auditoria. O `actorId` recebe o
|
|
@@ -13,12 +13,12 @@ const PAGE_SIZE = 20;
|
|
|
13
13
|
* vazar o segredo. `null` apenas se a guard não anexou (defensivo).
|
|
14
14
|
*/
|
|
15
15
|
async function ctxBits(ctx) {
|
|
16
|
-
const service = await ctx.containerResolver.make(
|
|
16
|
+
const service = await ctx.containerResolver.make('authkit.server');
|
|
17
17
|
const cfg = service.config;
|
|
18
18
|
const actor = {
|
|
19
19
|
actorId: ctx.adminApiKeyId ?? null,
|
|
20
20
|
ip: ctx.request.ip?.() ?? null,
|
|
21
|
-
source:
|
|
21
|
+
source: 'admin-api',
|
|
22
22
|
};
|
|
23
23
|
return { service, cfg, actor };
|
|
24
24
|
}
|
|
@@ -30,10 +30,9 @@ export default class ApiUsersController {
|
|
|
30
30
|
/** GET /users — listagem paginada com busca por e-mail. */
|
|
31
31
|
async index(ctx) {
|
|
32
32
|
const { cfg } = await ctxBits(ctx);
|
|
33
|
-
const search = ctx.request.input(
|
|
34
|
-
const page = Math.max(1, Number.parseInt(ctx.request.input(
|
|
35
|
-
const limit = Math.max(1, Math.min(100, Number.parseInt(ctx.request.input(
|
|
36
|
-
PAGE_SIZE));
|
|
33
|
+
const search = ctx.request.input('search', '').trim();
|
|
34
|
+
const page = Math.max(1, Number.parseInt(ctx.request.input('page', '1'), 10) || 1);
|
|
35
|
+
const limit = Math.max(1, Math.min(100, Number.parseInt(ctx.request.input('limit', String(PAGE_SIZE)), 10) || PAGE_SIZE));
|
|
37
36
|
const result = await cfg.accountStore.listAccounts({ search, page, limit });
|
|
38
37
|
const users = new AdminUsersService(cfg);
|
|
39
38
|
const data = await Promise.all(result.data.map(async (u) => userDto(u, await users.isDisabled(u.id))));
|
|
@@ -42,10 +41,10 @@ export default class ApiUsersController {
|
|
|
42
41
|
/** GET /users/:id */
|
|
43
42
|
async show(ctx) {
|
|
44
43
|
const { cfg } = await ctxBits(ctx);
|
|
45
|
-
const id = ctx.request.param(
|
|
44
|
+
const id = ctx.request.param('id');
|
|
46
45
|
const account = await cfg.accountStore.findById(id);
|
|
47
46
|
if (!account)
|
|
48
|
-
return ctx.response.notFound(apiError(
|
|
47
|
+
return ctx.response.notFound(apiError('not_found', 'Usuário não encontrado.'));
|
|
49
48
|
const disabled = await new AdminUsersService(cfg).isDisabled(id);
|
|
50
49
|
return userDto(account, disabled);
|
|
51
50
|
}
|
|
@@ -61,10 +60,10 @@ export default class ApiUsersController {
|
|
|
61
60
|
invite: invite ?? false,
|
|
62
61
|
}, actor);
|
|
63
62
|
if (!result.ok) {
|
|
64
|
-
if (result.reason ===
|
|
65
|
-
return ctx.response.badRequest(apiError(
|
|
63
|
+
if (result.reason === 'password_policy') {
|
|
64
|
+
return ctx.response.badRequest(apiError('password_policy', cfg.messages[result.messageKey] ?? result.messageKey));
|
|
66
65
|
}
|
|
67
|
-
return ctx.response.conflict(apiError(
|
|
66
|
+
return ctx.response.conflict(apiError('email_taken', 'Já existe uma conta com este e-mail.'));
|
|
68
67
|
}
|
|
69
68
|
ctx.response.status(201);
|
|
70
69
|
return { ...userDto(result.account), invited: result.invited };
|
|
@@ -72,10 +71,10 @@ export default class ApiUsersController {
|
|
|
72
71
|
/** PATCH /users/:id — { globalRoles?, name?, avatarUrl? }. */
|
|
73
72
|
async update(ctx) {
|
|
74
73
|
const { cfg, actor } = await ctxBits(ctx);
|
|
75
|
-
const id = ctx.request.param(
|
|
74
|
+
const id = ctx.request.param('id');
|
|
76
75
|
const account = await cfg.accountStore.findById(id);
|
|
77
76
|
if (!account)
|
|
78
|
-
return ctx.response.notFound(apiError(
|
|
77
|
+
return ctx.response.notFound(apiError('not_found', 'Usuário não encontrado.'));
|
|
79
78
|
const users = new AdminUsersService(cfg);
|
|
80
79
|
const { globalRoles: roles, name, avatarUrl, } = await ctx.request.validateUsing(adminUserUpdateValidator);
|
|
81
80
|
if (Array.isArray(roles)) {
|
|
@@ -83,11 +82,11 @@ export default class ApiUsersController {
|
|
|
83
82
|
// Proteções de escalonamento/lockout: último admin + auto-rebaixamento.
|
|
84
83
|
// O actorId vem do id da API key (M9); self-demote só aplica se identificável.
|
|
85
84
|
const guard = await users.guardGlobalRolesChange(id, normalized, actor.actorId);
|
|
86
|
-
if (guard ===
|
|
87
|
-
return ctx.response.conflict(apiError(
|
|
85
|
+
if (guard === 'last_admin') {
|
|
86
|
+
return ctx.response.conflict(apiError('last_admin', 'Não é possível remover a última conta com a role de administrador.'));
|
|
88
87
|
}
|
|
89
|
-
if (guard ===
|
|
90
|
-
return ctx.response.conflict(apiError(
|
|
88
|
+
if (guard === 'cannot_self_demote') {
|
|
89
|
+
return ctx.response.conflict(apiError('cannot_self_demote', 'Você não pode remover a sua própria role de administrador.'));
|
|
91
90
|
}
|
|
92
91
|
// Valida contra o catálogo (mesmo caminho do console) — roles fora do
|
|
93
92
|
// catálogo (e que o usuário não tinha) → 422.
|
|
@@ -96,7 +95,7 @@ export default class ApiUsersController {
|
|
|
96
95
|
if (errorKey) {
|
|
97
96
|
return ctx.response
|
|
98
97
|
.status(422)
|
|
99
|
-
.send(apiError(
|
|
98
|
+
.send(apiError('invalid_role', cfg.messages[errorKey] ?? errorKey));
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
if (name !== undefined || avatarUrl !== undefined) {
|
|
@@ -113,20 +112,20 @@ export default class ApiUsersController {
|
|
|
113
112
|
/** DELETE /users/:id — deleção completa (cascade) da conta. */
|
|
114
113
|
async destroy(ctx) {
|
|
115
114
|
const { service, cfg, actor } = await ctxBits(ctx);
|
|
116
|
-
const id = ctx.request.param(
|
|
115
|
+
const id = ctx.request.param('id');
|
|
117
116
|
// OPT-IN durável: passa o enqueue (cascade async); senão delete() roda síncrono.
|
|
118
117
|
const enqueue = cfg.accountLifecycle?.durable
|
|
119
|
-
? (await import(
|
|
118
|
+
? (await import('../durable/index.js')).enqueueDeletionVia(ctx.containerResolver)
|
|
120
119
|
: undefined;
|
|
121
120
|
const outcome = await new AdminUsersService(cfg).delete(service, id, actor, enqueue);
|
|
122
121
|
if (!outcome.ok) {
|
|
123
|
-
if (outcome.reason ===
|
|
124
|
-
return ctx.response.notFound(apiError(
|
|
122
|
+
if (outcome.reason === 'not_found') {
|
|
123
|
+
return ctx.response.notFound(apiError('not_found', 'Usuário não encontrado.'));
|
|
125
124
|
}
|
|
126
|
-
return ctx.response.conflict(apiError(
|
|
125
|
+
return ctx.response.conflict(apiError('capability_unsupported', 'O store de contas não suporta deletar usuários.'));
|
|
127
126
|
}
|
|
128
127
|
// Modo durável: a deleção foi ENFILEIRADA (cascade async) — responde 202-style.
|
|
129
|
-
if (
|
|
128
|
+
if ('enqueued' in outcome) {
|
|
130
129
|
return { id, deleted: true, enqueued: true, runId: outcome.runId };
|
|
131
130
|
}
|
|
132
131
|
return { id, deleted: true, ...outcome.result };
|
|
@@ -141,26 +140,26 @@ export default class ApiUsersController {
|
|
|
141
140
|
}
|
|
142
141
|
async #setStatus(ctx, disable) {
|
|
143
142
|
const { cfg, actor } = await ctxBits(ctx);
|
|
144
|
-
const id = ctx.request.param(
|
|
143
|
+
const id = ctx.request.param('id');
|
|
145
144
|
const applied = await new AdminUsersService(cfg).setStatus(id, disable, actor);
|
|
146
145
|
if (!applied) {
|
|
147
|
-
return ctx.response.conflict(apiError(
|
|
146
|
+
return ctx.response.conflict(apiError('capability_unsupported', 'O store de contas não suporta habilitar/desabilitar.'));
|
|
148
147
|
}
|
|
149
148
|
return { id, disabled: disable };
|
|
150
149
|
}
|
|
151
150
|
/** POST /users/:id/reset-password — envia o e-mail de reset. */
|
|
152
151
|
async resetPassword(ctx) {
|
|
153
152
|
const { cfg, actor } = await ctxBits(ctx);
|
|
154
|
-
const id = ctx.request.param(
|
|
153
|
+
const id = ctx.request.param('id');
|
|
155
154
|
const account = await new AdminUsersService(cfg).resetPassword(ctx, id, actor);
|
|
156
155
|
if (!account)
|
|
157
|
-
return ctx.response.notFound(apiError(
|
|
156
|
+
return ctx.response.notFound(apiError('not_found', 'Usuário não encontrado.'));
|
|
158
157
|
return { id, sent: true };
|
|
159
158
|
}
|
|
160
159
|
/** GET /users/:id/sessions — sessões (enriquecidas com contexto) + grants ativos. */
|
|
161
160
|
async sessions(ctx) {
|
|
162
161
|
const { service, cfg } = await ctxBits(ctx);
|
|
163
|
-
const id = ctx.request.param(
|
|
162
|
+
const id = ctx.request.param('id');
|
|
164
163
|
const admin = new AdminSessionsService(service);
|
|
165
164
|
const sessions = await enrichSessionsWithContext(cfg, id, await admin.listSessions(id));
|
|
166
165
|
const grants = await admin.listGrants(id);
|
|
@@ -173,10 +172,10 @@ export default class ApiUsersController {
|
|
|
173
172
|
/** POST /users/:id/revoke-sessions — revoga todas as sessões/grants. */
|
|
174
173
|
async revokeSessions(ctx) {
|
|
175
174
|
const { service, cfg, actor } = await ctxBits(ctx);
|
|
176
|
-
const id = ctx.request.param(
|
|
175
|
+
const id = ctx.request.param('id');
|
|
177
176
|
const result = await new AdminSessionsService(service).revokeAll(id);
|
|
178
177
|
await cfg.audit?.record({
|
|
179
|
-
type:
|
|
178
|
+
type: 'session.revoked_all',
|
|
180
179
|
accountId: id,
|
|
181
180
|
actorId: actor.actorId,
|
|
182
181
|
ip: actor.ip,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { AuthAccount } from '../../accounts/account_store.js';
|
|
2
|
-
import type {
|
|
3
|
-
import type { OrgDetail } from './admin_orgs_service.js';
|
|
4
|
-
import type { AdminClient, CreatedClient } from '../admin_clients_service.js';
|
|
5
|
-
import type { AdminSession, AdminGrant } from '../admin_sessions_service.js';
|
|
2
|
+
import type { OrgInvitation, OrgMember, OrgSummary } from '../../accounts/account_store.js';
|
|
6
3
|
import type { StoredAuditEvent } from '../../audit/audit_sink.js';
|
|
4
|
+
import type { AdminClient, CreatedClient } from '../admin_clients_service.js';
|
|
5
|
+
import type { AdminGrant, AdminSession } from '../admin_sessions_service.js';
|
|
6
|
+
import type { OrgDetail } from './admin_orgs_service.js';
|
|
7
7
|
/** Projeta uma conta para a forma JSON (camelCase) da Admin REST API. */
|
|
8
8
|
export declare function userDto(account: AuthAccount, disabled?: boolean): {
|
|
9
9
|
id: string;
|