feat(auth): migra login/sessão/roles para o auth partilhado (auditoria 1.3)
config/db.php liga-se com 'auth' => 'shared' em vez de 'own': login, sessão, papéis e guardas passam a correr sobre shared/platform-tools/functions_auth.php (auth_attempt/auth_login/ auth_at_least/auth_is/auth_session_start/auth_remember_*) em vez da lógica local duplicada. Os ~30 ficheiros em views/ que chamam require_login()/require_admin()/require_staff()/require_sysadmin()/ is_admin()/etc. não mudam — só o que está por baixo dessas funções. - roles: staff=40, client=10 registados para preservar a hierarquia sysadmin>admin>staff>client que os antigos in_array() garantiam. `users` já tinha exactamente as colunas por omissão do auth_schema(), não foi preciso mapear nada. - sessão: o bloco manual de ini_set()/session_start() em config/db.php foi substituído pela opção 'session' do platform_connect() — mesmo nome de cookie (FIT_SESS), mesmo SameSite=Lax, secure/httponly passam a ser geridos por auth_session_start(). - «Manter sessão iniciada»: config/functions_remember.php (a reimplementação local do selector/validator) foi eliminado a favor do módulo partilhado (auth_remember_issue/resume/clear via 'remember' => true), que já cobria o mesmo desenho. - auth_attempts: tinha (ip, scope) do throttle próprio da app (config/functions_throttle.php), não o `identifier` que o throttle partilhado exige. Migração aditiva (migration_20260925_shared_auth_attempts.sql) torna a tabela conforme sem tocar no throttle da app, que continua a ser o único usado para login/registo/recuperação — o throttle partilhado não é chamado, para não duplicar a mesma defesa com duas contagens. - last_login_ua (sem equivalente partilhado) passa a ser gravado por fit_stamp_login_ua(), chamado a seguir a auth_login(). Testado ponta-a-ponta num contentor scratch na VPS (site-fitme:8.4, cópia da BD de produção + migração aplicada): visitante anónimo é redirigido, password errada dá o mesmo erro genérico e conta para o throttle (bloqueia à 5ª tentativa, 429), login correcto regenera a sessão, grava last_login_at/ip/ua, audita "auth.login" e (com "lembrar-me") emite o cookie am_remember; esse cookie sozinho, numa sessão nova, repõe a sessão certa com rotação do validator; logout limpa sessão+cookie+token e audita "auth.logout"; platform.php (guard=require_sysadmin) e views/admin/dashboard.php respondem 302 sem sessão e 200 com sessão de sysadmin. php -l limpo em todos os ficheiros alterados. Migração já aplicada em produção (aditiva). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ccc644ac0d
commit
fa87aabb87
9 files changed
+196
-274
No files matched your search
@@ -0,0 +1,34 @@
|
||||
-- ─── auth_attempts: torna-a conforme com o throttle do platform-tools ────────
|
||||
-- Auditoria 2026-09-24, ponto 1.3: a app passa a usar o auth partilhado
|
||||
-- (shared/platform-tools/functions_auth.php) em vez do seu próprio login/
|
||||
-- sessão/roles. Ver config/db.php ('auth' => 'shared').
|
||||
--
|
||||
-- Esta tabela já existia (migration_20260913_local_auth.sql) com o formato do
|
||||
-- throttle PRÓPRIO da app (config/functions_throttle.php: `ip` + `scope`,
|
||||
-- cobre login/registo/recuperação). O throttle partilhado
|
||||
-- (auth_throttle_check/hit em functions_auth.php) espera `identifier` + `ip`
|
||||
-- e falharia (INSERT INTO auth_attempts (identifier, ip) ...) com um erro de
|
||||
-- SQL — silenciosamente engolido pelo try/catch do throttle partilhado, que
|
||||
-- por desenho falha aberto e deixaria de bloquear ninguém.
|
||||
--
|
||||
-- Decisão: a app CONTINUA a usar só o seu próprio throttle (login+registo+
|
||||
-- recuperação, dois níveis IP+conta) — não passa a chamar
|
||||
-- auth_throttle_check()/auth_throttle_hit() em lado nenhum, para não duplicar
|
||||
-- a defesa contra o mesmo formulário com duas contagens diferentes. Esta
|
||||
-- migração só torna a TABELA conforme com o que o platform_conformance()
|
||||
-- (config/db.php, 'conformance' => 'report') verifica, para o relatório de
|
||||
-- saúde ficar limpo e para deixar a porta aberta a usar o throttle partilhado
|
||||
-- no futuro sem precisar de outra migração.
|
||||
--
|
||||
-- `identifier` e `success` são aditivos, com omissão — invisíveis para o
|
||||
-- INSERT/SELECT do throttle próprio, que continua a nomear só `ip`+`scope`.
|
||||
-- `scope` passa a ter omissão (era NOT NULL sem omissão) porque é a única
|
||||
-- coluna que o INSERT do throttle partilhado não preenche; sem omissão esse
|
||||
-- INSERT falhava em modo estrito.
|
||||
--
|
||||
-- Safe to re-run (IF NOT EXISTS é extensão do MariaDB).
|
||||
|
||||
ALTER TABLE `auth_attempts` ADD COLUMN IF NOT EXISTS `identifier` VARCHAR(190) NOT NULL DEFAULT '' AFTER `id`;
|
||||
ALTER TABLE `auth_attempts` ADD COLUMN IF NOT EXISTS `success` TINYINT(1) NOT NULL DEFAULT 0 AFTER `attempted_at`;
|
||||
ALTER TABLE `auth_attempts` MODIFY COLUMN `scope` VARCHAR(20) NOT NULL DEFAULT '';
|
||||
ALTER TABLE `auth_attempts` ADD INDEX IF NOT EXISTS `idx_identifier_time` (`identifier`, `attempted_at`);
|
||||
Reference in new issue
Block a user