Initial commit of the Asset Management System, including project structure, Docker configuration, database migrations, and core application files. Added user authentication, asset management features, and basic UI components.
This commit is contained in:
120
app/Core/Application.php
Normal file
120
app/Core/Application.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use App\Middleware\AuthMiddleware;
|
||||
use App\Middleware\AdminMiddleware;
|
||||
use App\Middleware\CsrfMiddleware;
|
||||
|
||||
class Application
|
||||
{
|
||||
private Router $router;
|
||||
private Database $database;
|
||||
private Session $session;
|
||||
private array $middleware = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->router = Router::getInstance();
|
||||
$this->database = new Database();
|
||||
$this->session = new Session();
|
||||
|
||||
// Register middleware
|
||||
$this->registerMiddleware();
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
try {
|
||||
$request = new Request();
|
||||
$response = new Response();
|
||||
|
||||
// Apply global middleware
|
||||
$this->applyMiddleware($request, $response);
|
||||
|
||||
// Route the request
|
||||
$route = $this->router->match($request->getMethod(), $request->getPath());
|
||||
|
||||
if (!$route) {
|
||||
$this->handleNotFound();
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply route-specific middleware
|
||||
if (isset($route['middleware'])) {
|
||||
$this->applyRouteMiddleware($route['middleware'], $request, $response);
|
||||
}
|
||||
|
||||
// Execute controller
|
||||
$controller = new $route['controller']();
|
||||
$method = $route['method'];
|
||||
|
||||
// Inject dependencies
|
||||
$controller->setRequest($request);
|
||||
$controller->setResponse($response);
|
||||
$controller->setSession($this->session);
|
||||
$controller->setDatabase($this->database);
|
||||
|
||||
// Execute with parameters
|
||||
$params = $route['params'] ?? [];
|
||||
$result = call_user_func_array([$controller, $method], $params);
|
||||
|
||||
// Send response
|
||||
if ($result instanceof Response) {
|
||||
$result->send();
|
||||
} else {
|
||||
$response->setContent($result);
|
||||
$response->send();
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->handleError($e);
|
||||
}
|
||||
}
|
||||
|
||||
private function registerMiddleware(): void
|
||||
{
|
||||
$this->middleware = [
|
||||
'auth' => AuthMiddleware::class,
|
||||
'admin' => AdminMiddleware::class,
|
||||
'csrf' => CsrfMiddleware::class,
|
||||
];
|
||||
}
|
||||
|
||||
private function applyMiddleware(Request $request, Response $response): void
|
||||
{
|
||||
// Apply CSRF middleware to all POST requests
|
||||
if ($request->getMethod() === 'POST') {
|
||||
$csrfMiddleware = new CsrfMiddleware();
|
||||
$csrfMiddleware->handle($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
private function applyRouteMiddleware(array $middlewareNames, Request $request, Response $response): void
|
||||
{
|
||||
foreach ($middlewareNames as $name) {
|
||||
if (isset($this->middleware[$name])) {
|
||||
$middlewareClass = $this->middleware[$name];
|
||||
$middleware = new $middlewareClass();
|
||||
$middleware->handle($request, $response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function handleNotFound(): void
|
||||
{
|
||||
http_response_code(404);
|
||||
require APP_PATH . '/Views/errors/404.php';
|
||||
}
|
||||
|
||||
private function handleError(\Exception $e): void
|
||||
{
|
||||
if (APP_DEBUG) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
error_log($e->getMessage());
|
||||
http_response_code(500);
|
||||
require APP_PATH . '/Views/errors/500.php';
|
||||
}
|
||||
}
|
||||
132
app/Core/Database.php
Normal file
132
app/Core/Database.php
Normal file
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
use PDO;
|
||||
use PDOException;
|
||||
|
||||
class Database
|
||||
{
|
||||
private ?PDO $connection = null;
|
||||
private static ?Database $instance = null;
|
||||
|
||||
public static function getInstance(): Database
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function getConnection(): PDO
|
||||
{
|
||||
if ($this->connection === null) {
|
||||
$this->connect();
|
||||
}
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
private function connect(): void
|
||||
{
|
||||
try {
|
||||
$dsn = sprintf(
|
||||
'mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4',
|
||||
DB_HOST,
|
||||
DB_PORT,
|
||||
DB_NAME
|
||||
);
|
||||
|
||||
$options = [
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
PDO::ATTR_EMULATE_PREPARES => false,
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci"
|
||||
];
|
||||
|
||||
$this->connection = new PDO($dsn, DB_USER, DB_PASS, $options);
|
||||
|
||||
} catch (PDOException $e) {
|
||||
error_log('Database connection failed: ' . $e->getMessage());
|
||||
throw new \Exception('Database connection failed');
|
||||
}
|
||||
}
|
||||
|
||||
public function query(string $sql, array $params = []): \PDOStatement
|
||||
{
|
||||
$stmt = $this->getConnection()->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt;
|
||||
}
|
||||
|
||||
public function fetch(string $sql, array $params = []): ?array
|
||||
{
|
||||
$stmt = $this->query($sql, $params);
|
||||
$result = $stmt->fetch();
|
||||
return $result ?: null;
|
||||
}
|
||||
|
||||
public function fetchAll(string $sql, array $params = []): array
|
||||
{
|
||||
$stmt = $this->query($sql, $params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public function insert(string $table, array $data): int
|
||||
{
|
||||
$columns = implode(', ', array_keys($data));
|
||||
$placeholders = ':' . implode(', :', array_keys($data));
|
||||
|
||||
$sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})";
|
||||
|
||||
$this->query($sql, $data);
|
||||
return (int) $this->getConnection()->lastInsertId();
|
||||
}
|
||||
|
||||
public function update(string $table, array $data, string $where, array $whereParams = []): int
|
||||
{
|
||||
$setParts = [];
|
||||
foreach (array_keys($data) as $column) {
|
||||
$setParts[] = "{$column} = :{$column}";
|
||||
}
|
||||
$setClause = implode(', ', $setParts);
|
||||
|
||||
$sql = "UPDATE {$table} SET {$setClause} WHERE {$where}";
|
||||
|
||||
$params = array_merge($data, $whereParams);
|
||||
$stmt = $this->query($sql, $params);
|
||||
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
public function delete(string $table, string $where, array $params = []): int
|
||||
{
|
||||
$sql = "DELETE FROM {$table} WHERE {$where}";
|
||||
$stmt = $this->query($sql, $params);
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
public function beginTransaction(): bool
|
||||
{
|
||||
return $this->getConnection()->beginTransaction();
|
||||
}
|
||||
|
||||
public function commit(): bool
|
||||
{
|
||||
return $this->getConnection()->commit();
|
||||
}
|
||||
|
||||
public function rollback(): bool
|
||||
{
|
||||
return $this->getConnection()->rollback();
|
||||
}
|
||||
|
||||
public function inTransaction(): bool
|
||||
{
|
||||
return $this->getConnection()->inTransaction();
|
||||
}
|
||||
|
||||
public function quote(string $value): string
|
||||
{
|
||||
return $this->getConnection()->quote($value);
|
||||
}
|
||||
}
|
||||
182
app/Core/Request.php
Normal file
182
app/Core/Request.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class Request
|
||||
{
|
||||
private array $get;
|
||||
private array $post;
|
||||
private array $files;
|
||||
private array $server;
|
||||
private array $headers;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->get = $_GET;
|
||||
$this->post = $_POST;
|
||||
$this->files = $_FILES;
|
||||
$this->server = $_SERVER;
|
||||
$this->headers = $this->getRequestHeaders();
|
||||
}
|
||||
|
||||
public function getMethod(): string
|
||||
{
|
||||
return strtoupper($this->server['REQUEST_METHOD'] ?? 'GET');
|
||||
}
|
||||
|
||||
public function getPath(): string
|
||||
{
|
||||
$path = $this->server['REQUEST_URI'] ?? '/';
|
||||
$position = strpos($path, '?');
|
||||
|
||||
if ($position !== false) {
|
||||
$path = substr($path, 0, $position);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
return $this->get[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function post(string $key, $default = null)
|
||||
{
|
||||
return $this->post[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function file(string $key)
|
||||
{
|
||||
return $this->files[$key] ?? null;
|
||||
}
|
||||
|
||||
public function all(): array
|
||||
{
|
||||
return array_merge($this->get, $this->post);
|
||||
}
|
||||
|
||||
public function only(array $keys): array
|
||||
{
|
||||
$data = $this->all();
|
||||
return array_intersect_key($data, array_flip($keys));
|
||||
}
|
||||
|
||||
public function except(array $keys): array
|
||||
{
|
||||
$data = $this->all();
|
||||
return array_diff_key($data, array_flip($keys));
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($this->get[$key]) || isset($this->post[$key]);
|
||||
}
|
||||
|
||||
public function isPost(): bool
|
||||
{
|
||||
return $this->getMethod() === 'POST';
|
||||
}
|
||||
|
||||
public function isGet(): bool
|
||||
{
|
||||
return $this->getMethod() === 'GET';
|
||||
}
|
||||
|
||||
public function isAjax(): bool
|
||||
{
|
||||
return isset($this->headers['X-Requested-With']) &&
|
||||
$this->headers['X-Requested-With'] === 'XMLHttpRequest';
|
||||
}
|
||||
|
||||
public function getHeader(string $name): ?string
|
||||
{
|
||||
return $this->headers[$name] ?? null;
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function getIp(): string
|
||||
{
|
||||
return $this->server['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
}
|
||||
|
||||
public function getUserAgent(): string
|
||||
{
|
||||
return $this->server['HTTP_USER_AGENT'] ?? '';
|
||||
}
|
||||
|
||||
public function getContentType(): string
|
||||
{
|
||||
return $this->server['CONTENT_TYPE'] ?? '';
|
||||
}
|
||||
|
||||
public function getContentLength(): int
|
||||
{
|
||||
return (int) ($this->server['CONTENT_LENGTH'] ?? 0);
|
||||
}
|
||||
|
||||
public function validate(array $rules): array
|
||||
{
|
||||
$errors = [];
|
||||
$data = $this->all();
|
||||
|
||||
foreach ($rules as $field => $rule) {
|
||||
$value = $data[$field] ?? null;
|
||||
|
||||
if (strpos($rule, 'required') !== false && empty($value)) {
|
||||
$errors[$field][] = 'Das Feld ist erforderlich.';
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!empty($value)) {
|
||||
if (strpos($rule, 'email') !== false && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
$errors[$field][] = 'Ungültige E-Mail-Adresse.';
|
||||
}
|
||||
|
||||
if (strpos($rule, 'min:') !== false) {
|
||||
preg_match('/min:(\d+)/', $rule, $matches);
|
||||
$min = (int) $matches[1];
|
||||
if (strlen($value) < $min) {
|
||||
$errors[$field][] = "Mindestens {$min} Zeichen erforderlich.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'max:') !== false) {
|
||||
preg_match('/max:(\d+)/', $rule, $matches);
|
||||
$max = (int) $matches[1];
|
||||
if (strlen($value) > $max) {
|
||||
$errors[$field][] = "Maximal {$max} Zeichen erlaubt.";
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($rule, 'numeric') !== false && !is_numeric($value)) {
|
||||
$errors[$field][] = 'Nur Zahlen erlaubt.';
|
||||
}
|
||||
|
||||
if (strpos($rule, 'date') !== false && !strtotime($value)) {
|
||||
$errors[$field][] = 'Ungültiges Datum.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
private function getRequestHeaders(): array
|
||||
{
|
||||
$headers = [];
|
||||
|
||||
foreach ($this->server as $key => $value) {
|
||||
if (strpos($key, 'HTTP_') === 0) {
|
||||
$header = str_replace('_', '-', strtolower(substr($key, 5)));
|
||||
$headers[$header] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $headers;
|
||||
}
|
||||
}
|
||||
141
app/Core/Response.php
Normal file
141
app/Core/Response.php
Normal file
@@ -0,0 +1,141 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class Response
|
||||
{
|
||||
private string $content = '';
|
||||
private int $statusCode = 200;
|
||||
private array $headers = [];
|
||||
private string $contentType = 'text/html; charset=utf-8';
|
||||
|
||||
public function setContent(string $content): self
|
||||
{
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setStatusCode(int $statusCode): self
|
||||
{
|
||||
$this->statusCode = $statusCode;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getStatusCode(): int
|
||||
{
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
public function setHeader(string $name, string $value): self
|
||||
{
|
||||
$this->headers[$name] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setHeaders(array $headers): self
|
||||
{
|
||||
$this->headers = array_merge($this->headers, $headers);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHeaders(): array
|
||||
{
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
public function setContentType(string $contentType): self
|
||||
{
|
||||
$this->contentType = $contentType;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContentType(): string
|
||||
{
|
||||
return $this->contentType;
|
||||
}
|
||||
|
||||
public function redirect(string $url, int $statusCode = 302): self
|
||||
{
|
||||
$this->setHeader('Location', $url);
|
||||
$this->setStatusCode($statusCode);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function json(array $data, int $statusCode = 200): self
|
||||
{
|
||||
$this->setContentType('application/json');
|
||||
$this->setContent(json_encode($data, JSON_UNESCAPED_UNICODE));
|
||||
$this->setStatusCode($statusCode);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function download(string $filePath, string $filename = null): self
|
||||
{
|
||||
if (!file_exists($filePath)) {
|
||||
throw new \Exception('File not found: ' . $filePath);
|
||||
}
|
||||
|
||||
$filename = $filename ?: basename($filePath);
|
||||
$mimeType = mime_content_type($filePath) ?: 'application/octet-stream';
|
||||
|
||||
$this->setHeader('Content-Type', $mimeType);
|
||||
$this->setHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
|
||||
$this->setHeader('Content-Length', (string) filesize($filePath));
|
||||
$this->setHeader('Cache-Control', 'no-cache, must-revalidate');
|
||||
$this->setHeader('Pragma', 'no-cache');
|
||||
|
||||
$this->setContent(file_get_contents($filePath));
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function send(): void
|
||||
{
|
||||
// Set status code
|
||||
http_response_code($this->statusCode);
|
||||
|
||||
// Set content type
|
||||
header('Content-Type: ' . $this->contentType);
|
||||
|
||||
// Set additional headers
|
||||
foreach ($this->headers as $name => $value) {
|
||||
header($name . ': ' . $value);
|
||||
}
|
||||
|
||||
// Output content
|
||||
echo $this->content;
|
||||
exit;
|
||||
}
|
||||
|
||||
public function notFound(string $message = 'Seite nicht gefunden'): self
|
||||
{
|
||||
$this->setStatusCode(404);
|
||||
$this->setContent($message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function forbidden(string $message = 'Zugriff verweigert'): self
|
||||
{
|
||||
$this->setStatusCode(403);
|
||||
$this->setContent($message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function unauthorized(string $message = 'Nicht autorisiert'): self
|
||||
{
|
||||
$this->setStatusCode(401);
|
||||
$this->setContent($message);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function serverError(string $message = 'Interner Serverfehler'): self
|
||||
{
|
||||
$this->setStatusCode(500);
|
||||
$this->setContent($message);
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
108
app/Core/Router.php
Normal file
108
app/Core/Router.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class Router
|
||||
{
|
||||
private static ?Router $instance = null;
|
||||
private array $routes = [];
|
||||
private array $groups = [];
|
||||
|
||||
public static function getInstance(): Router
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::$instance = new self();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
public function get(string $path, array $handler, array $options = []): void
|
||||
{
|
||||
$this->addRoute('GET', $path, $handler, $options);
|
||||
}
|
||||
|
||||
public function post(string $path, array $handler, array $options = []): void
|
||||
{
|
||||
$this->addRoute('POST', $path, $handler, $options);
|
||||
}
|
||||
|
||||
public function put(string $path, array $handler, array $options = []): void
|
||||
{
|
||||
$this->addRoute('PUT', $path, $handler, $options);
|
||||
}
|
||||
|
||||
public function delete(string $path, array $handler, array $options = []): void
|
||||
{
|
||||
$this->addRoute('DELETE', $path, $handler, $options);
|
||||
}
|
||||
|
||||
public function group(array $attributes, callable $callback): void
|
||||
{
|
||||
$this->groups[] = $attributes;
|
||||
$callback($this);
|
||||
array_pop($this->groups);
|
||||
}
|
||||
|
||||
private function addRoute(string $method, string $path, array $handler, array $options = []): void
|
||||
{
|
||||
// Apply group middleware
|
||||
$middleware = $options['middleware'] ?? [];
|
||||
foreach ($this->groups as $group) {
|
||||
if (isset($group['middleware'])) {
|
||||
$middleware = array_merge($middleware, (array)$group['middleware']);
|
||||
}
|
||||
}
|
||||
|
||||
$route = [
|
||||
'method' => $method,
|
||||
'path' => $path,
|
||||
'controller' => $handler[0],
|
||||
'method' => $handler[1],
|
||||
'middleware' => $middleware,
|
||||
'pattern' => $this->buildPattern($path),
|
||||
];
|
||||
|
||||
$this->routes[] = $route;
|
||||
}
|
||||
|
||||
public function match(string $method, string $path): ?array
|
||||
{
|
||||
foreach ($this->routes as $route) {
|
||||
if ($route['method'] !== $method) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (preg_match($route['pattern'], $path, $matches)) {
|
||||
// Extract parameters
|
||||
$params = [];
|
||||
preg_match_all('/\{([^}]+)\}/', $route['path'], $paramNames);
|
||||
|
||||
for ($i = 0; $i < count($paramNames[1]); $i++) {
|
||||
$paramName = $paramNames[1][$i];
|
||||
$params[$paramName] = $matches[$i + 1] ?? null;
|
||||
}
|
||||
|
||||
return [
|
||||
'controller' => $route['controller'],
|
||||
'method' => $route['method'],
|
||||
'middleware' => $route['middleware'],
|
||||
'params' => $params,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function buildPattern(string $path): string
|
||||
{
|
||||
// Convert route parameters to regex pattern
|
||||
$pattern = preg_replace('/\{([^}]+)\}/', '([^/]+)', $path);
|
||||
return '#^' . $pattern . '$#';
|
||||
}
|
||||
|
||||
public function getRoutes(): array
|
||||
{
|
||||
return $this->routes;
|
||||
}
|
||||
}
|
||||
198
app/Core/Session.php
Normal file
198
app/Core/Session.php
Normal file
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
namespace App\Core;
|
||||
|
||||
class Session
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
}
|
||||
|
||||
public function set(string $key, $value): void
|
||||
{
|
||||
$_SESSION[$key] = $value;
|
||||
}
|
||||
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
return $_SESSION[$key] ?? $default;
|
||||
}
|
||||
|
||||
public function has(string $key): bool
|
||||
{
|
||||
return isset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
public function remove(string $key): void
|
||||
{
|
||||
unset($_SESSION[$key]);
|
||||
}
|
||||
|
||||
public function destroy(): void
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
public function regenerate(): bool
|
||||
{
|
||||
return session_regenerate_id(true);
|
||||
}
|
||||
|
||||
public function flash(string $key, $value): void
|
||||
{
|
||||
$_SESSION['flash'][$key] = $value;
|
||||
}
|
||||
|
||||
public function getFlash(string $key, $default = null)
|
||||
{
|
||||
$value = $_SESSION['flash'][$key] ?? $default;
|
||||
unset($_SESSION['flash'][$key]);
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function hasFlash(string $key): bool
|
||||
{
|
||||
return isset($_SESSION['flash'][$key]);
|
||||
}
|
||||
|
||||
public function getFlashMessages(): array
|
||||
{
|
||||
$messages = $_SESSION['flash'] ?? [];
|
||||
unset($_SESSION['flash']);
|
||||
return $messages;
|
||||
}
|
||||
|
||||
public function setUser(array $user): void
|
||||
{
|
||||
$this->set('user', $user);
|
||||
$this->regenerate();
|
||||
}
|
||||
|
||||
public function getUser(): ?array
|
||||
{
|
||||
return $this->get('user');
|
||||
}
|
||||
|
||||
public function isLoggedIn(): bool
|
||||
{
|
||||
return $this->has('user');
|
||||
}
|
||||
|
||||
public function logout(): void
|
||||
{
|
||||
$this->remove('user');
|
||||
$this->destroy();
|
||||
}
|
||||
|
||||
public function getUserId(): ?int
|
||||
{
|
||||
$user = $this->getUser();
|
||||
return $user['id'] ?? null;
|
||||
}
|
||||
|
||||
public function getUserRole(): ?string
|
||||
{
|
||||
$user = $this->getUser();
|
||||
return $user['role'] ?? null;
|
||||
}
|
||||
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->getUserRole() === 'admin';
|
||||
}
|
||||
|
||||
public function isAuditor(): bool
|
||||
{
|
||||
return $this->getUserRole() === 'auditor';
|
||||
}
|
||||
|
||||
public function isEmployee(): bool
|
||||
{
|
||||
return $this->getUserRole() === 'employee';
|
||||
}
|
||||
|
||||
public function generateCsrfToken(): string
|
||||
{
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$this->set('csrf_token', $token);
|
||||
$this->set('csrf_token_time', time());
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function validateCsrfToken(string $token): bool
|
||||
{
|
||||
$storedToken = $this->get('csrf_token');
|
||||
$tokenTime = $this->get('csrf_token_time', 0);
|
||||
|
||||
if (!$storedToken || !$tokenTime) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if token is expired (1 hour)
|
||||
if (time() - $tokenTime > CSRF_TOKEN_LIFETIME) {
|
||||
$this->remove('csrf_token');
|
||||
$this->remove('csrf_token_time');
|
||||
return false;
|
||||
}
|
||||
|
||||
return hash_equals($storedToken, $token);
|
||||
}
|
||||
|
||||
public function getCsrfToken(): string
|
||||
{
|
||||
$token = $this->get('csrf_token');
|
||||
if (!$token) {
|
||||
$token = $this->generateCsrfToken();
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
public function setLocale(string $locale): void
|
||||
{
|
||||
$this->set('locale', $locale);
|
||||
}
|
||||
|
||||
public function getLocale(): string
|
||||
{
|
||||
return $this->get('locale', 'de');
|
||||
}
|
||||
|
||||
public function setLastActivity(): void
|
||||
{
|
||||
$this->set('last_activity', time());
|
||||
}
|
||||
|
||||
public function isExpired(int $timeout = 3600): bool
|
||||
{
|
||||
$lastActivity = $this->get('last_activity', 0);
|
||||
return (time() - $lastActivity) > $timeout;
|
||||
}
|
||||
|
||||
public function setLoginAttempts(int $attempts): void
|
||||
{
|
||||
$this->set('login_attempts', $attempts);
|
||||
$this->set('login_attempts_time', time());
|
||||
}
|
||||
|
||||
public function getLoginAttempts(): int
|
||||
{
|
||||
$attempts = $this->get('login_attempts', 0);
|
||||
$attemptsTime = $this->get('login_attempts_time', 0);
|
||||
|
||||
// Reset attempts if lockout time has passed
|
||||
if (time() - $attemptsTime > LOGIN_LOCKOUT_TIME) {
|
||||
$this->setLoginAttempts(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $attempts;
|
||||
}
|
||||
|
||||
public function isLockedOut(): bool
|
||||
{
|
||||
return $this->getLoginAttempts() >= LOGIN_MAX_ATTEMPTS;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user