Folge von #177 (User Management). Kleinstes der fünf Folge-Issues und ohne Abhängigkeit zu
#179/#180 baubar.
Stand heute
Die drei Konten aus #177 sind mit Platzhaltern angelegt: admin/admin und user/user
(auf ausdrücklichen Wunsch), dazu e2e mit einem zufälligen Passwort in data/e2e-password.txt
(git-ignoriert). Ein Passwort ändern kann heute nur, wer auf den Server kommt: make-user.php <name> <pw> setzt für einen bestehenden Namen einfach den neuen Hash.
Eingeordnet: Der User hat am 2026-09-20 gesagt, dass ihm die Platzhalter erstmal egal sind, auf
dem Server läuft nichts Produktives. Das Issue bleibt trotzdem offen — nur eben nicht als
Notfall.
Was gebaut werden soll
Server — POST ?r=auth/password{currentPassword, newPassword} für das eigene Konto:
Das alte Passwort wird verlangt und geprüft, auch wenn eine Session da ist — sonst reicht ein
fremder offener Tab, um ein Konto zu übernehmen.
Mindestlänge serverseitig prüfen, dieselbe Regel wie in #178.
UserStore::put() kann das Ersetzen schon, es braucht also nur die Route und die Prüfung.
Andere Sessions beenden: Nach dem Wechsel sollten alle Logins dieses Kontos ungültig
werden, außer dem gerade benutzten. Dafür fehlt SessionStore ein destroyAllFor(username, außer $token) — dieselbe Ergänzung wie in #179.
Client — ein Dialog "Passwort ändern" mit altem Passwort, neuem und Wiederholung, alle drei
als Bullets (TextField kann das seit #177). Erreichbar aus der Toolbar, neben oder statt dem
Logout-Knopf; siehe Rückfrage 2. Die Regeln (leer, ungleich, zu kurz, neu gleich alt) gehören als
reine Funktion neben loginProblem() in src/editor/auth/loginRules.ts.
Tests
Unit: die Regeln der Maske, HttpAuthStore für "altes Passwort falsch" gegen "Server kaputt".
API direkt gemessen: mit falschem alten Passwort abgelehnt, danach meldet das alte Passwort
nicht mehr an und das neue schon.
e2e: in Modul 45 ändern und mit dem neuen Passwort neu anmelden. Achtung Aufräumen — das
Konto e2e steht im nächsten Lauf sonst mit dem falschen Passwort da (data/e2e-password.txt
bzw. GAME_MAKER_E2E_PASSWORD). Entweder am Ende zurücksetzen oder den Check an einem
Wegwerfkonto fahren; das hängt mit an #178.
Rückfragen
Sollen die Platzhalter beim Bau dieses Issues gleich mit geändert werden, oder bleiben sie
bewusst so, bis du es sagst? Nach deiner Ansage vom 2026-09-20 gehe ich von "bleiben" aus.
Wo soll der Einstieg sitzen? In der Toolbar ist rechts nur der Logout-Knopf; der
Benutzername steht seit #177 im Fenstertitel, weil die Leiste bei 1280 px sonst zu breit wird.
Vorschlag: aus dem Logout-Knopf ein kleines Konto-Menü machen (Name, "Passwort ändern",
"Logout") — das kostet keinen zusätzlichen Platz und hat gleich einen Ort für spätere
Kontosachen.
Soll das Ändern die anderen Sitzungen wirklich abmelden? Das ist das saubere Verhalten,
trifft hier aber vor allem dich selbst in einem zweiten Tab.
Folge von #177 (User Management). Kleinstes der fünf Folge-Issues und ohne Abhängigkeit zu
#179/#180 baubar.
## Stand heute
Die drei Konten aus #177 sind mit Platzhaltern angelegt: `admin`/`admin` und `user`/`user`
(auf ausdrücklichen Wunsch), dazu `e2e` mit einem zufälligen Passwort in `data/e2e-password.txt`
(git-ignoriert). Ein Passwort ändern kann heute nur, wer auf den Server kommt:
`make-user.php <name> <pw>` setzt für einen bestehenden Namen einfach den neuen Hash.
Eingeordnet: Der User hat am 2026-09-20 gesagt, dass ihm die Platzhalter erstmal egal sind, auf
dem Server läuft nichts Produktives. Das Issue bleibt trotzdem offen — nur eben nicht als
Notfall.
## Was gebaut werden soll
**Server** — `POST ?r=auth/password` `{currentPassword, newPassword}` für das **eigene** Konto:
- Das alte Passwort wird verlangt und geprüft, auch wenn eine Session da ist — sonst reicht ein
fremder offener Tab, um ein Konto zu übernehmen.
- Mindestlänge serverseitig prüfen, dieselbe Regel wie in #178.
- `UserStore::put()` kann das Ersetzen schon, es braucht also nur die Route und die Prüfung.
- **Andere Sessions beenden:** Nach dem Wechsel sollten alle Logins dieses Kontos ungültig
werden, außer dem gerade benutzten. Dafür fehlt `SessionStore` ein
`destroyAllFor(username, außer $token)` — dieselbe Ergänzung wie in #179.
**Client** — ein Dialog "Passwort ändern" mit altem Passwort, neuem und Wiederholung, alle drei
als Bullets (`TextField` kann das seit #177). Erreichbar aus der Toolbar, neben oder statt dem
Logout-Knopf; siehe Rückfrage 2. Die Regeln (leer, ungleich, zu kurz, neu gleich alt) gehören als
reine Funktion neben `loginProblem()` in `src/editor/auth/loginRules.ts`.
**Tests**
- Unit: die Regeln der Maske, `HttpAuthStore` für "altes Passwort falsch" gegen "Server kaputt".
- API direkt gemessen: mit falschem alten Passwort abgelehnt, danach meldet das alte Passwort
nicht mehr an und das neue schon.
- e2e: in Modul 45 ändern und mit dem neuen Passwort neu anmelden. **Achtung Aufräumen** — das
Konto `e2e` steht im nächsten Lauf sonst mit dem falschen Passwort da (`data/e2e-password.txt`
bzw. `GAME_MAKER_E2E_PASSWORD`). Entweder am Ende zurücksetzen oder den Check an einem
Wegwerfkonto fahren; das hängt mit an #178.
## Rückfragen
1. **Sollen die Platzhalter beim Bau dieses Issues gleich mit geändert werden,** oder bleiben sie
bewusst so, bis du es sagst? Nach deiner Ansage vom 2026-09-20 gehe ich von "bleiben" aus.
2. **Wo soll der Einstieg sitzen?** In der Toolbar ist rechts nur der Logout-Knopf; der
Benutzername steht seit #177 im Fenstertitel, weil die Leiste bei 1280 px sonst zu breit wird.
Vorschlag: aus dem Logout-Knopf ein kleines Konto-Menü machen (Name, "Passwort ändern",
"Logout") — das kostet keinen zusätzlichen Platz und hat gleich einen Ort für spätere
Kontosachen.
3. **Mindestlänge?** Vorschlag wie in #178: 8 Zeichen.
4. **Soll das Ändern die anderen Sitzungen wirklich abmelden?** Das ist das saubere Verhalten,
trifft hier aber vor allem dich selbst in einem zweiten Tab.
die platzhalter (= admin, user und e2e oder?) sollen bleiben ja
dein vorschlag passt
passt auch
wenn es nicht zu umständlich ist, dann ja, andere sitzungen abmelden bei passwortänderung
1. die platzhalter (= admin, user und e2e oder?) sollen bleiben ja
2. dein vorschlag passt
3. passt auch
4. wenn es nicht zu umständlich ist, dann ja, andere sitzungen abmelden bei passwortänderung
Kontomenü statt Logout-Knopf (Rückfrage 2, dein „passt auch"): Ganz rechts in der Toolbar steht Account. Darin „Signed in as <name>" als Eintrag, der nicht anklickbar ist, darunter Change password… und Logout.
Dialogsrc/editor/auth/PasswordDialog.ts: aktuelles Passwort, neues, Wiederholung, alle drei als Bullets. Anders als die übrigen Formulardialoge bleibt er bei einer Ablehnung stehen — sonst könnte er nicht sagen, warum. Die Regeln (leer, zu kurz, neu gleich alt, Wiederholung abweichend) liegen als reine Funktion passwordProblem() neben loginProblem().
Server:POST ?r=auth/password{currentPassword,newPassword} → 204. Das alte Passwort wird verlangt, obwohl eine Session da ist; 401 bei falschem alten Passwort, 422 unter der Mindestlänge. Die Route ist die einzige unter auth/, die eine Session braucht. Mindestlänge 8 in UserStore::MIN_PASSWORD_LENGTH und noch einmal im Client, damit die Maske ohne Serverfrage antworten kann.
Andere Sitzungen enden (Rückfrage 4): neu ist SessionStore::destroyAllFor($user, $keep) — alle übrigen Logins des Kontos werden beendet, die des Tabs, der ändert, bleibt. War nicht umständlich, die Sessions liegen ohnehin als Dateien vor.
Platzhalter bleiben (Rückfrage 1). Eine Klarstellung zu deiner Rückfrage: admin/admin und user/user sind die Platzhalter, e2e hat seit #177 ein zufälliges Passwort in data/e2e-password.txt. Angefasst wurde keines. Dass die beiden kürzer als 8 Zeichen sind, stört nicht: geprüft wird die Länge nur beim Setzen über die API, nicht beim Anmelden.
8 neue e2e-Checks in Modul 45: Kontomenü mit dem nicht wählbaren Namen, Dialog geht auf, zu kurzes Passwort ohne Serverfrage abgelehnt, falsches aktuelles Passwort abgelehnt (Dialog bleibt stehen), Änderung geht durch, altes Passwort meldet nicht mehr an, neues schon, am Ende Rücksetzen auf das Originalpasswort.
6 neue Unit-Tests in tests/core/auth.test.ts: die Regeln der Maske und HttpAuthStore.changePassword samt der Unterscheidung 401 (falsches altes Passwort) gegen 422 (neues zu kurz) — letzteres darf gerade nicht als „falsches Passwort" erscheinen.
API direkt gemessen über https://nockal.com mit einem Wegwerfkonto: ohne Session 401, GET 405, falsches altes Passwort 401, zu kurzes neues 422, richtige Angabe 204 — danach zweite Sitzung des Kontos {"user":null}, eigene weiter gültig, altes Passwort 401, neues 200. data/users.json danach aus der Sicherung zurückgelegt, Sessions des Wegwerfkontos gelöscht, make-user.php --list zeigt wieder genau admin, e2e, user.
Gegenprobe mit eingebautem Fehler: In Auth::changePassword die Prüfung des alten Passworts ausgehebelt → genau ein Check fällt (18 PASS, 1 FAIL). Original per cmp zurückgeprüft.
Check-Summe 1126 = 1118 + 8. Die 1118 ist von CI an 1ef497f bestätigt, die 1126 ist gerechnet; der Lauf zu c830c7a bestätigt sie.
Ein Punkt, der bleibt
Modul 45 fasst das echte Passwort des e2e-Kontos an und setzt es am Ende zurück. Stirbt ein Lauf genau dazwischen, steht das Konto auf <passwort>-tmp und jedes Tool scheitert am Anmelden. Die Reparatur (ein make-user.php-Aufruf) steht als Kommentar im Modul. Sauberer wäre ein Wegwerfkonto — das hängt an #178.
Doku
Wiki: Accounts (neuer Abschnitt Changing the password, Kontomenü statt Logout-Knopf), API (die Route mit 401/422 und dem Abmelden der anderen Sitzungen), Architecture (PasswordDialog), Concepts (Kontomenü), Development (Check-Summe).
Umgesetzt in `c830c7a`, Wiki in `3310139`.
## Was gebaut ist
**Kontomenü statt Logout-Knopf** (Rückfrage 2, dein „passt auch"): Ganz rechts in der Toolbar steht **Account**. Darin „Signed in as <name>" als Eintrag, der nicht anklickbar ist, darunter **Change password…** und **Logout**.
**Dialog** `src/editor/auth/PasswordDialog.ts`: aktuelles Passwort, neues, Wiederholung, alle drei als Bullets. Anders als die übrigen Formulardialoge bleibt er bei einer Ablehnung stehen — sonst könnte er nicht sagen, warum. Die Regeln (leer, zu kurz, neu gleich alt, Wiederholung abweichend) liegen als reine Funktion `passwordProblem()` neben `loginProblem()`.
**Server:** `POST ?r=auth/password` `{currentPassword,newPassword}` → 204. Das alte Passwort wird verlangt, obwohl eine Session da ist; 401 bei falschem alten Passwort, 422 unter der Mindestlänge. Die Route ist die einzige unter `auth/`, die eine Session braucht. Mindestlänge **8** in `UserStore::MIN_PASSWORD_LENGTH` und noch einmal im Client, damit die Maske ohne Serverfrage antworten kann.
**Andere Sitzungen enden** (Rückfrage 4): neu ist `SessionStore::destroyAllFor($user, $keep)` — alle übrigen Logins des Kontos werden beendet, die des Tabs, der ändert, bleibt. War nicht umständlich, die Sessions liegen ohnehin als Dateien vor.
**Platzhalter bleiben** (Rückfrage 1). Eine Klarstellung zu deiner Rückfrage: `admin`/`admin` und `user`/`user` sind die Platzhalter, `e2e` hat seit #177 ein zufälliges Passwort in `data/e2e-password.txt`. Angefasst wurde keines. Dass die beiden kürzer als 8 Zeichen sind, stört nicht: geprüft wird die Länge nur beim *Setzen* über die API, nicht beim Anmelden.
## Tests
```
./check.sh -> 118 Files, 1711 Tests, CHECK EXIT 0
npm run build -> BUILD EXIT 0
GM_STEPS=45 (frisch geseedet) -> ALL PASSED - 19 checks (vorher 11)
```
- **8 neue e2e-Checks in Modul 45:** Kontomenü mit dem nicht wählbaren Namen, Dialog geht auf, zu kurzes Passwort ohne Serverfrage abgelehnt, falsches aktuelles Passwort abgelehnt (Dialog bleibt stehen), Änderung geht durch, altes Passwort meldet nicht mehr an, neues schon, am Ende Rücksetzen auf das Originalpasswort.
- **6 neue Unit-Tests** in `tests/core/auth.test.ts`: die Regeln der Maske und `HttpAuthStore.changePassword` samt der Unterscheidung 401 (falsches altes Passwort) gegen 422 (neues zu kurz) — letzteres darf gerade nicht als „falsches Passwort" erscheinen.
- **API direkt gemessen** über `https://nockal.com` mit einem Wegwerfkonto: ohne Session 401, `GET` 405, falsches altes Passwort 401, zu kurzes neues 422, richtige Angabe 204 — danach zweite Sitzung des Kontos `{"user":null}`, eigene weiter gültig, altes Passwort 401, neues 200. `data/users.json` danach aus der Sicherung zurückgelegt, Sessions des Wegwerfkontos gelöscht, `make-user.php --list` zeigt wieder genau `admin`, `e2e`, `user`.
- **Gegenprobe mit eingebautem Fehler:** In `Auth::changePassword` die Prüfung des alten Passworts ausgehebelt → genau ein Check fällt (18 PASS, 1 FAIL). Original per `cmp` zurückgeprüft.
- **Check-Summe 1126** = 1118 + 8. Die 1118 ist von CI an `1ef497f` bestätigt, die 1126 ist gerechnet; der Lauf zu `c830c7a` bestätigt sie.
## Ein Punkt, der bleibt
Modul 45 fasst das echte Passwort des `e2e`-Kontos an und setzt es am Ende zurück. Stirbt ein Lauf genau dazwischen, steht das Konto auf `<passwort>-tmp` und jedes Tool scheitert am Anmelden. Die Reparatur (ein `make-user.php`-Aufruf) steht als Kommentar im Modul. Sauberer wäre ein Wegwerfkonto — das hängt an #178.
## Doku
Wiki: **Accounts** (neuer Abschnitt *Changing the password*, Kontomenü statt Logout-Knopf), **API** (die Route mit 401/422 und dem Abmelden der anderen Sitzungen), **Architecture** (`PasswordDialog`), **Concepts** (Kontomenü), **Development** (Check-Summe).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Folge von #177 (User Management). Kleinstes der fünf Folge-Issues und ohne Abhängigkeit zu
#179/#180 baubar.
Stand heute
Die drei Konten aus #177 sind mit Platzhaltern angelegt:
admin/adminunduser/user(auf ausdrücklichen Wunsch), dazu
e2emit einem zufälligen Passwort indata/e2e-password.txt(git-ignoriert). Ein Passwort ändern kann heute nur, wer auf den Server kommt:
make-user.php <name> <pw>setzt für einen bestehenden Namen einfach den neuen Hash.Eingeordnet: Der User hat am 2026-09-20 gesagt, dass ihm die Platzhalter erstmal egal sind, auf
dem Server läuft nichts Produktives. Das Issue bleibt trotzdem offen — nur eben nicht als
Notfall.
Was gebaut werden soll
Server —
POST ?r=auth/password{currentPassword, newPassword}für das eigene Konto:fremder offener Tab, um ein Konto zu übernehmen.
UserStore::put()kann das Ersetzen schon, es braucht also nur die Route und die Prüfung.werden, außer dem gerade benutzten. Dafür fehlt
SessionStoreeindestroyAllFor(username, außer $token)— dieselbe Ergänzung wie in #179.Client — ein Dialog "Passwort ändern" mit altem Passwort, neuem und Wiederholung, alle drei
als Bullets (
TextFieldkann das seit #177). Erreichbar aus der Toolbar, neben oder statt demLogout-Knopf; siehe Rückfrage 2. Die Regeln (leer, ungleich, zu kurz, neu gleich alt) gehören als
reine Funktion neben
loginProblem()insrc/editor/auth/loginRules.ts.Tests
HttpAuthStorefür "altes Passwort falsch" gegen "Server kaputt".nicht mehr an und das neue schon.
Konto
e2esteht im nächsten Lauf sonst mit dem falschen Passwort da (data/e2e-password.txtbzw.
GAME_MAKER_E2E_PASSWORD). Entweder am Ende zurücksetzen oder den Check an einemWegwerfkonto fahren; das hängt mit an #178.
Rückfragen
bewusst so, bis du es sagst? Nach deiner Ansage vom 2026-09-20 gehe ich von "bleiben" aus.
Benutzername steht seit #177 im Fenstertitel, weil die Leiste bei 1280 px sonst zu breit wird.
Vorschlag: aus dem Logout-Knopf ein kleines Konto-Menü machen (Name, "Passwort ändern",
"Logout") — das kostet keinen zusätzlichen Platz und hat gleich einen Ort für spätere
Kontosachen.
trifft hier aber vor allem dich selbst in einem zweiten Tab.
Passwort aendernto Passwort ändernUmgesetzt in
c830c7a, Wiki in3310139.Was gebaut ist
Kontomenü statt Logout-Knopf (Rückfrage 2, dein „passt auch"): Ganz rechts in der Toolbar steht Account. Darin „Signed in as <name>" als Eintrag, der nicht anklickbar ist, darunter Change password… und Logout.
Dialog
src/editor/auth/PasswordDialog.ts: aktuelles Passwort, neues, Wiederholung, alle drei als Bullets. Anders als die übrigen Formulardialoge bleibt er bei einer Ablehnung stehen — sonst könnte er nicht sagen, warum. Die Regeln (leer, zu kurz, neu gleich alt, Wiederholung abweichend) liegen als reine FunktionpasswordProblem()nebenloginProblem().Server:
POST ?r=auth/password{currentPassword,newPassword}→ 204. Das alte Passwort wird verlangt, obwohl eine Session da ist; 401 bei falschem alten Passwort, 422 unter der Mindestlänge. Die Route ist die einzige unterauth/, die eine Session braucht. Mindestlänge 8 inUserStore::MIN_PASSWORD_LENGTHund noch einmal im Client, damit die Maske ohne Serverfrage antworten kann.Andere Sitzungen enden (Rückfrage 4): neu ist
SessionStore::destroyAllFor($user, $keep)— alle übrigen Logins des Kontos werden beendet, die des Tabs, der ändert, bleibt. War nicht umständlich, die Sessions liegen ohnehin als Dateien vor.Platzhalter bleiben (Rückfrage 1). Eine Klarstellung zu deiner Rückfrage:
admin/adminunduser/usersind die Platzhalter,e2ehat seit #177 ein zufälliges Passwort indata/e2e-password.txt. Angefasst wurde keines. Dass die beiden kürzer als 8 Zeichen sind, stört nicht: geprüft wird die Länge nur beim Setzen über die API, nicht beim Anmelden.Tests
tests/core/auth.test.ts: die Regeln der Maske undHttpAuthStore.changePasswordsamt der Unterscheidung 401 (falsches altes Passwort) gegen 422 (neues zu kurz) — letzteres darf gerade nicht als „falsches Passwort" erscheinen.https://nockal.commit einem Wegwerfkonto: ohne Session 401,GET405, falsches altes Passwort 401, zu kurzes neues 422, richtige Angabe 204 — danach zweite Sitzung des Kontos{"user":null}, eigene weiter gültig, altes Passwort 401, neues 200.data/users.jsondanach aus der Sicherung zurückgelegt, Sessions des Wegwerfkontos gelöscht,make-user.php --listzeigt wieder genauadmin,e2e,user.Auth::changePassworddie Prüfung des alten Passworts ausgehebelt → genau ein Check fällt (18 PASS, 1 FAIL). Original percmpzurückgeprüft.1ef497fbestätigt, die 1126 ist gerechnet; der Lauf zuc830c7abestätigt sie.Ein Punkt, der bleibt
Modul 45 fasst das echte Passwort des
e2e-Kontos an und setzt es am Ende zurück. Stirbt ein Lauf genau dazwischen, steht das Konto auf<passwort>-tmpund jedes Tool scheitert am Anmelden. Die Reparatur (einmake-user.php-Aufruf) steht als Kommentar im Modul. Sauberer wäre ein Wegwerfkonto — das hängt an #178.Doku
Wiki: Accounts (neuer Abschnitt Changing the password, Kontomenü statt Logout-Knopf), API (die Route mit 401/422 und dem Abmelden der anderen Sitzungen), Architecture (
PasswordDialog), Concepts (Kontomenü), Development (Check-Summe).CI-Nachtrag zu
c830c7a: grün. Gate (Task 172) 118 Testdateien,CHECK EXIT 0; Suite (Task 173)ALL PASSED – 1126 checks, Smokes 24 / 19 / 21. Die Check-Summe 1126 ist damit bestätigt.