This commit is contained in:
2026-07-12 19:21:35 +02:00
parent f2d944b117
commit 41cb412cb0
49 changed files with 3170 additions and 52 deletions
@@ -0,0 +1,168 @@
---
plan_id: 20260712-1647-zapamietywanie-logowania
title: Zapamiętywanie logowania przez 30 dni
storage: plan-first
legacy_phase: null
created: 2026-07-12T16:47:19+02:00
status: planned
type: execute
autonomous: true
delegation: auto
files_modified:
- app/Database/Migrations/2026-07-12-160001_CreateRememberTokens.php
- app/Models/RememberTokenModel.php
- app/Controllers/Auth.php
- app/Filters/AuthFilter.php
- app/Views/auth/login.php
- tests/unit/RememberTokenModelTest.php
quality_radar: ok
---
<objective>
## Cel
Umożliwić użytkownikowi zaznaczenie opcji „Zapamiętaj mnie” podczas logowania i utrzymać dostęp przez 30 dni niezależnie na wielu komputerach.
## Powód
Obecna sesja wygasa po 2 godzinach i nie przeżywa zamknięcia przeglądarki. Osobny token dla każdego urządzenia zachowuje równoległe logowania bez współdzielenia jednego identyfikatora sesji.
## Wynik
Checkbox w formularzu, trwałe tokeny przechowywane w bazie wyłącznie jako skróty, automatyczne odtworzenie sesji oraz unieważnienie tokenu bieżącego urządzenia przy wylogowaniu.
</objective>
<context>
## Dokumenty projektu
@.paul/PROJECT.md
@.paul/STATE.md
@.paul/config.md
@.paul/codebase/impact_map.md
@.paul/codebase/quality_risks.md
## Pliki źródłowe
@app/Controllers/Auth.php
@app/Filters/AuthFilter.php
@app/Views/auth/login.php
@app/Config/Routes.php
@app/Config/Cookie.php
@app/Config/Session.php
</context>
<clarifications>
- „Wiele komputerów jednocześnie” oznacza osobny aktywny token na każdym urządzeniu; kolejne logowanie nie usuwa wcześniejszych tokenów.
- Wylogowanie usuwa token tylko z bieżącego urządzenia. Globalne „wyloguj wszędzie” jest poza zakresem.
</clarifications>
<impact_scan>
## Quality Radar
**Status:** ok
**Narzędzia:** codebase-memory-mcp 0.6.1; jscpd i ast-grep wyłączone polityką projektu.
## Dotknięte obszary
- Logowanie: `Auth::login`, `Auth::logout`, `app/Views/auth/login.php`.
- Ochrona tras: `AuthFilter::before` odtwarza zwykłą sesję po poprawnym tokenie.
- Dane: nowa tabela tokenów z wieloma rekordami; konto nadal pochodzi z `.env`.
- Wdrożenie: nowa migracja wymaga ręcznego uruchomienia na hostido.
## Ryzyka duplikacji / wartości hardcoded
- Czas 30 dni ma jedno źródło prawdy w `RememberTokenModel`.
- Token cookie nie może być zapisany jawnie w bazie; tabela przechowuje selektor i hash walidatora.
- Nie wydłużać globalnie `Session::$expiration`, bo nie zapewnia niezależnych urządzeń.
## Jawne odroczenia
- Panel urządzeń i „wyloguj wszędzie” — dodać dopiero na żądanie.
- Harmonogram czyszczenia — przy tej skali wygasłe tokeny wystarczy usuwać podczas użycia mechanizmu.
</impact_scan>
<acceptance_criteria>
## AC-1: Logowanie bez zapamiętania
```gherkin
Given użytkownik nie zaznaczył Zapamiętaj mnie
When poda poprawny email i hasło
Then otrzymuje wyłącznie obecną sesję bez trwałego cookie logowania
```
## AC-2: Logowanie zapamiętane przez 30 dni
```gherkin
Given użytkownik zaznaczył Zapamiętaj mnie
When poda poprawny email i hasło
Then aplikacja zapisuje bezpieczny token i cookie ważne 30 dni
And po utracie zwykłej sesji poprawny token odtwarza sesję i wpuszcza na chronioną trasę
```
## AC-3: Wiele urządzeń
```gherkin
Given użytkownik zaznaczył opcję na dwóch urządzeniach
When oba urządzenia otwierają chronioną trasę bez zwykłej sesji
Then oba niezależne tokeny pozostają poprawne
And wylogowanie na jednym urządzeniu nie unieważnia drugiego
```
## AC-4: Odrzucenie nieważnego tokenu
```gherkin
Given cookie jest zmienione, wygasłe lub nie ma odpowiadającego rekordu
When użytkownik otwiera chronioną trasę
Then cookie i wadliwy rekord są usuwane w możliwym zakresie
And użytkownik trafia na `/login`
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Dodać bezpieczne tokeny per urządzenie</name>
<files>app/Database/Migrations/2026-07-12-160001_CreateRememberTokens.php, app/Models/RememberTokenModel.php</files>
<action>Utworzyć tabelę z unikalnym selektorem, hashem walidatora, `expires_at` i timestampami. W modelu umieścić stałą 30 dni oraz operacje wystawienia tokenu przez `random_bytes`, weryfikacji przez `hash_equals`, usunięcia bieżącego tokenu i wygasłych rekordów. Każde logowanie dopisuje rekord.</action>
<verify>php -l app/Database/Migrations/2026-07-12-160001_CreateRememberTokens.php; php -l app/Models/RememberTokenModel.php</verify>
<done>AC-2, AC-3 i AC-4 mają trwały mechanizm danych bez jawnych sekretów w bazie.</done>
</task>
<task type="auto">
<name>Task 2: Podłączyć checkbox, odtworzenie sesji i wylogowanie</name>
<files>app/Controllers/Auth.php, app/Filters/AuthFilter.php, app/Views/auth/login.php</files>
<action>Dodać checkbox `remember`. Po poprawnym logowaniu tworzyć cookie tylko po zaznaczeniu. W filtrze zachować obecną ścieżkę sesji, a przy jej braku zweryfikować cookie i ustawić `logged_in`/`user_email`. Cookie: HttpOnly, SameSite=Lax, 30 dni, Secure w produkcji. `logout()` usuwa token bieżącego cookie, cookie i sesję.</action>
<verify>php -l app/Controllers/Auth.php; php -l app/Filters/AuthFilter.php; php -l app/Views/auth/login.php</verify>
<done>AC-1..AC-4 są obsłużone w przepływie HTTP.</done>
</task>
<task type="auto">
<name>Task 3: Sprawdzić bezpieczeństwo i równoległe urządzenia</name>
<files>tests/unit/RememberTokenModelTest.php</files>
<action>Dodać jeden mały test obejmujący dwa jednocześnie poprawne tokeny, odrzucenie zmienionego walidatora oraz unieważnienie jednego bez wpływu na drugi. Wykonać UAT w dwóch profilach przeglądarki.</action>
<verify>vendor/bin/phpunit tests/unit/RememberTokenModelTest.php; ręcznie: dwa profile przeglądarki zgodnie z AC-1..AC-4</verify>
<done>AC-1..AC-4 mają automatyczną kontrolę tokenów i potwierdzenie przepływu przeglądarkowego.</done>
</task>
</tasks>
<boundaries>
## Nie zmieniać
- Hasło i email nadal pochodzą z `.env`; nie tworzyć systemu wielu kont.
- Nie zmieniać czasu zwykłej sesji ani chronionych tras.
- Nie dotykać modułów finansów, inwestycji i paragonów.
## Ograniczenia zakresu
- Bez nowej zależności, panelu urządzeń, resetu hasła i „wyloguj wszędzie”.
- Bez surowego tokenu lub hasła w bazie i logach.
</boundaries>
<verification>
- [ ] `php -l` przechodzi dla zmienionych plików PHP.
- [ ] `vendor/bin/phpunit tests/unit/RememberTokenModelTest.php` przechodzi w środowisku z `vendor/`.
- [ ] UAT potwierdza zwykłe logowanie, 30-dniowe cookie, dwa profile, wylogowanie jednego profilu i odrzucenie zmienionego cookie.
- [ ] Cookie ma HttpOnly, SameSite=Lax, właściwy termin i Secure na produkcji.
- [ ] Migracja jest zastosowana ręcznie na zdalnej bazie przed wdrożeniem kodu.
- [ ] Ryzyka Quality Radar są obsłużone lub jawnie odroczone.
</verification>
<success_criteria>
- [ ] Wszystkie AC przechodzą.
- [ ] Weryfikacja jest kompletna.
- [ ] Dwa urządzenia pozostają zalogowane niezależnie przez maksymalnie 30 dni.
- [ ] Baza nie zawiera surowych tokenów.
</success_criteria>
<output>
SUMMARY.md path: `.paul/plans/20260712-1647-zapamietywanie-logowania/SUMMARY.md`
</output>
@@ -0,0 +1,75 @@
---
plan_id: 20260712-1647-zapamietywanie-logowania
title: Zapamiętywanie logowania przez 30 dni
completed: 2026-07-12T17:31:28+02:00
storage: plan-first
quality_radar: degraded
---
# Summary: Zapamiętywanie logowania przez 30 dni
## Objective
Dodano opcjonalne logowanie zapamiętane przez 30 dni z niezależnym tokenem dla każdego komputera.
## What Was Built
| Area | Result |
|------|--------|
| Dane | Tabela `remember_tokens` z selektorem, hashem walidatora i terminem ważności; tabela utworzona również na zdalnej bazie. |
| Logowanie | Checkbox `remember` wystawia 30-dniowe cookie tylko po zaznaczeniu. |
| Ochrona tras | `AuthFilter` odtwarza zwykłą sesję z poprawnego tokenu. |
| Wylogowanie | Usuwa token wyłącznie bieżącego urządzenia, cookie i sesję. |
| Test | Dodano jeden test dwóch tokenów, manipulacji walidatorem i selektywnego unieważnienia. |
## Files Modified
- `app/Database/Migrations/2026-07-12-160001_CreateRememberTokens.php` — schemat tokenów.
- `app/Models/RememberTokenModel.php` — wystawianie, weryfikacja i usuwanie tokenów.
- `app/Controllers/Auth.php` — wystawianie cookie i selektywne wylogowanie.
- `app/Filters/AuthFilter.php` — odtworzenie sesji.
- `app/Views/auth/login.php` — checkbox „Zapamiętaj mnie”.
- `tests/unit/RememberTokenModelTest.php` — kontrola logiki wielu urządzeń.
## Acceptance Criteria Results
| Criterion | Status | Evidence |
|-----------|--------|----------|
| AC-1 | Pass | Bez checkboxa `Auth::login` nie wystawia trwałego cookie. |
| AC-2 | Partial | Kod, cookie 30 dni i zdalna tabela są gotowe; brak potwierdzonego końcowego UAT po migracji. |
| AC-3 | Partial | Model dopisuje niezależne rekordy, a logout usuwa bieżący selektor; test nie został uruchomiony lokalnie. |
| AC-4 | Partial | Filtr odrzuca zły/wygasły token i usuwa cookie; test nie został uruchomiony lokalnie. |
## Verification Results
| Check | Result | Notes |
|-------|--------|-------|
| `php -l` dla 6 plików | Pass | Brak błędów składni. |
| `vendor/bin/phpunit tests/unit/RememberTokenModelTest.php` | Skipped | Lokalnie brak `vendor/` i `vendor/bin/phpunit`. |
| Zdalny `remember_tokens` | Pass | Potwierdzono 6 kolumn i 3 indeksy. |
| UAT w dwóch profilach | Skipped | Niepotwierdzony przez użytkownika po aktualizacji bazy. |
## Quality Radar Results
**Status:** degraded
- New risks: wdrożenie kodu przed migracją powoduje błąd DB przy użyciu tokenu.
- Resolved risks: zdalna tabela została utworzona; surowy walidator nie trafia do bazy.
- Deferred risks: test PHPUnit i UAT dwóch profili — środowisko lokalne nie ma `vendor/`, UAT wymaga potwierdzenia przeglądarkowego.
- Raw outputs: `.paul/codebase/radar/codebase-memory-post-apply.txt`.
## Deviations
- PHPUnit nie został uruchomiony lokalnie; wykonano lint pliku testowego.
- UAT w dwóch profilach nie został potwierdzony przed UNIFY.
- Legacy `ROADMAP.md` pominięto w trybie plan-first. Commit fazowy pominięto zgodnie z `preferences.auto_commit: false` oraz z powodu licznych niezwiązanych zmian w working tree.
## Key Decisions / Patterns
- Osobny token per urządzenie zamiast wydłużania globalnej sesji.
- W bazie przechowywany jest wyłącznie hash walidatora.
- Wylogowanie nie usuwa tokenów innych urządzeń.
## Follow-up
- Jednorazowo potwierdzić logowanie w dwóch profilach, zamknięcie sesji i wylogowanie tylko jednego profilu.