feat(audit): platform-tools atualizado (12f26fd) e registo de auditoria ligado
- shared/platform-tools: ea3616a → 12f26fd (27 commits). Traz o módulo de audit (f761d99, o pin da frota), «manter sessão iniciada», o registo de segurança da frota (o gancho que o login já tinha passa a funcionar) e a correção do IP do cliente encontrada nesta auditoria (último X-Forwarded-For / X-Real-IP), promovida ao partilhado. client_ip() da app delega nele. - config/functions_audit.php: audit() sobre platform_audit(); tabela audit_log criada no primeiro uso, segredos ocultados, nunca parte a ação. - Registado: login, falha de login, bloqueio por excesso de tentativas, logout, pedidos de recuperação, mudança/definição de palavra-passe, registo e criação de utilizadores, convites, perfil (antes/depois), todas as definições (db_update_setting, com valor anterior), cada passo da lixeira (apagar, restaurar, eliminar de vez) e ações nas mensagens. - Sistema → Auditoria (views/sysadmin/audit.php), só sysadmin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e300fc341
commit
0e8b53f620
65 files changed
+5852
-455
No files matched your search
@@ -75,6 +75,11 @@ function auth_login(PDO $pdo, string $email, string $password): ?array {
|
||||
function auth_set_password(PDO $pdo, int $userId, string $password): void {
|
||||
$pdo->prepare("UPDATE users SET password_hash = ? WHERE id = ?")
|
||||
->execute([password_hash($password, PASSWORD_DEFAULT), $userId]);
|
||||
$self = (int) ($_SESSION['user_id'] ?? 0) === $userId;
|
||||
audit($self ? 'auth.password_change' : 'user.password_set', [
|
||||
'target_type' => 'user', 'target_id' => $userId,
|
||||
'summary' => $self ? 'O próprio alterou a palavra-passe' : 'Palavra-passe definida por outra pessoa ou por link de recuperação',
|
||||
]);
|
||||
|
||||
// Best-effort: the password itself is already saved, so a missing
|
||||
// `password_resets` table (migration not applied yet) must not turn a
|
||||
@@ -116,6 +121,8 @@ function auth_register_client(PDO $pdo, array $data): array {
|
||||
$pdo->prepare(
|
||||
"UPDATE users SET name = ?, phone = COALESCE(?, phone), password_hash = ?, is_active = 1 WHERE id = ?"
|
||||
)->execute([$name, $phone, password_hash($data['password'], PASSWORD_DEFAULT), $row['id']]);
|
||||
audit('user.register', ['target_type' => 'user', 'target_id' => (int) $row['id'], 'target_label' => $name,
|
||||
'summary' => 'Registo que reclamou uma conta de cliente sem palavra-passe', 'actor' => null]);
|
||||
return ['id' => (int) $row['id']];
|
||||
}
|
||||
|
||||
@@ -136,7 +143,10 @@ function auth_register_client(PDO $pdo, array $data): array {
|
||||
if ((int) ($e->errorInfo[1] ?? 0) === 1062) return ['error' => 'email_taken'];
|
||||
throw $e;
|
||||
}
|
||||
return ['id' => (int) $pdo->lastInsertId()];
|
||||
$newId = (int) $pdo->lastInsertId();
|
||||
audit('user.register', ['target_type' => 'user', 'target_id' => $newId, 'target_label' => $name,
|
||||
'summary' => 'Novo cliente registado no site', 'actor' => null]);
|
||||
return ['id' => $newId];
|
||||
}
|
||||
|
||||
// ─── Backoffice user creation ────────────────────────────────────────────────
|
||||
@@ -153,7 +163,10 @@ function auth_create_sysadmin(PDO $pdo, string $name, string $email, string $pas
|
||||
strtolower(trim($email)),
|
||||
password_hash($password, PASSWORD_DEFAULT),
|
||||
]);
|
||||
return (int) $pdo->lastInsertId();
|
||||
$id = (int) $pdo->lastInsertId();
|
||||
audit('user.create', ['target_type' => 'user', 'target_id' => $id, 'target_label' => $name,
|
||||
'summary' => 'Sysadmin criado pelo setup inicial', 'actor' => null]);
|
||||
return $id;
|
||||
}
|
||||
|
||||
// Returns ['id' => int] on success, or ['error' => 'email_taken'].
|
||||
@@ -184,7 +197,10 @@ function _create_backoffice_user(PDO $pdo, string $role, string $name, string $e
|
||||
if ((int) ($e->errorInfo[1] ?? 0) === 1062) return ['error' => 'email_taken'];
|
||||
throw $e;
|
||||
}
|
||||
return ['id' => (int) $pdo->lastInsertId()];
|
||||
$id = (int) $pdo->lastInsertId();
|
||||
audit('user.create', ['target_type' => 'user', 'target_id' => $id, 'target_label' => $name,
|
||||
'changes' => ['role' => ['antes' => null, 'depois' => $role]]]);
|
||||
return ['id' => $id];
|
||||
}
|
||||
|
||||
// ─── Password reset ──────────────────────────────────────────────────────────
|
||||
@@ -257,6 +273,7 @@ function send_password_reset_email(string $name, string $email, string $resetUrl
|
||||
// same mechanism, different copy.
|
||||
function send_password_invite_email(PDO $pdo, array $user): bool {
|
||||
if (empty($user['email'])) return false;
|
||||
audit('user.invite', ['target_type' => 'user', 'target_id' => (int) $user['id'], 'target_label' => (string) ($user['name'] ?? '')]);
|
||||
$token = auth_create_reset_token($pdo, (int) $user['id']);
|
||||
$url = base_url('views/auth/reset_password.php?token=' . $token);
|
||||
$name = (string) ($user['name'] ?? '');
|
||||
|
||||
Reference in new issue
Block a user