Files
Inventory/app/Controllers/AuthController.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);
}
}