feat(auth): harden login flows and finalize quality-check coverage

This commit is contained in:
2026-03-07 22:54:33 +01:00
parent aefe80457c
commit 7153fec05c
20 changed files with 1769 additions and 28 deletions

View File

@@ -0,0 +1,297 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"plan_ref": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json",
"summary": "Explizite Guard-Matrix fuer alle In-Scope-Flows angelegt (Flow->Datei-Zuordnung, Guard-Refs, priorisierte Findings, priority_summary mit critical/high/medium open=0)."
},
{
"path": "pages/auth/register().php",
"summary": "Register-POST auf klares POST-Gate umgestellt und CSRF-Pruefung vor Request-Verarbeitung erzwungen; CSRF-Fehlerpfad konsistent umgesetzt."
},
{
"path": "templates/login.phtml",
"summary": "Lokalisierte Passwort-Toggle-Labels als Datenattribute fuer JS-Consumer bereitgestellt."
},
{
"path": "web/js/components/app-password-toggle.js",
"summary": "Login-Passwort-Toggle konsolidiert: duplicate-init-Guard eingefuehrt (passwordToggleBound), erneute Wrapper/Listener-Bindungen verhindert."
},
{
"path": "web/js/components/app-password-hints.js",
"summary": "Login-Passworthinweise konsolidiert: idempotente Container-Bindung eingefuehrt (passwordHintsBound), Mehrfach-Listener bei Re-Init verhindert."
},
{
"path": "web/css/pages/app-login.css",
"summary": "Ungenutzten Legacy-Selektor .back_to_login entfernt (konkrete CSS-Cleanup-Massnahme im Login-Scope)."
},
{
"path": "i18n/default_de.json",
"summary": "Neue i18n-Keys fuer Passwort-Toggle (Show/Hide password) in Deutsch ergaenzt."
},
{
"path": "i18n/default_en.json",
"summary": "Neue i18n-Keys fuer Passwort-Toggle (Show/Hide password) in Englisch ergaenzt."
},
{
"path": "tests/Service/Auth/PasswordResetServiceTest.php",
"summary": "Neue Unit-Tests fuer PasswordResetService (requestReset/verifyCode/resetPassword inkl. expiry/attempts/used/success)."
},
{
"path": "tests/Service/Auth/EmailVerificationServiceTest.php",
"summary": "Neue Unit-Tests fuer EmailVerificationService (sendVerification/verifyCode/resendVerification inkl. already_verified/attempt-limit)."
},
{
"path": "tests/Service/Auth/MicrosoftOidcServiceTest.php",
"summary": "Automatisierten SSO-Regressionstest fuer Callback-Domain-Policy ergänzt (email_domain_not_allowed)."
},
{
"path": "tests/Architecture/AuthLoginFrontendCleanupContractTest.php",
"summary": "Neuer Architektur-Contract fuer Frontend-Cleanup-Invarianten (kein .back_to_login, duplicate-binding guards in Login-JS)."
},
{
"path": "tests/Architecture/AuthLoginAccessibilityContractTest.php",
"summary": "A11y-Contract erweitert: Toggle muss lokalisierte Labels nutzen und darf kein tabIndex=-1 verwenden."
},
{
"path": "tests/Architecture/AuthRegisterSecurityContractTest.php",
"summary": "Neuer Security-Contract: Register-POST muss CSRF vor Payload-Verarbeitung pruefen."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-001",
"status": "pass",
"evidence": "Guard-Matrix dokumentiert pro Auth-Flow die Layer-Zuordnung; Aenderungen bleiben in bestehender Schichtung (Pages orchestration, Services business logic)."
},
{
"guard_id": "GR-CORE-002",
"status": "pass",
"evidence": "QG-004 structural checks weiterhin 0; keine direkte Service/Gateway/Repository-Instanziierung in pages eingefuehrt."
},
{
"guard_id": "GR-CORE-003",
"status": "pass",
"evidence": "Auth pages nutzen weiterhin requestInput()-Vertrag; register nutzt now isMethod('POST') + requestInput body access ohne superglobal drift."
},
{
"guard_id": "GR-CORE-004",
"status": "pass",
"evidence": "QG-004 ApiResponse::readJsonBody check in pages/api/v1 bleibt 0."
},
{
"guard_id": "GR-CORE-005",
"status": "pass",
"evidence": "Keine Abschwaechung serverseitiger AuthZ-Pruefungen; Guard-Matrix zeigt keine offenen critical/high findings in AuthZ-relevanten Flows."
},
{
"guard_id": "GR-CORE-006",
"status": "pass",
"evidence": "Tenant-Scope-Invarianten unveraendert; SSO-Callback-Regression nun automatisiert mit Domain-Policy-Fehlerpfad abgesichert."
},
{
"guard_id": "GR-CORE-008",
"status": "pass",
"evidence": "Keine neuen direkten Superglobal-Zugriffe in pages/auth oder api pages eingefuehrt."
},
{
"guard_id": "GR-CORE-012",
"status": "pass",
"evidence": "Mutierende Erfolgsfaelle bleiben PRG-konform (register success -> redirect verify-email etc.)."
},
{
"guard_id": "GR-SEC-001",
"status": "pass",
"evidence": "Register-POST enforce't CSRF vor Payload-Verarbeitung; AuthRegisterSecurityContractTest bestaetigt die Reihenfolge."
},
{
"guard_id": "GR-SEC-002",
"status": "pass",
"evidence": "Keine neuen unredacted PII/secret logging paths eingefuehrt; neue Tests arbeiten mock-basiert ohne Log-Ausleitung."
},
{
"guard_id": "GR-SEC-003",
"status": "pass",
"evidence": "Keine SQL-String-Building-Aenderungen; QG-004 structural checks weiterhin 0 und neue Tests nutzen Repository-Interfaces."
},
{
"guard_id": "GR-SEC-004",
"status": "pass",
"evidence": "Keine externen CDN/remote assets in touched login templates/js/css eingefuehrt."
},
{
"guard_id": "GR-SEC-005",
"status": "pass",
"evidence": "Keine Kryptografie-Aenderungen in diesem Run; bestehende Crypto-Pfade unveraendert."
},
{
"guard_id": "GR-UI-009",
"status": "pass",
"evidence": "A11y bleibt intakt: AuthLoginAccessibilityContractTest gruen, Toggle keyboard-fokusfaehig und semantisch button-basiert."
},
{
"guard_id": "GR-UI-010",
"status": "pass",
"evidence": "Neue Toggle-Texte werden ueber t()-Keys bereitgestellt und in de/en gepflegt."
},
{
"guard_id": "GR-UI-011",
"status": "pass",
"evidence": "Login-JS konsolidiert: app-password-toggle und app-password-hints besitzen duplicate-binding guards (idempotente Initialisierung)."
},
{
"guard_id": "GR-UI-012",
"status": "pass",
"evidence": "Konkrete Login-CSS-Cleanup-Massnahme umgesetzt: ungenutzter Selektor .back_to_login entfernt; Contract testet Abwesenheit."
},
{
"guard_id": "GR-UI-013",
"status": "pass",
"evidence": "Interaktive Controls bleiben semantisch/keyboard-operabel; Toggle aktualisiert aria-labels korrekt (show/hide)."
},
{
"guard_id": "GR-UI-015",
"status": "pass",
"evidence": "i18n key completeness erfuellt: Show/Hide password in default_de.json und default_en.json vorhanden."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "Testabdeckung erweitert fuer Reset/Verify plus explizite SSO-Regression (MicrosoftOidcServiceTest callback domain policy)."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "Neue Tests bleiben DI/mock-freundlich ohne untestbare neue global side-effects."
},
{
"guard_id": "GR-LANG-002",
"status": "pass",
"evidence": "composer cs:check pass (0/518 files fixable)."
}
],
"commands": [
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Service/Auth/MicrosoftOidcServiceTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginFrontendCleanupContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Service/Auth/PasswordResetServiceTest.php tests/Service/Auth/EmailVerificationServiceTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php tests/Architecture/AuthRegisterSecurityContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "(rg 'new\\s+[^\\s(]+Factory\\(' lib/Service || true) | wc -l && (rg --glob '!**/*Factory.php' 'new\\s+[^\\s(]+(Service|Gateway|Repository)\\(' lib/Service || true) | wc -l && (rg \"\\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\\(\" pages lib templates web || true) | wc -l && (rg -n 'new\\s+[^\\s(]+(Service|Gateway|Repository)\\(' pages -g '*.php' || true) | wc -l && (rg -n '\\bDB::' pages -g '*.php' || true) | wc -l && (rg -n 'app\\([^\\n]*Factory::class\\)->create' pages/admin -g '*.php' || true) | wc -l && (rg -n 'Factory::class' pages/admin -g '*.php' || true) | wc -l && (rg -n 'ApiResponse::readJsonBody\\(' pages/api/v1 -g '*.php' || true) | wc -l",
"result": "pass"
},
{
"cmd": "docker compose exec php composer cs:check",
"result": "pass"
},
{
"cmd": "Manual browser smoke (QG-005) for /login, /password/forgot->verify->reset, /verify-email, /auth/microsoft/start+callback, devtools console",
"result": "pass"
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "Exit code 0. PHPUnit full: 487 tests, 11030 assertions, Warnings 1, Deprecations 1 (non-blocking)."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "Exit code 0. PHPStan: [OK] No errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Exit code 0. CoreStarterkitContractTest: 11 tests, 6167 assertions."
},
{
"gate_id": "QG-004",
"result": "pass",
"notes": "Alle strukturellen rg-check Zaehler sind 0 (8/8 checks gruen)."
},
{
"gate_id": "QG-005",
"result": "pass",
"notes": "Manueller Browser-Smoke zuvor erfolgreich bestaetigt (lokal+SSO+Recovery+Verify+Console)."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "Exit code 0. composer cs:check: 0 von 518 Dateien fixbar."
}
],
"test_results": [
{
"name": "MicrosoftOidcServiceTest",
"result": "pass",
"notes": "15 tests, 61 assertions; inkl. neuer callback regression email_domain_not_allowed."
},
{
"name": "AuthLoginFrontendCleanupContractTest",
"result": "pass",
"notes": "3 tests, 15 assertions; prueft CSS cleanup + JS duplicate-binding guards."
},
{
"name": "PasswordResetServiceTest + EmailVerificationServiceTest",
"result": "pass",
"notes": "27 tests, 81 assertions."
},
{
"name": "AuthLoginAccessibilityContractTest + AuthRegisterSecurityContractTest",
"result": "pass",
"notes": "6 tests, 105 assertions."
},
{
"name": "CoreStarterkitContractTest",
"result": "pass",
"notes": "11 tests, 6167 assertions."
},
{
"name": "PHPUnit Full",
"result": "pass",
"notes": "487 tests, 11030 assertions, warnings/deprecations vorhanden aber kein failure."
},
{
"name": "PHPStan",
"result": "pass",
"notes": "No errors."
},
{
"name": "PHP CS Check",
"result": "pass",
"notes": "0/518 files fixable."
},
{
"name": "Manual Browser Smoke (QG-005)",
"result": "pass",
"notes": "Manueller Smoke erfolgreich bestaetigt."
}
],
"open_items": []
}