diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/.gitkeep b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/execution-report.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/execution-report.json new file mode 100644 index 0000000..5f48d86 --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/execution-report.json @@ -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": [] +} diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/finalize.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/finalize.json new file mode 100644 index 0000000..393db80 --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/finalize.json @@ -0,0 +1,10 @@ +{ + "task_id": "LOGIN-AUTH-QUALITY-CHECK-001", + "ready_to_finalize": true, + "guard_review": "pass", + "acceptance_review": "pass", + "ci_status": "pass", + "final_action": "commit", + "commit_message": "feat(auth): finalize login auth quality check hardening and coverage", + "notes": "review-guards, review-acceptance und execution-report sind jeweils PASS; QG-001 bis QG-006 sind PASS." +} diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json new file mode 100644 index 0000000..642ee90 --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json @@ -0,0 +1,231 @@ +{ + "task_id": "LOGIN-AUTH-QUALITY-CHECK-001", + "scope_ref": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json", + "flows": [ + { + "flow": "local login", + "files": [ + "pages/auth/login().php", + "pages/auth/login(login).phtml", + "web/js/app-login-init.js", + "web/js/components/app-password-toggle.js", + "web/js/components/app-password-hints.js", + "web/css/pages/app-login.css" + ], + "guard_ids": [ + "GR-CORE-003", + "GR-CORE-005", + "GR-CORE-006", + "GR-SEC-001", + "GR-UI-009", + "GR-UI-010", + "GR-UI-011", + "GR-UI-012", + "GR-UI-013", + "GR-UI-015" + ], + "findings": [ + { + "id": "F-LOGIN-001", + "severity": "medium", + "status": "resolved", + "summary": "Password toggle lacked duplicate-init guard and keyboard focus resilience evidence.", + "evidence": "web/js/components/app-password-toggle.js now has passwordToggleBound guard; tests/Architecture/AuthLoginFrontendCleanupContractTest.php validates guard presence." + }, + { + "id": "F-LOGIN-002", + "severity": "low", + "status": "resolved", + "summary": "Unused legacy login CSS selector remained in app-login.css.", + "evidence": "Selector .back_to_login removed; tests/Architecture/AuthLoginFrontendCleanupContractTest.php asserts absence." + } + ] + }, + { + "flow": "register", + "files": [ + "pages/auth/register().php", + "pages/auth/register(login).phtml" + ], + "guard_ids": [ + "GR-CORE-003", + "GR-CORE-012", + "GR-SEC-001", + "GR-UI-009", + "GR-UI-010" + ], + "findings": [ + { + "id": "F-REGISTER-001", + "severity": "high", + "status": "resolved", + "summary": "Register POST accepted payload without upfront CSRF verification.", + "evidence": "pages/auth/register().php now enforces Session::checkCsrfToken() before payload handling; tests/Architecture/AuthRegisterSecurityContractTest.php green." + } + ] + }, + { + "flow": "forgot/verify/reset", + "files": [ + "pages/auth/forgot().php", + "pages/auth/verify().php", + "pages/auth/reset().php", + "lib/Service/Auth/PasswordResetService.php", + "pages/auth/forgot(login).phtml", + "pages/auth/verify(login).phtml", + "pages/auth/reset(login).phtml" + ], + "guard_ids": [ + "GR-SEC-001", + "GR-SEC-002", + "GR-TEST-001", + "GR-TEST-002", + "GR-UI-009" + ], + "findings": [ + { + "id": "F-RESET-001", + "severity": "medium", + "status": "resolved", + "summary": "Edge-case unit regression coverage for reset lifecycle was incomplete.", + "evidence": "tests/Service/Auth/PasswordResetServiceTest.php adds requestReset/verifyCode/resetPassword edge and failure paths." + } + ] + }, + { + "flow": "verify-email", + "files": [ + "pages/auth/verify-email().php", + "pages/auth/verify-email(login).phtml", + "lib/Service/Auth/EmailVerificationService.php" + ], + "guard_ids": [ + "GR-SEC-001", + "GR-SEC-002", + "GR-TEST-001", + "GR-TEST-002", + "GR-UI-009" + ], + "findings": [ + { + "id": "F-VERIFY-001", + "severity": "medium", + "status": "resolved", + "summary": "Automated coverage for verify-email resend/attempt-limit branches was incomplete.", + "evidence": "tests/Service/Auth/EmailVerificationServiceTest.php covers send/verify/resend including already_verified and attempt limit." + } + ] + }, + { + "flow": "microsoft start", + "files": [ + "pages/auth/microsoft/start().php", + "lib/Service/Auth/TenantSsoService.php", + "lib/Service/Auth/MicrosoftOidcService.php" + ], + "guard_ids": [ + "GR-CORE-005", + "GR-CORE-006", + "GR-SEC-002", + "GR-TEST-001", + "GR-TEST-002" + ], + "findings": [ + { + "id": "F-MSSO-START-001", + "severity": "none", + "status": "none", + "summary": "No open finding.", + "evidence": "tests/Service/Auth/MicrosoftOidcServiceTest.php already covered startAuthorization negative and PKCE happy paths." + } + ] + }, + { + "flow": "microsoft callback", + "files": [ + "pages/auth/microsoft/callback().php", + "lib/Service/Auth/MicrosoftOidcService.php", + "lib/Service/Auth/SsoUserLinkService.php", + "lib/Service/Auth/TenantSsoService.php" + ], + "guard_ids": [ + "GR-CORE-005", + "GR-CORE-006", + "GR-SEC-002", + "GR-TEST-001", + "GR-TEST-002" + ], + "findings": [ + { + "id": "F-MSSO-CALLBACK-001", + "severity": "medium", + "status": "resolved", + "summary": "Missing explicit automated regression case for domain-policy rejection in callback.", + "evidence": "tests/Service/Auth/MicrosoftOidcServiceTest.php now includes testHandleCallbackReturnsEmailDomainNotAllowedWhenTenantDomainPolicyRejectsUser." + } + ] + }, + { + "flow": "api auth login", + "files": [ + "pages/api/v1/auth/login().php" + ], + "guard_ids": [ + "GR-CORE-004", + "GR-CORE-005", + "GR-SEC-002", + "GR-SEC-003", + "GR-CORE-006" + ], + "findings": [ + { + "id": "F-API-LOGIN-001", + "severity": "none", + "status": "none", + "summary": "No open finding.", + "evidence": "QG-004 structural checks remain 0 violations; no direct readJsonBody/superglobal drift introduced." + } + ] + }, + { + "flow": "api me password", + "files": [ + "pages/api/v1/me/password().php" + ], + "guard_ids": [ + "GR-CORE-005", + "GR-SEC-002", + "GR-SEC-003", + "GR-TEST-001" + ], + "findings": [ + { + "id": "F-API-ME-PASSWORD-001", + "severity": "none", + "status": "none", + "summary": "No open finding.", + "evidence": "No API behavior change in this run; quality gates and architecture contracts stay green." + } + ] + } + ], + "priority_summary": { + "critical": { + "open": 0, + "resolved": 0 + }, + "high": { + "open": 0, + "resolved": 1 + }, + "medium": { + "open": 0, + "resolved": 4 + }, + "low": { + "open": 0, + "resolved": 1 + } + }, + "notes": "Alle kritischen/hohen Findings sind geschlossen; keine offenen critical/high/medium Befunde im In-Scope-Auth-Bereich." +} diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json new file mode 100644 index 0000000..3b0150a --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json @@ -0,0 +1,249 @@ +{ + "task_id": "LOGIN-AUTH-QUALITY-CHECK-001", + "summary": "Ganzheitlicher Quality-Check fuer Auth/Login/Password (lokal + Microsoft SSO) mit Guard- und Best-Practice-Review ueber PHP und Frontend, gezielten Korrekturen bei Verstoessen (inkl. unnoetigem CSS/JS), sowie Ergaenzung und Absicherung durch reproduzierbare Tests und Quality Gates.", + "assumptions": [ + "Scope umfasst lokalen Login/Password-Flow und Microsoft-SSO-Start/Callback inklusive tenantabhaengiger Login-Methoden.", + "Umsetzungstiefe ist 'gezielte Fixes', kein breiter Architektur-Refactor.", + "Wenn API-Verhalten nicht geaendert wird, ist keine OpenAPI-Aenderung noetig.", + "Alle neu eingefuehrten sichtbaren Texte werden im selben Merge in de+en gepflegt." + ], + "scope": { + "in": [ + "pages/auth/login().php, register().php, forgot().php, verify().php, reset().php, verify-email().php", + "pages/auth/login(login).phtml, register(login).phtml, forgot(login).phtml, verify(login).phtml, reset(login).phtml, verify-email(login).phtml", + "pages/auth/microsoft/start().php und pages/auth/microsoft/callback().php", + "pages/api/v1/auth/login().php und pages/api/v1/me/password().php", + "lib/Service/Auth/AuthService.php, RememberMeService.php, PasswordResetService.php, EmailVerificationService.php, MicrosoftOidcService.php, TenantSsoService.php, SsoUserLinkService.php", + "templates/login.phtml und templates/partials/auth-help-links.phtml (plus ggf. auth-bezogene Partials)", + "web/js/app-login-init.js, web/js/components/app-password-toggle.js, web/js/components/app-password-hints.js", + "web/css/pages/app-login.css", + "tests/Service/Auth/*, tests/Http/ApiAuthTest.php, tests/Architecture/AuthLoginAccessibilityContractTest.php, tests/Architecture/AuthHelpLinksContractTest.php", + "i18n/default_de.json und i18n/default_en.json fuer neue Keys" + ], + "out": [ + "Neues Auth-Feature-Design oder Produktneuentwicklung", + "Breite Refactors ausserhalb Auth/Login/Password/SSO", + "DB-Schema-Migrationen", + "Nicht-authentifizierungsbezogene Admin-Module" + ] + }, + "guardrails": { + "required_guard_ids": [ + "GR-CORE-001", + "GR-CORE-002", + "GR-CORE-003", + "GR-CORE-004", + "GR-CORE-005", + "GR-CORE-006", + "GR-CORE-008", + "GR-CORE-012", + "GR-SEC-001", + "GR-SEC-002", + "GR-SEC-003", + "GR-SEC-004", + "GR-SEC-005", + "GR-UI-009", + "GR-UI-010", + "GR-UI-011", + "GR-UI-012", + "GR-UI-013", + "GR-UI-015", + "GR-TEST-001", + "GR-TEST-002", + "GR-LANG-002" + ], + "required_quality_gate_ids": [ + "QG-001", + "QG-002", + "QG-003", + "QG-004", + "QG-005", + "QG-006" + ] + }, + "success_criteria": [ + { + "id": "SC-001", + "criterion": "Eine vollstaendige Guard-Matrix fuer den Auth/Login/Password-Scope liegt vor, mit Datei-/Flow-Bezug und priorisierten Findings (kritisch/hoch/mittel)." + }, + { + "id": "SC-002", + "criterion": "Alle kritischen und hohen Guard-Verstoesse im Scope sind durch gezielte Code-Anpassungen behoben oder mit begruendeter, nachvollziehbarer Ausnahme dokumentiert." + }, + { + "id": "SC-003", + "criterion": "Frontend-Review entfernt oder konsolidiert unnoetige/duplizierte Login-CSS- und Login-JS-Logik, ohne A11y- oder UX-Regression." + }, + { + "id": "SC-004", + "criterion": "Serverseitige Sicherheitsinvarianten bleiben fuer alle betroffenen Auth-Endpunkte intakt (AuthZ, Tenant-Scope, CSRF auf POST, keine sensiblen Leaks)." + }, + { + "id": "SC-005", + "criterion": "Automatisierte Tests sind erweitert, sodass Login/Password-Edge-Cases und Regressionen (inkl. Reset/Verify sowie SSO-Pfade) reproduzierbar abgedeckt sind." + }, + { + "id": "SC-006", + "criterion": "Alle Pflicht-Gates QG-001 bis QG-006 laufen mit PASS und nachvollziehbarer Evidenz." + }, + { + "id": "SC-007", + "criterion": "Manuelle End-to-End-Smokes fuer lokale Login- und SSO-Kernfluesse sind erfolgreich, inklusive Browser-Console ohne neue JS-Fehler." + } + ], + "implementation_steps": [ + { + "id": "S1", + "title": "Guard-Matrix und Baseline erstellen", + "description": "Dokumentiere pro Auth/Login/Password-Flow die relevanten Guards aus Katalog und ordne sie konkreten Dateien/Codepfaden zu; erfasse bestehende Testabdeckung und initiale Gap-Liste.", + "guard_refs": [ + "GR-CORE-001", + "GR-TEST-001" + ] + }, + { + "id": "S2", + "title": "PHP Page/API Boundary-Review durchfuehren", + "description": "Pruefe pages/auth/* und pages/api/v1/auth|me auf requestInput-Vertrag, Superglobal-Vermeidung, CSRF-Reihenfolge, API-Fehlerkonsistenz, PRG-Verhalten und serverseitige Sicherheitspruefungen.", + "guard_refs": [ + "GR-CORE-003", + "GR-CORE-004", + "GR-CORE-008", + "GR-CORE-012", + "GR-SEC-001", + "GR-CORE-005", + "GR-CORE-006" + ] + }, + { + "id": "S3", + "title": "Service-Layer Security- und Testability-Review", + "description": "Pruefe AuthService/RememberMe/PasswordReset/EmailVerification/MicrosoftOIDC/TenantSSO auf Auth- und Tenant-Invarianten, PII-Redaktion, SQL-/Crypto-Nutzung sowie unit-testbare Konstruktion via Dependency Injection.", + "guard_refs": [ + "GR-CORE-001", + "GR-SEC-002", + "GR-SEC-003", + "GR-SEC-005", + "GR-TEST-002" + ] + }, + { + "id": "S4", + "title": "Frontend/UI-UX Quality-Review mit CSS/JS-Cleanup", + "description": "Pruefe Login-Templates, Login-CSS und Login-JS auf A11y-Basics, i18n-Konformitaet, Semantik, UI-State-Vollstaendigkeit sowie JS/CSS-Reuse-first; entferne unnoetige/duplizierte Regeln oder Logik gezielt.", + "guard_refs": [ + "GR-UI-009", + "GR-UI-010", + "GR-UI-011", + "GR-UI-012", + "GR-UI-013", + "GR-UI-015", + "GR-SEC-004" + ] + }, + { + "id": "S5", + "title": "Gezielte Korrekturen umsetzen", + "description": "Setze nur die priorisierten High-Impact-Fixes um (PHP + Frontend), ohne Scope-Ausweitung; bei UI-Textaenderungen i18n-Keys in de/en im selben Schritt nachziehen.", + "guard_refs": [ + "GR-TEST-002", + "GR-UI-010", + "GR-UI-015", + "GR-LANG-002" + ] + }, + { + "id": "S6", + "title": "Testabdeckung gezielt erweitern", + "description": "Ergaenze/erweitere PHPUnit-Tests fuer unterabgedeckte Auth/Login/Password-Pfade (v. a. PasswordResetService, EmailVerificationService, Login/SSO-Edge-Cases) sowie passende Architektur-Contracts fuer Markup-/A11y-Invarianten.", + "guard_refs": [ + "GR-TEST-001", + "GR-TEST-002", + "GR-UI-009", + "GR-SEC-001" + ] + }, + { + "id": "S7", + "title": "Quality Gates und Smokes ausfuehren", + "description": "Fuehre QG-001 bis QG-006 aus, inklusive struktureller rg-Checks und JS-Smoke; dokumentiere pro Gate PASS/FAIL/Blocker mit kurzer Evidenz.", + "guard_refs": [ + "GR-LANG-002", + "GR-TEST-001", + "GR-UI-011", + "GR-UI-012" + ] + }, + { + "id": "S8", + "title": "Acceptance gegen SC-IDs abschliessen", + "description": "Mappe jede Success-Criterion-ID auf konkrete Evidenz (Dateien, Tests, Gate-Resultate, Smoke-Ergebnisse) und markiere verbleibende Restrisiken transparent.", + "guard_refs": [ + "GR-TEST-001", + "GR-CORE-005" + ] + } + ], + "tests": [ + "Bestehende Tests: tests/Service/Auth/AuthServiceTest.php und tests/Service/Auth/RememberMeServiceTest.php gezielt erweitern (fehlende Login-/Tenant-/SSO-Edge-Cases).", + "Neue Tests: tests/Service/Auth/PasswordResetServiceTest.php (requestReset, verifyCode, resetPassword inkl. expiry/attempts/used).", + "Neue Tests: tests/Service/Auth/EmailVerificationServiceTest.php (sendVerification, verifyCode, resendVerification inkl. already_verified/attempt limits).", + "Bestehende Architekturtests: tests/Architecture/AuthLoginAccessibilityContractTest.php und tests/Architecture/AuthHelpLinksContractTest.php bei Bedarf erweitern.", + "API-bezogene Regressionen: gezielte Ergaenzung in tests/Http/ApiAuthTest.php oder neuer fokussierter Auth-API-Test fuer Login-/Password-Randfaelle.", + "QG-001: docker compose exec php vendor/bin/phpunit", + "QG-002: docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress", + "QG-003: docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php", + "QG-004: strukturelle rg-Checks gemaess agent-system/checks/quality-gates.json (Erwartung: 0 Verstosse)", + "QG-005: Browser-Smoke fuer Login, Forgot/Verify/Reset, Microsoft Start/Callback; DevTools Console ohne neue Errors", + "QG-006: docker compose exec php composer cs:check" + ], + "acceptance_checks": [ + "SC-001: Guard-Matrix enthaelt jeden In-Scope-Flow und referenziert konkrete Dateien plus Priorisierung der Findings.", + "SC-002: Kein offener kritischer/hoher Guard-Verstoss im Scope ohne dokumentierte und akzeptierte Ausnahme.", + "SC-003: Login-Frontend hat keine neu eingefuehrte A11y-Regressionsspur und keine unnoetige duplizierte CSS/JS-Logik in den geprueften Bereichen.", + "SC-004: AuthZ-, Tenant- und CSRF-Invarianten sind fuer alle betroffenen POST- und Login-Endpunkte nachweisbar intakt.", + "SC-005: Neue/erweiterte Tests schlagen bei kontrollierter Regelverletzung fehl und laufen im Zielzustand stabil gruen.", + "SC-006: QG-001 bis QG-006 sind mit PASS protokolliert.", + "SC-007: Manueller Smoke bestaetigt erfolgreiche Kernfluesse (lokal + SSO + Logout) ohne neue Browser-Console-Fehler." + ], + "ux_notes": { + "affected_patterns": [ + "Auth login card/layout (templates/login.phtml + pages/auth/*(login).phtml)", + "Tenant selection pattern (login-tenant-fieldset, login-tenant-choice-grid)", + "Auth help links partial (templates/partials/auth-help-links.phtml)", + "Password hint/toggle behavior (web/js/components/app-password-hints.js, app-password-toggle.js)", + "Login page styling (web/css/pages/app-login.css)" + ], + "ui_states_required": [ + "loading", + "empty", + "error", + "success" + ], + "a11y_touchpoints": [ + "Email/password/code inputs inklusive aria-invalid und aria-describedby Verknuepfungen", + "Fehler-Notices als assertive live regions mit fokussierbarer Rueckmeldung", + "Tenant-Radioauswahl und Tastatur-Fokuspfad", + "Microsoft Login CTA und lokale Login-Buttons mit semantischer Bedienbarkeit", + "Password toggle control (button semantics, focus-visible, aria-label updates)" + ] + }, + "risks": [ + { + "risk": "Gezielte CSS-Bereinigung kann visuelle Login-Regressionen erzeugen, obwohl funktional korrekt.", + "mitigation": "Nur additive/minimale CSS-Deltas, visuelle Smokes pro Auth-Stage und QG-005 verpflichtend." + }, + { + "risk": "Aenderungen an tenantabhaengiger Login-Methodik koennen lokale oder Microsoft-Login-Pfade unbeabsichtigt blockieren.", + "mitigation": "SSO- und Local-Branches jeweils mit positiven und negativen Tests absichern; End-to-End-Smoke fuer Start/Callback/Login ausfuehren." + }, + { + "risk": "Security-Fixes koennen durch Nebenwirkungen API- oder Session-Verhalten brechen.", + "mitigation": "Invarianten zuerst testen (CSRF/AuthZ/Tenant), danach nur kleine isolierte Codeaenderungen mit Ruecktests." + }, + { + "risk": "Neue Tests werden flakey wegen Session/Global-State-Kopplung.", + "mitigation": "Konsequent Mocking/DI nutzen, globale Abhaengigkeiten kapseln und deterministische Assertions ohne Zeitrennen definieren." + } + ] +} \ No newline at end of file diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-acceptance.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-acceptance.json new file mode 100644 index 0000000..71b53cd --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-acceptance.json @@ -0,0 +1,57 @@ +{ + "task_id": "LOGIN-AUTH-QUALITY-CHECK-001", + "verdict": "pass", + "checked_criterion_ids": [ + "SC-001", + "SC-002", + "SC-003", + "SC-004", + "SC-005", + "SC-006", + "SC-007" + ], + "checks": [ + { + "criterion_id": "SC-001", + "criterion": "Eine vollstaendige Guard-Matrix fuer den Auth/Login/Password-Scope liegt vor, mit Datei-/Flow-Bezug und priorisierten Findings (kritisch/hoch/mittel).", + "result": "pass", + "evidence": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json dokumentiert alle In-Scope-Flows mit Datei-/Guard-Zuordnung; priority_summary weist open=0 fuer critical/high/medium aus (high resolved=1, medium resolved=4)." + }, + { + "criterion_id": "SC-002", + "criterion": "Alle kritischen und hohen Guard-Verstoesse im Scope sind durch gezielte Code-Anpassungen behoben oder mit begruendeter, nachvollziehbarer Ausnahme dokumentiert.", + "result": "pass", + "evidence": "guard-matrix.json zeigt keine offenen critical/high Findings; der High-Fund F-REGISTER-001 ist als resolved dokumentiert und in pages/auth/register().php umgesetzt (CSRF vor Payload), abgesichert durch tests/Architecture/AuthRegisterSecurityContractTest.php (lokal PASS)." + }, + { + "criterion_id": "SC-003", + "criterion": "Frontend-Review entfernt oder konsolidiert unnoetige/duplizierte Login-CSS- und Login-JS-Logik, ohne A11y- oder UX-Regression.", + "result": "pass", + "evidence": "Konkrete Cleanup-Evidence vorhanden: web/css/pages/app-login.css ohne .back_to_login sowie idempotente Duplicate-Binding-Guards in web/js/components/app-password-toggle.js und web/js/components/app-password-hints.js; belegt durch tests/Architecture/AuthLoginFrontendCleanupContractTest.php (lokal PASS, 3 Tests/15 Assertions) und A11y-Contract tests/Architecture/AuthLoginAccessibilityContractTest.php (lokal PASS)." + }, + { + "criterion_id": "SC-004", + "criterion": "Serverseitige Sicherheitsinvarianten bleiben fuer alle betroffenen Auth-Endpunkte intakt (AuthZ, Tenant-Scope, CSRF auf POST, keine sensiblen Leaks).", + "result": "pass", + "evidence": "CSRF-Invariante fuer Register ist codeseitig durchgesetzt und per Contract-Test bestaetigt; QG-004 Structural Checks lokal erneut 8x0 (u.a. keine verbotenen direkten Instanziierungen, keine ApiResponse::readJsonBody-Nutzung in pages/api/v1). execution-report.json guard_evidence markiert GR-CORE-005/006 und GR-SEC-002 als pass ohne offene Punkte." + }, + { + "criterion_id": "SC-005", + "criterion": "Automatisierte Tests sind erweitert, sodass Login/Password-Edge-Cases und Regressionen (inkl. Reset/Verify sowie SSO-Pfade) reproduzierbar abgedeckt sind.", + "result": "pass", + "evidence": "Reset/Verify-Abdeckung: tests/Service/Auth/PasswordResetServiceTest.php und tests/Service/Auth/EmailVerificationServiceTest.php (lokal PASS, zusammen 27 Tests/81 Assertions). SSO-Abdeckung: tests/Service/Auth/MicrosoftOidcServiceTest.php enthaelt Callback-Domain-Policy-Regression (lokal PASS, 15 Tests/61 Assertions)." + }, + { + "criterion_id": "SC-006", + "criterion": "Alle Pflicht-Gates QG-001 bis QG-006 laufen mit PASS und nachvollziehbarer Evidenz.", + "result": "pass", + "evidence": "execution-report.json weist QG-001..QG-006 als pass aus; lokal verifiziert wurden QG-004 (8 strukturelle Checks alle 0) und QG-006 (composer cs:check PASS, 0/518 fixable)." + }, + { + "criterion_id": "SC-007", + "criterion": "Manuelle End-to-End-Smokes fuer lokale Login- und SSO-Kernfluesse sind erfolgreich, inklusive Browser-Console ohne neue JS-Fehler.", + "result": "pass", + "evidence": "execution-report.json dokumentiert QG-005 als pass fuer lokale Login- und SSO-Kernpfade inkl. Console-Check; keine widersprechenden Open Items vorhanden." + } + ] +} diff --git a/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-guards.json b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-guards.json new file mode 100644 index 0000000..fe60ee3 --- /dev/null +++ b/agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/review-guards.json @@ -0,0 +1,37 @@ +{ + "task_id": "LOGIN-AUTH-QUALITY-CHECK-001", + "verdict": "pass", + "checked_guard_ids": [ + "GR-CORE-001", + "GR-CORE-002", + "GR-CORE-003", + "GR-CORE-004", + "GR-CORE-005", + "GR-CORE-006", + "GR-CORE-008", + "GR-CORE-012", + "GR-SEC-001", + "GR-SEC-002", + "GR-SEC-003", + "GR-SEC-004", + "GR-SEC-005", + "GR-UI-009", + "GR-UI-010", + "GR-UI-011", + "GR-UI-012", + "GR-UI-013", + "GR-UI-015", + "GR-TEST-001", + "GR-TEST-002", + "GR-LANG-002" + ], + "checked_quality_gate_ids": [ + "QG-001", + "QG-002", + "QG-003", + "QG-004", + "QG-005", + "QG-006" + ], + "findings": [] +} diff --git a/i18n/default_de.json b/i18n/default_de.json index a1def28..b109928 100644 --- a/i18n/default_de.json +++ b/i18n/default_de.json @@ -52,6 +52,8 @@ "Select your tenant to continue": "Mandant auswählen und weiter", "Choose how you want to sign in": "Anmeldeart auswählen", "Continue": "Weiter", + "Show password": "Passwort anzeigen", + "Hide password": "Passwort verbergen", "Use different email": "Andere E-Mail verwenden", "Back to login": "Zurück zum Login", "Encrypted connection": "Verschlüsselt", diff --git a/i18n/default_en.json b/i18n/default_en.json index ebd56ba..772c9af 100644 --- a/i18n/default_en.json +++ b/i18n/default_en.json @@ -52,6 +52,8 @@ "Select your tenant to continue": "Select your tenant to continue", "Choose how you want to sign in": "Choose how you want to sign in", "Continue": "Continue", + "Show password": "Show password", + "Hide password": "Hide password", "Use different email": "Use different email", "Back to login": "Back to login", "Encrypted connection": "Encrypted", diff --git a/pages/auth/register().php b/pages/auth/register().php index 35b0220..aae299c 100644 --- a/pages/auth/register().php +++ b/pages/auth/register().php @@ -4,6 +4,7 @@ use MintyPHP\Http\SessionStoreInterface; use MintyPHP\Router; use MintyPHP\Service\Auth\AuthServicesFactory; use MintyPHP\Service\User\UserServicesFactory; +use MintyPHP\Session; use MintyPHP\Support\Flash; $sessionStore = app(SessionStoreInterface::class); @@ -15,29 +16,35 @@ if (!allowRegistration()) { Router::redirect('login'); } -if ($request->hasBody('email')) { - $authService = (app(AuthServicesFactory::class))->createAuthService(); - $firstName = $request->bodyString('first_name'); - $lastName = $request->bodyString('last_name'); - $email = $request->bodyString('email'); - $password = (string) $request->body('password', ''); - $password2 = (string) $request->body('password2', ''); +if ($request->isMethod('POST')) { + if (!Session::checkCsrfToken()) { + $error = t('Form expired, please try again'); + } elseif ($request->hasBody('email')) { + $authService = (app(AuthServicesFactory::class))->createAuthService(); + $firstName = $request->bodyString('first_name'); + $lastName = $request->bodyString('last_name'); + $email = $request->bodyString('email'); + $password = (string) $request->body('password', ''); + $password2 = (string) $request->body('password2', ''); - $result = $authService->register([ - 'first_name' => $firstName, - 'last_name' => $lastName, - 'email' => $email, - 'password' => $password, - 'password2' => $password2, - ]); + $result = $authService->register([ + 'first_name' => $firstName, + 'last_name' => $lastName, + 'email' => $email, + 'password' => $password, + 'password2' => $password2, + ]); - if (!($result['ok'] ?? false)) { - $error = $result['error'] ?? t('User can not be registered'); + if (!($result['ok'] ?? false)) { + $error = $result['error'] ?? t('User can not be registered'); + } else { + // Store email in session for verify-email page + $sessionStore->set('email_verification_email', $result['email'] ?? $email); + Flash::success(t('Registration successful! Please check your email for the verification code.'), 'verify-email', 'registration_success'); + Router::redirect('verify-email'); + } } else { - // Store email in session for verify-email page - $sessionStore->set('email_verification_email', $result['email'] ?? $email); - Flash::success(t('Registration successful! Please check your email for the verification code.'), 'verify-email', 'registration_success'); - Router::redirect('verify-email'); + $error = t('User can not be registered'); } } diff --git a/templates/login.phtml b/templates/login.phtml index 4a06ddc..3356437 100644 --- a/templates/login.phtml +++ b/templates/login.phtml @@ -15,7 +15,11 @@ if ($bufferTitle !== '') { ?> -style="" > +style="" >