236 lines
8.6 KiB
PHP
236 lines
8.6 KiB
PHP
<?php
|
|
/**
|
|
* @file login.php
|
|
* @brief Steuert den Login-Vorgang und baut eine Benutzersitzung auf.
|
|
*
|
|
* @details Enthält das Formular und die Verarbeitung des POST-Requests,
|
|
* verifiziert Passwort-Hashes, veranlasst ggf. einen Re-Hash und speichert Attribute in der Session.
|
|
*/
|
|
|
|
// login.php
|
|
|
|
/**
|
|
* @brief Bindet die Bootstrap-Datei ein, um Session und Basiskonfigurationen zu gewährleisten.
|
|
*/
|
|
require_once __DIR__ . '/lib/bootstrap.php';
|
|
|
|
/**
|
|
* 1) DB-Verbindung (einmal)
|
|
* @brief Stellt eine Datenbankverbindung her.
|
|
* @var mysqli $conn Die etablierte Datenbankverbindung.
|
|
*/
|
|
$conn = db_connect();
|
|
|
|
/**
|
|
* 2) POST-Verarbeitung VOR jeglicher Ausgabe
|
|
* @brief Initialisierung von Fehler- und Infomeldungen.
|
|
*/
|
|
/** @var string|null $loginError Speichert mögliche Fehlermeldungen während des Login-Prozesses. */
|
|
$loginError = null;
|
|
/** @var string|null $loginInfo Speichert mögliche Informationsmeldungen für den Benutzer aus. */
|
|
$loginInfo = null;
|
|
|
|
/**
|
|
* @brief Prüft, ob der Nutzer frisch von der Registrierungsseite kommt.
|
|
* @details Die Variable $_GET['registered'] wird gesetzt, wenn eine erfolgreiche Weiterleitung von register.php stattfand.
|
|
*/
|
|
if (isset($_GET['registered']) && $_GET['registered'] === '1')
|
|
{
|
|
/** @brief Setzt eine Info-Nachricht für die UI, dass die Registrierung erfolgreich war. */
|
|
$loginInfo = 'Registrierung erfolgreich. Du kannst dich jetzt einloggen.';
|
|
}
|
|
|
|
/**
|
|
* @brief Fängt das Formular-Submit ab, wenn die Anfrage-Methode POST ist.
|
|
* @details Überprüft, ob Daten aus dem Formular abgesendet wurden.
|
|
*/
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST')
|
|
{
|
|
/** @var string $uname Speichert den eingegebenen Nutzernamen vor der Validierung. */
|
|
$uname = '';
|
|
|
|
/**
|
|
* @brief Prüft, ob das Feld `uname` übermittelt wurde und weist es zu.
|
|
*/
|
|
if (isset($_POST['uname']))
|
|
{
|
|
/** @details Führt zudem ein trim() aus, um überschüssige Leerzeichen am Anfang und Ende zu entfernen. */
|
|
$uname = trim($_POST['uname']);
|
|
}
|
|
|
|
/**
|
|
* @brief Prüft, ob das Feld `pw` übermittelt wurde und weist es zu.
|
|
*/
|
|
if (isset($_POST['pw']))
|
|
{
|
|
/** @var string $pw Speichert das eingegebene Passwort im Klartext. */
|
|
$pw = $_POST['pw'];
|
|
}
|
|
else
|
|
{
|
|
/** @details Falls kein Passwort übergeben wurde, wird ein leerer String zugewiesen. */
|
|
$pw = '';
|
|
}
|
|
|
|
/**
|
|
* @brief Basic Validierung vor dem Datenbank-Look-Up.
|
|
* @details Prüft, ob Nutzername oder Passwort komplett leer sind.
|
|
*/
|
|
if ($uname === '' || $pw === '')
|
|
{
|
|
/** @brief Fehler-Status setzen, wenn mindestens eins der benötigten Felder leer ist. */
|
|
$loginError = "Bitte Username und Passwort eingeben.";
|
|
}
|
|
else
|
|
{
|
|
/**
|
|
* @brief Login ist SELECT, mit Prepared Statement (sicher) und ?-Platzhalter
|
|
* @details Es werden die Felder userID, displayName und passwordHash gesucht, um den Nutzer später authentifizieren zu können.
|
|
* @var mysqli_stmt|false $stmt Das vorbereitete Query-Statement.
|
|
*/
|
|
$stmt = mysqli_prepare(
|
|
$conn,
|
|
"SELECT userID, displayName, passwordHash FROM users WHERE displayName = ? LIMIT 1"
|
|
);
|
|
|
|
/// Prüft, ob beim Vorbereiten des Statements ein Fehler geschah.
|
|
if (!$stmt)
|
|
{
|
|
$loginError = "Datenbankfehler.";
|
|
}
|
|
else
|
|
{
|
|
/// Bindet den gesuchten Nutzernamen als String (`s`) an das SQL-Statement.
|
|
mysqli_stmt_bind_param($stmt, "s", $uname);
|
|
/// Führt die Datenbankabfrage aus.
|
|
mysqli_stmt_execute($stmt);
|
|
|
|
/// @var mysqli_result|false $result Das ResultSet der Select-Abfrage.
|
|
$result = mysqli_stmt_get_result($stmt);
|
|
|
|
/** @var array|null $user Enthält die abgerufenen Datensätze aus der DB. */
|
|
$user = null;
|
|
|
|
/// Sofern ein Resultat gefunden wurde, wird die erste Zeile assoziativ in `$user` geschrieben.
|
|
if ($result)
|
|
{
|
|
$user = mysqli_fetch_assoc($result);
|
|
}
|
|
|
|
/**
|
|
* @brief Passwort prüfen: Eingabe gegen gespeicherten Hash (password_hash/password_verify)
|
|
*/
|
|
if ($user && password_verify($pw, $user['passwordHash']))
|
|
{
|
|
/**
|
|
* @brief Optional: Rehash, falls Algorithmus/Cost geändert wurde.
|
|
* @details Überprüft, ob der bestehende Hash nicht mehr den aktuellen Anforderungen von PASSWORD_DEFAULT entspricht.
|
|
*/
|
|
if (password_needs_rehash($user['passwordHash'], PASSWORD_DEFAULT))
|
|
{
|
|
/** @var string $newHash Erzeugt einen neuen Hash mit dem aktuellen Standard-Algorithmus. */
|
|
$newHash = password_hash($pw, PASSWORD_DEFAULT);
|
|
/** @var mysqli_stmt|false $upd Bereitet das Statement für das Update in der DB vor. */
|
|
$upd = mysqli_prepare($conn, "UPDATE users SET passwordHash = ? WHERE userID = ?");
|
|
if ($upd)
|
|
{
|
|
/// @var int $userID Konvertiert die ID sicher in einen Integer.
|
|
$userID = (int)$user['userID'];
|
|
/// Bindet Parameter für das Update-Query (s = string, i = int).
|
|
mysqli_stmt_bind_param($upd, "si", $newHash, $userID);
|
|
/// Führt das Update auf den Passworthash durch.
|
|
mysqli_stmt_execute($upd);
|
|
/// Schließt das Update-Statement.
|
|
mysqli_stmt_close($upd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Setzt die Session-Werte zur Authentifizierung des Nutzers.
|
|
*/
|
|
$_SESSION['user_id'] = (int)$user['userID'];
|
|
$_SESSION['displayName'] = $user['displayName'];
|
|
|
|
/** @brief Schließt das Query-Statement und die DB-Verbindung (falls nicht gepoolt) sauber ab. */
|
|
mysqli_stmt_close($stmt);
|
|
mysqli_close($conn);
|
|
|
|
/**
|
|
* @brief Leitet den Benutzer ins Profil/Konto weiter.
|
|
* @details Bricht zudem die Skriptausführung mittels exit ab, nachdem der Header gesendet wurde.
|
|
*/
|
|
header("Location: account.php");
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* @brief Fallback, falls Authentifizierung am PW oder fehlendem Nutzer scheitert.
|
|
*/
|
|
$loginError = "Ungltige Zugangsdaten.";
|
|
mysqli_stmt_close($stmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Bindet die Header-HTML-Komponente ein.
|
|
*/
|
|
include 'header.php';
|
|
?>
|
|
|
|
<main class="auth" role="main">
|
|
<section class="auth__grid" aria-label="Login Bereich">
|
|
<div class="auth__card">
|
|
<header class="auth__header">
|
|
<h2 class="auth__title">Login</h2>
|
|
</header>
|
|
|
|
<?php
|
|
/** @brief Rendert optional Erfolgsnachrichten (z.B. nach Registrierung). */
|
|
if ($loginInfo): ?>
|
|
<p class="auth__alert__sucess"
|
|
role="status"><?php echo htmlspecialchars($loginInfo, ENT_QUOTES, 'UTF-8'); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<?php
|
|
/** @brief Rendert optional aufgetretene Fehler beim Login-Versuch. */
|
|
if ($loginError): ?>
|
|
<p class="auth__alert__error"
|
|
role="alert"><?php echo htmlspecialchars($loginError, ENT_QUOTES, 'UTF-8'); ?></p>
|
|
<?php endif; ?>
|
|
|
|
<form class="auth__form" action="login.php" method="POST" autocomplete="on">
|
|
<div class="auth__field">
|
|
<label for="uname">Username</label>
|
|
<input type="text" id="uname" name="uname" inputmode="text" autocomplete="username" required>
|
|
</div>
|
|
|
|
<div class="auth__field">
|
|
<label for="pw">Passwort</label>
|
|
<input type="password" id="pw" name="pw" autocomplete="current-password" required>
|
|
</div>
|
|
|
|
<div class="auth__actions">
|
|
<input class="auth__submit" type="submit" value="Einloggen">
|
|
</div>
|
|
</form>
|
|
|
|
<div class="auth__links">
|
|
<p class="auth__muted">Neu hier? <a href="register.php">Account erstellen</a></p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
|
|
<?php
|
|
/**
|
|
* @brief Schließt die Datenbankbindung, bevor der Footer geladen und die Seite an den Client geschickt wird.
|
|
*/
|
|
mysqli_close($conn);
|
|
|
|
/**
|
|
* @brief Bindet die Footer-HTML-Komponente ein.
|
|
*/
|
|
include 'footer.php';
|
|
?>
|