diff --git a/public_html/admins.php b/public_html/admins.php index 8a775f9..cd0df29 100644 --- a/public_html/admins.php +++ b/public_html/admins.php @@ -23,11 +23,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $ldapObj = new Ldap(); $ldapObj->bindService(); $entry = $ldapObj->findUserByLogin($login); - $nome = $entry['displayname'][0] ?? null; if ($entry === null) { $erro = 'Login não encontrado no AD. Confira e tente novamente.'; } else { + $nome = $entry['displayname'][0] ?? null; AdminRepository::add($login, $nome, $papel, $quemFez); AuditLog::record($quemFez, 'adicionou_admin', $login, "papel={$papel}"); $ok = 'Administrador adicionado com sucesso.'; @@ -36,18 +36,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $erro = 'Erro ao consultar o AD: ' . $e->getMessage(); } } - } elseif ($acao === 'remover') { + } elseif ($acao === 'desativar') { $id = (int) ($_POST['id'] ?? 0); $admin = AdminRepository::find($id); if (!$admin) { $erro = 'Administrador não encontrado.'; } elseif ($admin['papel'] === 'admin' && AdminRepository::countActiveAdmins($id) === 0) { - $erro = 'Não é possível remover o último administrador ativo do sistema.'; + $erro = 'Não é possível desativar o último administrador ativo do sistema.'; } else { AdminRepository::setActive($id, false); - AuditLog::record($quemFez, 'removeu_admin', $admin['login']); - $ok = 'Administrador removido (desativado) com sucesso.'; + AuditLog::record($quemFez, 'desativou_admin', $admin['login']); + $ok = 'Administrador desativado com sucesso.'; } } elseif ($acao === 'reativar') { $id = (int) ($_POST['id'] ?? 0); @@ -57,6 +57,22 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { AuditLog::record($quemFez, 'reativou_admin', $admin['login']); $ok = 'Administrador reativado.'; } + } elseif ($acao === 'atualizar_papel') { + $id = (int) ($_POST['id'] ?? 0); + $papel = ($_POST['papel'] ?? 'operador') === 'admin' ? 'admin' : 'operador'; + $admin = AdminRepository::find($id); + + if (!$admin) { + $erro = 'Administrador não encontrado.'; + } elseif ($admin['papel'] === 'admin' && $papel === 'operador' && AdminRepository::countActiveAdmins($id) === 0) { + $erro = 'Não é possível rebaixar o último administrador ativo do sistema.'; + } elseif ($admin['papel'] === $papel) { + $ok = 'Nenhuma alteração: o papel já era esse.'; + } else { + AdminRepository::setPapel($id, $papel); + AuditLog::record($quemFez, 'alterou_papel_admin', $admin['login'], "de={$admin['papel']} para={$papel}"); + $ok = 'Papel atualizado com sucesso.'; + } } } @@ -94,7 +110,21 @@ View::header('Administradores'); - + + +
+ + + + +
+ + + + Ativo @@ -106,8 +136,8 @@ View::header('Administradores');
- - + + diff --git a/public_html/auditoria.php b/public_html/auditoria.php new file mode 100644 index 0000000..90d6456 --- /dev/null +++ b/public_html/auditoria.php @@ -0,0 +1,86 @@ +query('SELECT COUNT(*) FROM audit_log')->fetchColumn(); + +$stmt = Database::pdo()->prepare( + 'SELECT * FROM audit_log ORDER BY criado_em DESC, id DESC LIMIT :limit OFFSET :offset' +); +$stmt->bindValue('limit', $perPage, PDO::PARAM_INT); +$stmt->bindValue('offset', $offset, PDO::PARAM_INT); +$stmt->execute(); +$logs = $stmt->fetchAll(); + +$totalPages = max(1, (int) ceil($total / $perPage)); + +$acaoLabels = [ + 'login' => 'Login', + 'login_negado' => 'Login negado (sem permissão)', + 'login_falhou' => 'Login falhou (usuário/senha)', + 'logout' => 'Logoff', + 'habilitou_aluno' => 'Habilitou aluno', + 'desabilitou_aluno' => 'Desabilitou aluno', + 'alterou_senha_aluno' => 'Alterou senha de aluno', + 'adicionou_admin' => 'Adicionou administrador', + 'desativou_admin' => 'Desativou administrador', + 'reativou_admin' => 'Reativou administrador', + 'alterou_papel_admin' => 'Alterou papel de administrador', + 'atualizou_settings' => 'Atualizou configurações', +]; + +View::header('Auditoria'); +?> +
+

Auditoria

+

+ Histórico de logins (inclusive tentativas com senha incorreta), alterações em + alunos, administradores e configurações. Mostrando de registros. +

+ + + + + + + + + + + + + + + + + + + + + + + +
Data/horaUsuárioAçãoAlvoDetalheIP
Nenhum registro ainda.
+ + 1): ?> +

+ + + + + + + | + +

+ +
+ $cn, - 'nome' => $entry['sn'][0] ?? $cn, + 'nome' => $entry['displayname'][0] ?? $entry['cn'][0] ?? $cn, 'habilitado' => ($accountControl & 2) !== 2, 'descricao' => $entry['description'][0] ?? '', ]; diff --git a/public_html/index.php b/public_html/index.php index 5a1e8bb..af6c9b3 100644 --- a/public_html/index.php +++ b/public_html/index.php @@ -33,13 +33,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { - Gestão de Alunos - LabInfo + Gestão de Alunos Lab. Informática
-

Gestão de Alunos (AD)

+

Gestão de Alunos Lab. Informática

diff --git a/src/AdminRepository.php b/src/AdminRepository.php index b585c87..0c9b2d2 100644 --- a/src/AdminRepository.php +++ b/src/AdminRepository.php @@ -50,6 +50,12 @@ final class AdminRepository $stmt->execute(['ativo' => $active ? 1 : 0, 'id' => $id]); } + public static function setPapel(int $id, string $papel): void + { + $stmt = Database::pdo()->prepare('UPDATE admins SET papel = :papel WHERE id = :id'); + $stmt->execute(['papel' => $papel, 'id' => $id]); + } + public static function find(int $id): ?array { $stmt = Database::pdo()->prepare('SELECT * FROM admins WHERE id = :id'); diff --git a/src/Auth.php b/src/Auth.php index 4228959..c1d9f26 100644 --- a/src/Auth.php +++ b/src/Auth.php @@ -34,6 +34,7 @@ final class Auth self::logAttempt($login, $ip, $bindOk); if (!$bindOk) { + AuditLog::record($login, 'login_falhou', null, 'usuário ou senha incorretos'); return ['ok' => false, 'error' => 'Usuário ou senha incorretos.']; } diff --git a/src/Ldap.php b/src/Ldap.php index 854553f..a96ecff 100644 --- a/src/Ldap.php +++ b/src/Ldap.php @@ -19,6 +19,18 @@ final class Ldap ldap_set_option($this->connect, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($this->connect, LDAP_OPT_REFERRALS, 0); ldap_set_option($this->connect, LDAP_OPT_NETWORK_TIMEOUT, 5); + + // O AD recusa operações sensíveis (troca de senha via unicodePwd) em canal + // não criptografado ("Server is unwilling to perform"). Se o servidor não + // for ldaps://, negocia STARTTLS antes de qualquer bind. + if (!str_starts_with(strtolower($server), 'ldaps://')) { + if (!@ldap_start_tls($this->connect)) { + throw new LdapException( + 'Falha ao negociar STARTTLS com o LDAP: ' . ldap_error($this->connect) . + '. Troque ldap.server para ldaps:// no config/local.php se o servidor não suportar STARTTLS.' + ); + } + } } /** diff --git a/src/View.php b/src/View.php index 230d7f4..040d3bf 100644 --- a/src/View.php +++ b/src/View.php @@ -8,17 +8,18 @@ final class View - <?= htmlspecialchars($title, ENT_QUOTES) ?> - LabInfo + <?= htmlspecialchars($title, ENT_QUOTES) ?> - Gestão de Alunos Lab. Informática
- Gestão de Alunos (AD) + Gestão de Alunos Lab. Informática