Merge pull request 'Fix #46: Reiter "Profil" zum Ändern des eigenen Passworts' (#47) from fds2610-bot/Finanzmanager:main into main
Reviewed-on: https://codeberg.org/fds2610/Finanzmanager/pulls/47
This commit is contained in:
commit
9022144c9e
6 changed files with 121 additions and 0 deletions
14
Changes.md
14
Changes.md
|
|
@ -1,5 +1,19 @@
|
|||
# Änderungen
|
||||
|
||||
## Einstellungen: Reiter "Profil" zum Ändern des eigenen Passworts (20.08.2026)
|
||||
|
||||
Bislang ließ sich das eigene Passwort nur direkt über den Datenbestand ändern (Feature #46).
|
||||
Der Einstellungsdialog bekommt dafür einen neuen Reiter `Profil`, der den angemeldeten
|
||||
Benutzernamen anzeigt und ein eigenes Formular für die Passwortänderung enthält – getrennt
|
||||
vom generischen Speichern-Formular der übrigen Reiter, weil hier kein Einstellungswert,
|
||||
sondern der Anmeldedatensatz geschrieben wird.
|
||||
|
||||
`EinstellungenController::passwortAendern()` (Route `POST /einstellungen/passwort`) verlangt
|
||||
das aktuelle Passwort (geprüft über `Benutzer::pruefeAnmeldung()`), ein neues mit mindestens
|
||||
10 Zeichen – dieselbe Grenze wie bei der Ersteinrichtung – und dessen Wiederholung. Bei Erfolg
|
||||
schreibt `Benutzer::passwortSetzen()` den neuen Hash; ein Eintrag landet wie bei An- und
|
||||
Abmeldung im Debug-Log.
|
||||
|
||||
## CONTRIBUTE.md und Setup-Skript für Mitwirkende (19.08.2026)
|
||||
|
||||
`CONTRIBUTE.md` beschreibt, wie Beiträge ankommen (Issue, PR, E-Mail), welche
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ $router->post('/cockpit/vermoegen/konten', fn() => (new CockpitController())->ko
|
|||
$router->get('/einstellungen', fn() => (new EinstellungenController())->index());
|
||||
$router->post('/einstellungen', fn() => (new EinstellungenController())->speichern());
|
||||
$router->post('/einstellungen/log-leeren', fn() => (new EinstellungenController())->logLeeren());
|
||||
$router->post('/einstellungen/passwort', fn() => (new EinstellungenController())->passwortAendern());
|
||||
|
||||
// Konten. Die festen Pfade stehen vor den Mustern mit {id}, sonst schluckt
|
||||
// "/konten/{id}/…" die Route "/konten/neu".
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ declare(strict_types=1);
|
|||
|
||||
namespace Finanzmanager\Controllers;
|
||||
|
||||
use Finanzmanager\Auth;
|
||||
use Finanzmanager\Models\Benutzer;
|
||||
use Finanzmanager\Models\Einstellung;
|
||||
use Finanzmanager\Models\Kategorie;
|
||||
use Finanzmanager\Services\Cockpit;
|
||||
|
|
@ -90,6 +92,39 @@ class EinstellungenController extends BaseController
|
|||
);
|
||||
}
|
||||
|
||||
/** Eigenes Passwort ändern, Reiter "Profil". Passt nicht in felderFuer()/speichern(),
|
||||
* weil es keinen Einstellungswert schreibt, sondern die Anmeldedaten des Benutzers. */
|
||||
public function passwortAendern(): void
|
||||
{
|
||||
$this->verlangeAnmeldung();
|
||||
|
||||
$benutzer = Auth::benutzer();
|
||||
if ($benutzer === null) {
|
||||
View::redirect('/login');
|
||||
}
|
||||
|
||||
$altesPasswort = (string)($_POST['passwort_alt'] ?? '');
|
||||
$neuesPasswort = (string)($_POST['passwort_neu'] ?? '');
|
||||
$wiederholung = (string)($_POST['passwort_neu_wdh'] ?? '');
|
||||
|
||||
$fehler = match (true) {
|
||||
Benutzer::pruefeAnmeldung($benutzer['benutzername'], $altesPasswort) === null
|
||||
=> 'Das aktuelle Passwort ist falsch.',
|
||||
mb_strlen($neuesPasswort) < 10 => 'Das neue Passwort muss mindestens 10 Zeichen lang sein.',
|
||||
$neuesPasswort !== $wiederholung => 'Die beiden neuen Passwörter stimmen nicht überein.',
|
||||
default => null,
|
||||
};
|
||||
|
||||
if ($fehler !== null) {
|
||||
View::redirect('/einstellungen?bereich=profil', $fehler, 'fehler');
|
||||
}
|
||||
|
||||
Benutzer::passwortSetzen((int)$benutzer['id'], $neuesPasswort);
|
||||
DebugLog::schreibe('auth', 'Passwort geändert', ['benutzer_id' => $benutzer['id']]);
|
||||
|
||||
View::redirect('/einstellungen?bereich=profil', 'Passwort geändert.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Welche Felder zu welchem Reiter gehören, inklusive Typ.
|
||||
* Bereiche ohne Einträge sind Platzhalter für spätere Phasen.
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ class Einstellung
|
|||
'import' => 'Import/Export',
|
||||
'wertpapiere' => 'Wertpapiere',
|
||||
'diagnose' => 'Diagnose',
|
||||
'profil' => 'Profil',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use Finanzmanager\View;
|
|||
/** @var list<string> $kacheln */
|
||||
/** @var list<array<string,mixed>> $kategorien */
|
||||
/** @var string $csrfToken */
|
||||
/** @var array|null $benutzer */
|
||||
|
||||
$wert = static fn(string $k, string $standard = ''): string => (string)($werte[$k] ?? $standard);
|
||||
|
||||
|
|
@ -266,6 +267,11 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php elseif ($bereich === 'profil'): ?>
|
||||
<p class="feldhinweis">
|
||||
Angemeldet als <strong><?= View::h($benutzer['benutzername'] ?? '') ?></strong>.
|
||||
</p>
|
||||
|
||||
<?php else: ?>
|
||||
<p class="platzhalter">
|
||||
Dieser Bereich füllt sich in einer späteren Phase:
|
||||
|
|
@ -280,6 +286,28 @@ include __DIR__ . '/../layout/kopf.php';
|
|||
<?php endif; ?>
|
||||
</form>
|
||||
|
||||
<?php if ($bereich === 'profil'): ?>
|
||||
<form method="post" action="/einstellungen/passwort" class="formular breit">
|
||||
<input type="hidden" name="csrf_token" value="<?= View::h($csrfToken) ?>">
|
||||
|
||||
<label for="passwort_alt">Aktuelles Passwort</label>
|
||||
<input type="password" id="passwort_alt" name="passwort_alt" autocomplete="current-password" required>
|
||||
|
||||
<label for="passwort_neu">Neues Passwort</label>
|
||||
<input type="password" id="passwort_neu" name="passwort_neu" autocomplete="new-password"
|
||||
minlength="10" required>
|
||||
<p class="feldhinweis">Mindestens 10 Zeichen.</p>
|
||||
|
||||
<label for="passwort_neu_wdh">Neues Passwort wiederholen</label>
|
||||
<input type="password" id="passwort_neu_wdh" name="passwort_neu_wdh" autocomplete="new-password"
|
||||
minlength="10" required>
|
||||
|
||||
<div class="knopfleiste">
|
||||
<button type="submit" class="knopf primaer">Passwort ändern</button>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($bereich === 'diagnose' && $logDateien !== []): ?>
|
||||
<form method="post" action="/einstellungen/log-leeren" class="formular breit"
|
||||
data-bestaetigen="Alle Logdateien löschen?">
|
||||
|
|
|
|||
42
tests/36_ProfilPasswortTest.php
Normal file
42
tests/36_ProfilPasswortTest.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Finanzmanager\Models\Benutzer;
|
||||
|
||||
class ProfilPasswortTest extends TestCase
|
||||
{
|
||||
protected string $testName = 'Test 36: Profil – eigenes Passwort ändern';
|
||||
|
||||
public function run(): void
|
||||
{
|
||||
$this->passwortWirdGeaendert();
|
||||
$this->altesPasswortWirdUngueltig();
|
||||
}
|
||||
|
||||
private function passwortWirdGeaendert(): void
|
||||
{
|
||||
$name = 'test_' . bin2hex(random_bytes(4));
|
||||
$id = Benutzer::anlegen($name, 'altespasswort123');
|
||||
|
||||
Benutzer::passwortSetzen($id, 'neuespasswort456');
|
||||
|
||||
$this->assertNotEmpty(
|
||||
Benutzer::pruefeAnmeldung($name, 'neuespasswort456'),
|
||||
'Anmeldung mit dem neuen Passwort gelingt'
|
||||
);
|
||||
}
|
||||
|
||||
private function altesPasswortWirdUngueltig(): void
|
||||
{
|
||||
$name = 'test_' . bin2hex(random_bytes(4));
|
||||
$id = Benutzer::anlegen($name, 'altespasswort123');
|
||||
|
||||
Benutzer::passwortSetzen($id, 'neuespasswort456');
|
||||
|
||||
$this->assertNull(
|
||||
Benutzer::pruefeAnmeldung($name, 'altespasswort123'),
|
||||
'Anmeldung mit dem alten Passwort schlägt danach fehl'
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue