@adonis-agora/authkit-server 0.47.0 → 0.49.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 +22 -7
- package/build/index.js +18 -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/account_screen_props.d.ts +163 -0
- package/build/src/host/account_screen_props.js +23 -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 +5 -8
- package/build/src/host/controllers/account_mfa_controller.js +72 -18
- 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 +116 -116
- package/build/src/host/controllers/account_session_controller.js +13 -8
- 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 +23 -58
- package/build/src/host/renderers/inertia_renderer.js +23 -58
- 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 +2 -2
- 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
|
@@ -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;
|
|
@@ -89,11 +89,12 @@ export class AdminClientsService {
|
|
|
89
89
|
// grantTypes derrubava token-exchange).
|
|
90
90
|
const merged = {
|
|
91
91
|
clientId,
|
|
92
|
-
redirectUris: input.redirectUris ??
|
|
93
|
-
postLogoutRedirectUris: input.postLogoutRedirectUris ??
|
|
94
|
-
grantTypes: input.grantTypes ??
|
|
92
|
+
redirectUris: input.redirectUris ?? existing.redirect_uris ?? [],
|
|
93
|
+
postLogoutRedirectUris: input.postLogoutRedirectUris ?? existing.post_logout_redirect_uris ?? [],
|
|
94
|
+
grantTypes: input.grantTypes ?? existing.grant_types ?? [],
|
|
95
95
|
tokenEndpointAuthMethod: input.tokenEndpointAuthMethod ??
|
|
96
|
-
|
|
96
|
+
existing.token_endpoint_auth_method ??
|
|
97
|
+
'client_secret_basic',
|
|
97
98
|
backchannelLogoutUri: input.backchannelLogoutUri ?? existing.backchannel_logout_uri,
|
|
98
99
|
backchannelLogoutSessionRequired: input.backchannelLogoutSessionRequired ??
|
|
99
100
|
existing.backchannel_logout_session_required,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { extname } from 'node:path';
|
|
3
|
-
import { getAdminPrefix } from '../admin_prefix.js';
|
|
4
3
|
import { resolveAccountRoles } from '../account_roles.js';
|
|
4
|
+
import { getAdminPrefix } from '../admin_prefix.js';
|
|
5
5
|
// ─── Shell HTML cache ─────────────────────────────────────────────────────────
|
|
6
6
|
/**
|
|
7
7
|
* Points to the Vite-built index.html inside build/host/ui-dist/.
|
|
@@ -147,7 +147,9 @@ export default class AdminShellController {
|
|
|
147
147
|
async serveAsset(ctx) {
|
|
148
148
|
// AdonisJS wildcard route `*` → params['*'] is an array of path segments.
|
|
149
149
|
const wildcardParams = ctx.request.params()['*'];
|
|
150
|
-
const file = Array.isArray(wildcardParams)
|
|
150
|
+
const file = Array.isArray(wildcardParams)
|
|
151
|
+
? wildcardParams.join('/')
|
|
152
|
+
: String(wildcardParams ?? '');
|
|
151
153
|
if (!file || file.includes('..') || file.includes('\0')) {
|
|
152
154
|
return ctx.response.notFound();
|
|
153
155
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
+
import { apiError, clientDto, createdClientDto } from '../admin_api/dto.js';
|
|
2
3
|
import { AdminClientsService } from '../admin_clients_service.js';
|
|
3
|
-
import {
|
|
4
|
-
import { clientInputValidator, clientCreateInput, clientPartialInput } from '../admin_validators.js';
|
|
4
|
+
import { clientCreateInput, clientInputValidator, clientPartialInput, } from '../admin_validators.js';
|
|
5
5
|
/**
|
|
6
6
|
* Endpoints JSON de clients OIDC do console admin React.
|
|
7
7
|
*
|
|
@@ -89,7 +89,9 @@ export default class ConsoleClientsController {
|
|
|
89
89
|
newSecret = await svc.regenerateSecret(id);
|
|
90
90
|
}
|
|
91
91
|
catch (err) {
|
|
92
|
-
return ctx.response
|
|
92
|
+
return ctx.response
|
|
93
|
+
.status(409)
|
|
94
|
+
.send(apiError('not_applicable', err.message ?? 'Cannot regenerate secret for a public client.'));
|
|
93
95
|
}
|
|
94
96
|
await cfg.audit?.record({
|
|
95
97
|
type: 'client.secret_regenerated',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { buildImpersonationPanel } from '../impersonation.js';
|
|
3
2
|
import { apiError } from '../admin_api/dto.js';
|
|
3
|
+
import { buildImpersonationPanel } from '../impersonation.js';
|
|
4
4
|
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
5
5
|
/**
|
|
6
6
|
* Endpoint JSON do painel de impersonation do console admin React.
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
3
2
|
import { apiError } from '../admin_api/dto.js';
|
|
4
3
|
import { buildKeysStatus, rotateNow } from '../key_rotation_actions.js';
|
|
4
|
+
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
5
5
|
function notSupported(ctx) {
|
|
6
|
-
return ctx.response
|
|
7
|
-
.status(501)
|
|
8
|
-
.send(apiError('not_implemented', 'jwks não é managed+store.'));
|
|
6
|
+
return ctx.response.status(501).send(apiError('not_implemented', 'jwks não é managed+store.'));
|
|
9
7
|
}
|
|
10
8
|
/**
|
|
11
9
|
* Endpoints JSON de gestão da chave de assinatura managed para o console admin React.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
3
2
|
import { AdminOrgsService } from '../admin_api/admin_orgs_service.js';
|
|
3
|
+
import { apiError, orgDetailDto, orgDto } from '../admin_api/dto.js';
|
|
4
|
+
import { orgAddMemberValidator, orgCreateValidator, orgInvitationValidator, orgMemberRoleValidator, orgUpdateValidator, } from '../admin_validators.js';
|
|
5
|
+
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
4
6
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
5
|
-
import { orgDto, orgDetailDto, apiError } from '../admin_api/dto.js';
|
|
6
|
-
import { orgCreateValidator, orgUpdateValidator, orgAddMemberValidator, orgMemberRoleValidator, orgInvitationValidator, } from '../admin_validators.js';
|
|
7
7
|
/**
|
|
8
8
|
* Endpoints JSON de organizações do console admin React.
|
|
9
9
|
* 404 honesto quando o store não suporta organizações (`capability_unsupported`).
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
-
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
3
|
-
import { resolveEffectiveRolesCatalog, SETTING_KEYS, } from '../runtime_toggles.js';
|
|
4
|
-
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
5
2
|
import { apiError } from '../admin_api/dto.js';
|
|
6
3
|
import { roleCreateValidator, roleUpdateValidator } from '../admin_validators.js';
|
|
4
|
+
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
5
|
+
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
6
|
+
import { SETTING_KEYS, resolveEffectiveRolesCatalog, } from '../runtime_toggles.js';
|
|
7
7
|
/** Regex de validação: nome de role — letras maiúsculas, dígitos e underscore. */
|
|
8
8
|
const ROLE_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
9
9
|
/** ADMIN é protegido: não pode ser removido. */
|
|
@@ -53,7 +53,12 @@ export default class ConsoleRolesController {
|
|
|
53
53
|
newEntry.description = description;
|
|
54
54
|
const updated = [...current.roles, newEntry].sort((a, b) => a.name.localeCompare(b.name));
|
|
55
55
|
await rs.setSetting(SETTING_KEYS.ROLES_CATALOG, { roles: updated }, actorId);
|
|
56
|
-
await cfg.audit?.record({
|
|
56
|
+
await cfg.audit?.record({
|
|
57
|
+
type: 'roles_catalog.updated',
|
|
58
|
+
actorId,
|
|
59
|
+
ip,
|
|
60
|
+
metadata: { action: 'create', role: name },
|
|
61
|
+
});
|
|
57
62
|
return { name, description: description || undefined };
|
|
58
63
|
}
|
|
59
64
|
/** PATCH {prefix}/api/roles/:name — { description? } */
|
|
@@ -72,11 +77,14 @@ export default class ConsoleRolesController {
|
|
|
72
77
|
if (!current.roles.some((r) => r.name === roleName)) {
|
|
73
78
|
return ctx.response.notFound(apiError('not_found', 'Role não encontrada.'));
|
|
74
79
|
}
|
|
75
|
-
const updatedRoles = current.roles.map((r) => r.name === roleName
|
|
76
|
-
? { name: r.name, ...(description ? { description } : {}) }
|
|
77
|
-
: r);
|
|
80
|
+
const updatedRoles = current.roles.map((r) => r.name === roleName ? { name: r.name, ...(description ? { description } : {}) } : r);
|
|
78
81
|
await rs.setSetting(SETTING_KEYS.ROLES_CATALOG, { roles: updatedRoles }, actorId);
|
|
79
|
-
await cfg.audit?.record({
|
|
82
|
+
await cfg.audit?.record({
|
|
83
|
+
type: 'roles_catalog.updated',
|
|
84
|
+
actorId,
|
|
85
|
+
ip,
|
|
86
|
+
metadata: { action: 'edit', role: roleName },
|
|
87
|
+
});
|
|
80
88
|
const found = updatedRoles.find((r) => r.name === roleName);
|
|
81
89
|
return found;
|
|
82
90
|
}
|
|
@@ -88,7 +96,9 @@ export default class ConsoleRolesController {
|
|
|
88
96
|
const ip = ctx.request.ip?.() ?? null;
|
|
89
97
|
const roleName = decodeURIComponent(ctx.request.param('name'));
|
|
90
98
|
if (roleName === PROTECTED_ROLE) {
|
|
91
|
-
return ctx.response
|
|
99
|
+
return ctx.response
|
|
100
|
+
.status(409)
|
|
101
|
+
.send(apiError('protected', cfg.messages['admin.roles.admin_protected'] ?? 'A role ADMIN é protegida.'));
|
|
92
102
|
}
|
|
93
103
|
const rs = await resolveRuntimeSettings(ctx);
|
|
94
104
|
if (!rs)
|
|
@@ -97,12 +107,20 @@ export default class ConsoleRolesController {
|
|
|
97
107
|
if (!current.roles.some((r) => r.name === roleName)) {
|
|
98
108
|
return ctx.response.notFound(apiError('not_found', 'Role não encontrada.'));
|
|
99
109
|
}
|
|
100
|
-
|
|
110
|
+
const updatedRoles = current.roles.filter((r) => r.name !== roleName);
|
|
101
111
|
if (!updatedRoles.some((r) => r.name === PROTECTED_ROLE)) {
|
|
102
|
-
updatedRoles.unshift({
|
|
112
|
+
updatedRoles.unshift({
|
|
113
|
+
name: PROTECTED_ROLE,
|
|
114
|
+
description: 'Full access to the admin console',
|
|
115
|
+
});
|
|
103
116
|
}
|
|
104
117
|
await rs.setSetting(SETTING_KEYS.ROLES_CATALOG, { roles: updatedRoles }, actorId);
|
|
105
|
-
await cfg.audit?.record({
|
|
118
|
+
await cfg.audit?.record({
|
|
119
|
+
type: 'roles_catalog.updated',
|
|
120
|
+
actorId,
|
|
121
|
+
ip,
|
|
122
|
+
metadata: { action: 'delete', role: roleName },
|
|
123
|
+
});
|
|
106
124
|
return { ok: true, deleted: roleName };
|
|
107
125
|
}
|
|
108
126
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
+
import { apiError, grantDto, sessionDto } from '../admin_api/dto.js';
|
|
2
3
|
import { AdminSessionsService } from '../admin_sessions_service.js';
|
|
3
|
-
import { enrichSessionsWithContext } from '../session_context.js';
|
|
4
|
-
import { sessionDto, grantDto, apiError } from '../admin_api/dto.js';
|
|
5
|
-
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
6
4
|
import { sessionAccountValidator } from '../admin_validators.js';
|
|
5
|
+
import { ACCOUNT_SESSION_KEY } from '../middleware/account_auth.js';
|
|
6
|
+
import { enrichSessionsWithContext } from '../session_context.js';
|
|
7
7
|
/**
|
|
8
8
|
* Endpoints JSON de sessões/grants do console admin React.
|
|
9
9
|
*
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import '../augmentations.js';
|
|
2
|
+
import { apiError, settingDto } from '../admin_api/dto.js';
|
|
3
|
+
import { SettingLockedError, isSettingLocked, lockedSettingKeys } from '../config_locks.js';
|
|
2
4
|
import { resolveRuntimeSettings } from '../runtime_settings.js';
|
|
3
|
-
import { settingDto, apiError } from '../admin_api/dto.js';
|
|
4
|
-
import { isSettingLocked, lockedSettingKeys, SettingLockedError } from '../config_locks.js';
|
|
5
5
|
/** 423 Locked: setting travada via defineConfig. */
|
|
6
6
|
function lockedResponse(ctx, err) {
|
|
7
7
|
return ctx.response
|
|
@@ -15,8 +15,8 @@ function notSupported(ctx) {
|
|
|
15
15
|
function resolveOrgId(ctx) {
|
|
16
16
|
const qs = ctx.request.qs();
|
|
17
17
|
if ('organizationId' in qs) {
|
|
18
|
-
const v = qs
|
|
19
|
-
return v
|
|
18
|
+
const v = qs.organizationId;
|
|
19
|
+
return v?.trim() ? v.trim() : null;
|
|
20
20
|
}
|
|
21
21
|
return null;
|
|
22
22
|
}
|
|
@@ -80,7 +80,13 @@ export default class ConsoleSettingsController {
|
|
|
80
80
|
metadata: { key, value: body.value, source: 'admin-console', organizationId: orgId },
|
|
81
81
|
});
|
|
82
82
|
const saved = await svc.getSetting(key, orgId);
|
|
83
|
-
return settingDto({
|
|
83
|
+
return settingDto({
|
|
84
|
+
key,
|
|
85
|
+
organizationId: orgId,
|
|
86
|
+
value: saved,
|
|
87
|
+
updatedAt: new Date(),
|
|
88
|
+
updatedBy: null,
|
|
89
|
+
});
|
|
84
90
|
}
|
|
85
91
|
/** DELETE {prefix}/api/settings/:key */
|
|
86
92
|
async destroy(ctx) {
|