210 lines
6.5 KiB
PHP
210 lines
6.5 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers;
|
|
|
|
use App\Core\BaseController;
|
|
use App\Models\User;
|
|
use App\Models\PasswordReset;
|
|
|
|
class AuthController extends BaseController
|
|
{
|
|
public function showLogin(): string
|
|
{
|
|
if ($this->session->isLoggedIn()) {
|
|
return $this->redirect('/dashboard');
|
|
}
|
|
|
|
return $this->render('auth/login');
|
|
}
|
|
|
|
public function login(): Response
|
|
{
|
|
$email = $this->request->post('email');
|
|
$password = $this->request->post('password');
|
|
|
|
// Check if account is locked
|
|
if ($this->session->isLockedOut()) {
|
|
$this->flash('error', 'Account ist gesperrt. Bitte warten Sie 15 Minuten.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
// Validate input
|
|
$errors = $this->validate([
|
|
'email' => 'required|email',
|
|
'password' => 'required'
|
|
]);
|
|
|
|
if (!empty($errors)) {
|
|
foreach ($errors as $field => $fieldErrors) {
|
|
foreach ($fieldErrors as $error) {
|
|
$this->flash('error', $error);
|
|
}
|
|
}
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
// Find user
|
|
$user = (new User($this->database))->findByEmail($email);
|
|
|
|
if (!$user || !$user['active']) {
|
|
$this->incrementLoginAttempts();
|
|
$this->flash('error', 'Ungültige Anmeldedaten oder Account inaktiv.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
// Verify password
|
|
if (!password_verify($password, $user['passhash'])) {
|
|
$this->incrementLoginAttempts();
|
|
$this->flash('error', 'Ungültige Anmeldedaten.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
// Check if password needs rehash
|
|
if (password_needs_rehash($user['passhash'], PASSWORD_ARGON2ID)) {
|
|
$newHash = password_hash($password, PASSWORD_ARGON2ID);
|
|
(new User($this->database))->update($user['id'], ['passhash' => $newHash]);
|
|
}
|
|
|
|
// Reset login attempts
|
|
$this->session->setLoginAttempts(0);
|
|
|
|
// Set user session
|
|
$this->session->setUser($user);
|
|
$this->session->setLastActivity();
|
|
|
|
// Log audit
|
|
$this->logAudit('login', 'users', $user['id']);
|
|
|
|
$this->flash('success', 'Erfolgreich angemeldet.');
|
|
return $this->redirect('/dashboard');
|
|
}
|
|
|
|
public function logout(): Response
|
|
{
|
|
if ($this->session->isLoggedIn()) {
|
|
$userId = $this->session->getUserId();
|
|
$this->logAudit('logout', 'users', $userId);
|
|
}
|
|
|
|
$this->session->logout();
|
|
$this->flash('success', 'Erfolgreich abgemeldet.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
public function showForgotPassword(): string
|
|
{
|
|
return $this->render('auth/forgot-password');
|
|
}
|
|
|
|
public function forgotPassword(): Response
|
|
{
|
|
$email = $this->request->post('email');
|
|
|
|
$errors = $this->validate([
|
|
'email' => 'required|email'
|
|
]);
|
|
|
|
if (!empty($errors)) {
|
|
foreach ($errors as $field => $fieldErrors) {
|
|
foreach ($fieldErrors as $error) {
|
|
$this->flash('error', $error);
|
|
}
|
|
}
|
|
return $this->redirect('/password/forgot');
|
|
}
|
|
|
|
$user = (new User($this->database))->findByEmail($email);
|
|
|
|
if ($user && $user['active']) {
|
|
$token = bin2hex(random_bytes(32));
|
|
$expiresAt = date('Y-m-d H:i:s', strtotime('+1 hour'));
|
|
|
|
$passwordReset = new PasswordReset($this->database);
|
|
$passwordReset->create([
|
|
'user_id' => $user['id'],
|
|
'token' => $token,
|
|
'expires_at' => $expiresAt
|
|
]);
|
|
|
|
// Send email (in production, implement email service)
|
|
// $this->sendPasswordResetEmail($user['email'], $token);
|
|
}
|
|
|
|
// Always show success message for security
|
|
$this->flash('success', 'Falls die E-Mail-Adresse existiert, wurde ein Reset-Link gesendet.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
public function showResetPassword(): string
|
|
{
|
|
$token = $this->request->get('token');
|
|
|
|
if (!$token) {
|
|
$this->flash('error', 'Ungültiger Reset-Link.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
$passwordReset = new PasswordReset($this->database);
|
|
$reset = $passwordReset->findByToken($token);
|
|
|
|
if (!$reset || strtotime($reset['expires_at']) < time()) {
|
|
$this->flash('error', 'Reset-Link ist ungültig oder abgelaufen.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
return $this->render('auth/reset-password', ['token' => $token]);
|
|
}
|
|
|
|
public function resetPassword(): Response
|
|
{
|
|
$token = $this->request->post('token');
|
|
$password = $this->request->post('password');
|
|
$passwordConfirm = $this->request->post('password_confirm');
|
|
|
|
$errors = $this->validate([
|
|
'password' => 'required|min:8',
|
|
'password_confirm' => 'required'
|
|
]);
|
|
|
|
if ($password !== $passwordConfirm) {
|
|
$this->flash('error', 'Passwörter stimmen nicht überein.');
|
|
return $this->redirect('/password/reset?token=' . $token);
|
|
}
|
|
|
|
if (!empty($errors)) {
|
|
foreach ($errors as $field => $fieldErrors) {
|
|
foreach ($fieldErrors as $error) {
|
|
$this->flash('error', $error);
|
|
}
|
|
}
|
|
return $this->redirect('/password/reset?token=' . $token);
|
|
}
|
|
|
|
$passwordReset = new PasswordReset($this->database);
|
|
$reset = $passwordReset->findByToken($token);
|
|
|
|
if (!$reset || strtotime($reset['expires_at']) < time()) {
|
|
$this->flash('error', 'Reset-Link ist ungültig oder abgelaufen.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
// Update user password
|
|
$userModel = new User($this->database);
|
|
$userModel->update($reset['user_id'], [
|
|
'passhash' => password_hash($password, PASSWORD_ARGON2ID)
|
|
]);
|
|
|
|
// Delete used reset token
|
|
$passwordReset->delete($reset['id']);
|
|
|
|
$this->flash('success', 'Passwort erfolgreich geändert. Sie können sich jetzt anmelden.');
|
|
return $this->redirect('/login');
|
|
}
|
|
|
|
private function incrementLoginAttempts(): void
|
|
{
|
|
$attempts = $this->session->getLoginAttempts();
|
|
$this->session->setLoginAttempts($attempts + 1);
|
|
}
|
|
}
|