add user management
This commit is contained in:
@@ -17,6 +17,7 @@ security:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
user_checker: App\Security\ActiveUserChecker
|
||||
access_denied_handler: App\Security\AccessDeniedHandler
|
||||
context: retriex_user_area
|
||||
|
||||
form_login:
|
||||
@@ -39,6 +40,7 @@ security:
|
||||
lazy: true
|
||||
provider: app_user_provider
|
||||
user_checker: App\Security\ActiveUserChecker
|
||||
access_denied_handler: App\Security\AccessDeniedHandler
|
||||
context: retriex_user_area
|
||||
|
||||
form_login:
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
# RetrieX Patch p94 - Error Pages and Access Denied UX
|
||||
|
||||
## Ziel
|
||||
|
||||
Dieser Patch ergänzt benutzerfreundliche Fehlerseiten und behandelt insbesondere den Fall, dass ein bereits eingeloggter Benutzer in einen Bereich wechselt, für den ihm die passende Rolle fehlt.
|
||||
|
||||
Beispiel:
|
||||
|
||||
- Benutzer ist im Chat angemeldet, besitzt aber keine `ROLE_ADMIN_AREA`.
|
||||
- Benutzer öffnet `/admin` oder `/admin/login`.
|
||||
- Statt Symfony-Default-403 oder verwirrender Login-Seite erscheint eine klare Fehlerseite mit benötigter Rolle und Abmelde-Option.
|
||||
|
||||
## Enthaltene Änderungen
|
||||
|
||||
Neue Dateien:
|
||||
|
||||
- `src/Security/AccessDeniedHandler.php`
|
||||
- `src/Security/AccessDeniedPageRenderer.php`
|
||||
- `templates/error/layout.html.twig`
|
||||
- `templates/error/access_denied.html.twig`
|
||||
- `templates/bundles/TwigBundle/Exception/error403.html.twig`
|
||||
- `templates/bundles/TwigBundle/Exception/error404.html.twig`
|
||||
- `templates/bundles/TwigBundle/Exception/error500.html.twig`
|
||||
- `templates/bundles/TwigBundle/Exception/error.html.twig`
|
||||
|
||||
Geänderte Dateien:
|
||||
|
||||
- `config/packages/security.yaml`
|
||||
- `src/Controller/Admin/SecurityController.php`
|
||||
- `src/Controller/Chat/SecurityController.php`
|
||||
|
||||
## Verhalten
|
||||
|
||||
### 403 / fehlende Rolle
|
||||
|
||||
Der neue `AccessDeniedHandler` rendert eine konsistente 403-Seite für Admin- und Chat-Firewalls.
|
||||
|
||||
Die Seite zeigt:
|
||||
|
||||
- Bereich, z. B. `Adminbereich` oder `Chatbereich`
|
||||
- benötigte Rolle, z. B. `ROLE_ADMIN_AREA` oder `ROLE_CHAT_USER`
|
||||
- angemeldeten Benutzer
|
||||
- Link zurück in einen Bereich, für den der Benutzer Rechte hat
|
||||
- Abmelde-Link, um sich mit einem anderen Benutzer anzumelden
|
||||
|
||||
### Login mit falschem Bereich
|
||||
|
||||
Die Login-Controller behandeln nun explizit den Fall:
|
||||
|
||||
- Benutzer ist bereits authentifiziert
|
||||
- Benutzer hat aber nicht die Zielrolle des Loginbereichs
|
||||
|
||||
Dann wird direkt die 403-Seite gerendert, statt erneut die Login-Maske zu zeigen.
|
||||
|
||||
### Generische Fehlerseiten
|
||||
|
||||
Symfony/Twig Exception-Templates wurden ergänzt für:
|
||||
|
||||
- `403`
|
||||
- `404`
|
||||
- `500`
|
||||
- generische Fehler
|
||||
|
||||
Diese greifen insbesondere im produktiven Fehlerhandling. Für Access-Denied-Fälle greift zusätzlich der Security-Handler auch unabhängig vom generischen Exception-Template.
|
||||
|
||||
## Security-Konfiguration
|
||||
|
||||
Beide Firewalls nutzen nun denselben Handler:
|
||||
|
||||
```yaml
|
||||
admin:
|
||||
access_denied_handler: App\Security\AccessDeniedHandler
|
||||
|
||||
chat:
|
||||
access_denied_handler: App\Security\AccessDeniedHandler
|
||||
```
|
||||
|
||||
## Nicht geändert
|
||||
|
||||
Keine Änderungen an:
|
||||
|
||||
- `AgentRunner`
|
||||
- Retrieval
|
||||
- Scoring
|
||||
- Shop-Matching
|
||||
- PromptBuilder
|
||||
- RAG-/Commerce-YAMLs
|
||||
- User-CRUD-Fachlogik aus p93
|
||||
|
||||
## Lokale Checks
|
||||
|
||||
Ausgeführt:
|
||||
|
||||
```bash
|
||||
php -l src/Security/AccessDeniedPageRenderer.php
|
||||
php -l src/Security/AccessDeniedHandler.php
|
||||
php -l src/Controller/Admin/SecurityController.php
|
||||
php -l src/Controller/Chat/SecurityController.php
|
||||
python3 -c "import yaml; yaml.safe_load(open('config/packages/security.yaml'))"
|
||||
```
|
||||
|
||||
Symfony-Console-Checks konnten lokal nicht ausgeführt werden, weil `vendor/` im ZIP nicht enthalten ist.
|
||||
|
||||
## Empfohlene Prüfungen in der Zielumgebung
|
||||
|
||||
```bash
|
||||
php bin/console lint:yaml config/packages/security.yaml
|
||||
php bin/console lint:twig templates/error templates/bundles/TwigBundle/Exception
|
||||
php bin/console cache:clear
|
||||
php bin/console debug:container App\Security\AccessDeniedHandler
|
||||
php bin/console debug:router | grep -E 'admin_login|chat_login|admin_dashboard|chat_index'
|
||||
php bin/console mto:agent:config:validate
|
||||
php bin/console mto:agent:regression:test
|
||||
php bin/console mto:agent:config:audit-source --details
|
||||
```
|
||||
|
||||
## Manuelle Smoke-Tests
|
||||
|
||||
1. Als reiner `ROLE_CHAT_USER` einloggen und `/admin` öffnen.
|
||||
- Erwartung: 403-Seite mit benötigter Rolle `ROLE_ADMIN_AREA`.
|
||||
|
||||
2. Als reiner `ROLE_ADMIN_AREA` ohne `ROLE_CHAT_USER` `/chat` öffnen.
|
||||
- Erwartung: 403-Seite mit benötigter Rolle `ROLE_CHAT_USER`.
|
||||
|
||||
3. Als deaktivierter User einloggen.
|
||||
- Erwartung: Login wird weiterhin durch p93 `ActiveUserChecker` blockiert.
|
||||
|
||||
4. Nicht existente Route öffnen, z. B. `/admin/foo-does-not-exist`.
|
||||
- Erwartung: freundliche 404-Fehlerseite, sofern Exception-Handling im Prod-Modus aktiv ist.
|
||||
|
||||
5. Fehler im Prod-Modus provozieren.
|
||||
- Erwartung: freundliche 500-Seite mit Link zu System-Logs für Admin-User.
|
||||
@@ -315,6 +315,14 @@ body {
|
||||
background-color: #86b7fe !important;
|
||||
}
|
||||
|
||||
.btn-info{
|
||||
background-color: #86b7fe !important;
|
||||
}
|
||||
|
||||
.text-info{
|
||||
color: #86b7fe !important;
|
||||
}
|
||||
|
||||
.card.bg-black,
|
||||
.card.bg-dark {
|
||||
color: #e2e2e2;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Admin;
|
||||
|
||||
use App\Security\AccessDeniedPageRenderer;
|
||||
use App\Security\ApplicationRoles;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
@@ -11,13 +15,25 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
final class SecurityController extends AbstractController
|
||||
{
|
||||
#[Route('/admin/login', name: 'admin_login')]
|
||||
public function login(AuthenticationUtils $authUtils): Response
|
||||
{
|
||||
// Wenn bereits eingeloggt → direkt ins Dashboard
|
||||
if ($this->getUser() !== null && $this->isGranted('ROLE_ADMIN_AREA')) {
|
||||
public function login(
|
||||
Request $request,
|
||||
AuthenticationUtils $authUtils,
|
||||
AccessDeniedPageRenderer $accessDeniedPageRenderer,
|
||||
): Response {
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($user !== null && $this->isGranted(ApplicationRoles::ROLE_ADMIN_AREA)) {
|
||||
return $this->redirectToRoute('admin_dashboard');
|
||||
}
|
||||
|
||||
if ($user !== null) {
|
||||
return $accessDeniedPageRenderer->renderForbidden(
|
||||
$request,
|
||||
'admin',
|
||||
ApplicationRoles::ROLE_ADMIN_AREA,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render('admin/security/login.html.twig', [
|
||||
'last_username' => $authUtils->getLastUsername(),
|
||||
'error' => $authUtils->getLastAuthenticationError(),
|
||||
@@ -27,7 +43,7 @@ final class SecurityController extends AbstractController
|
||||
#[Route('/admin/logout', name: 'admin_logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
// Symfony interceptet diese Route, daher bleibt sie leer.
|
||||
// Symfony intercepts this route via the firewall logout configuration.
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Controller\Chat;
|
||||
|
||||
use App\Security\AccessDeniedPageRenderer;
|
||||
use App\Security\ApplicationRoles;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
@@ -19,12 +22,25 @@ use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
final class SecurityController extends AbstractController
|
||||
{
|
||||
#[Route('/chat/login', name: 'chat_login', methods: ['GET', 'POST'])]
|
||||
public function login(AuthenticationUtils $authUtils): Response
|
||||
{
|
||||
if ($this->getUser() !== null && $this->isGranted('ROLE_CHAT_USER')) {
|
||||
public function login(
|
||||
Request $request,
|
||||
AuthenticationUtils $authUtils,
|
||||
AccessDeniedPageRenderer $accessDeniedPageRenderer,
|
||||
): Response {
|
||||
$user = $this->getUser();
|
||||
|
||||
if ($user !== null && $this->isGranted(ApplicationRoles::ROLE_CHAT_USER)) {
|
||||
return $this->redirectToRoute('chat_index');
|
||||
}
|
||||
|
||||
if ($user !== null) {
|
||||
return $accessDeniedPageRenderer->renderForbidden(
|
||||
$request,
|
||||
'chat',
|
||||
ApplicationRoles::ROLE_CHAT_USER,
|
||||
);
|
||||
}
|
||||
|
||||
return $this->render('chat/security/login.html.twig', [
|
||||
'last_username' => $authUtils->getLastUsername(),
|
||||
'error' => $authUtils->getLastAuthenticationError(),
|
||||
@@ -34,6 +50,7 @@ final class SecurityController extends AbstractController
|
||||
#[Route('/chat/logout', name: 'chat_logout', methods: ['GET'])]
|
||||
public function logout(): void
|
||||
{
|
||||
// Symfony intercepts this route via the firewall logout configuration.
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
||||
|
||||
25
src/Security/AccessDeniedHandler.php
Normal file
25
src/Security/AccessDeniedHandler.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
|
||||
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
|
||||
|
||||
/**
|
||||
* Shows a user-friendly 403 page instead of the framework default access denied response.
|
||||
*/
|
||||
final readonly class AccessDeniedHandler implements AccessDeniedHandlerInterface
|
||||
{
|
||||
public function __construct(private AccessDeniedPageRenderer $renderer)
|
||||
{
|
||||
}
|
||||
|
||||
public function handle(Request $request, AccessDeniedException $accessDeniedException): ?Response
|
||||
{
|
||||
return $this->renderer->renderForbidden($request);
|
||||
}
|
||||
}
|
||||
153
src/Security/AccessDeniedPageRenderer.php
Normal file
153
src/Security/AccessDeniedPageRenderer.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Security;
|
||||
|
||||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Security\Core\User\UserInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Renders a consistent forbidden page for firewall access denials and login area mismatches.
|
||||
*/
|
||||
final readonly class AccessDeniedPageRenderer
|
||||
{
|
||||
public function __construct(
|
||||
private Environment $twig,
|
||||
private Security $security,
|
||||
) {
|
||||
}
|
||||
|
||||
public function renderForbidden(
|
||||
Request $request,
|
||||
?string $area = null,
|
||||
?string $requiredRole = null,
|
||||
?string $message = null,
|
||||
): Response {
|
||||
$context = $this->buildContext($request, $area, $requiredRole, $message);
|
||||
|
||||
return new Response(
|
||||
$this->twig->render('error/access_denied.html.twig', $context),
|
||||
Response::HTTP_FORBIDDEN,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildContext(
|
||||
Request $request,
|
||||
?string $area,
|
||||
?string $requiredRole,
|
||||
?string $message,
|
||||
): array {
|
||||
$areaKey = $area ?? $this->detectArea($request);
|
||||
$target = $this->targetContext($areaKey);
|
||||
$currentUser = $this->security->getUser();
|
||||
$fallbackLink = $this->fallbackLink($target['home_route']);
|
||||
|
||||
return [
|
||||
'status_code' => Response::HTTP_FORBIDDEN,
|
||||
'status_text' => 'Zugriff verweigert',
|
||||
'area_label' => $target['label'],
|
||||
'required_role' => $requiredRole ?? $target['required_role'],
|
||||
'login_route' => $target['login_route'],
|
||||
'logout_route' => $target['logout_route'],
|
||||
'home_route' => $fallbackLink['route'],
|
||||
'home_label' => $fallbackLink['label'],
|
||||
'current_user_identifier' => $currentUser instanceof UserInterface ? $currentUser->getUserIdentifier() : null,
|
||||
'message' => $message ?? $this->defaultMessage($currentUser instanceof UserInterface, $target['label']),
|
||||
];
|
||||
}
|
||||
|
||||
private function detectArea(Request $request): string
|
||||
{
|
||||
$path = $request->getPathInfo();
|
||||
|
||||
if (str_starts_with($path, '/admin')) {
|
||||
return 'admin';
|
||||
}
|
||||
|
||||
if (
|
||||
$path === '/'
|
||||
|| str_starts_with($path, '/chat')
|
||||
|| str_starts_with($path, '/ask-jobs')
|
||||
|| str_starts_with($path, '/ask-sse')
|
||||
|| str_starts_with($path, '/history')
|
||||
|| str_starts_with($path, '/chat-messages/frontend')
|
||||
) {
|
||||
return 'chat';
|
||||
}
|
||||
|
||||
return 'application';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{label: string, required_role: string, login_route: string|null, logout_route: string|null, home_route: string|null}
|
||||
*/
|
||||
private function targetContext(string $area): array
|
||||
{
|
||||
return match ($area) {
|
||||
'admin' => [
|
||||
'label' => 'Adminbereich',
|
||||
'required_role' => ApplicationRoles::ROLE_ADMIN_AREA,
|
||||
'login_route' => 'admin_login',
|
||||
'logout_route' => 'admin_logout',
|
||||
'home_route' => 'admin_dashboard',
|
||||
],
|
||||
'chat' => [
|
||||
'label' => 'Chatbereich',
|
||||
'required_role' => ApplicationRoles::ROLE_CHAT_USER,
|
||||
'login_route' => 'chat_login',
|
||||
'logout_route' => 'chat_logout',
|
||||
'home_route' => 'chat_index',
|
||||
],
|
||||
default => [
|
||||
'label' => 'Anwendung',
|
||||
'required_role' => ApplicationRoles::ROLE_USER,
|
||||
'login_route' => null,
|
||||
'logout_route' => null,
|
||||
'home_route' => null,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{route: string|null, label: string|null}
|
||||
*/
|
||||
private function fallbackLink(?string $targetHomeRoute): array
|
||||
{
|
||||
if ($targetHomeRoute === 'admin_dashboard' && $this->security->isGranted(ApplicationRoles::ROLE_ADMIN_AREA)) {
|
||||
return ['route' => 'admin_dashboard', 'label' => 'Zum Admin-Dashboard'];
|
||||
}
|
||||
|
||||
if ($targetHomeRoute === 'chat_index' && $this->security->isGranted(ApplicationRoles::ROLE_CHAT_USER)) {
|
||||
return ['route' => 'chat_index', 'label' => 'Zum Chat'];
|
||||
}
|
||||
|
||||
if ($this->security->isGranted(ApplicationRoles::ROLE_CHAT_USER)) {
|
||||
return ['route' => 'chat_index', 'label' => 'Zum Chat'];
|
||||
}
|
||||
|
||||
if ($this->security->isGranted(ApplicationRoles::ROLE_ADMIN_AREA)) {
|
||||
return ['route' => 'admin_dashboard', 'label' => 'Zum Admin-Dashboard'];
|
||||
}
|
||||
|
||||
return ['route' => null, 'label' => null];
|
||||
}
|
||||
|
||||
private function defaultMessage(bool $authenticated, string $areaLabel): string
|
||||
{
|
||||
if ($authenticated) {
|
||||
return sprintf(
|
||||
'Du bist angemeldet, aber dein Benutzerkonto hat nicht die notwendige Berechtigung für den %s.',
|
||||
$areaLabel,
|
||||
);
|
||||
}
|
||||
|
||||
return sprintf('Für den Zugriff auf den %s ist eine Anmeldung mit passender Rolle erforderlich.', $areaLabel);
|
||||
}
|
||||
}
|
||||
25
templates/bundles/TwigBundle/Exception/error.html.twig
Normal file
25
templates/bundles/TwigBundle/Exception/error.html.twig
Normal file
@@ -0,0 +1,25 @@
|
||||
{% extends 'error/layout.html.twig' %}
|
||||
|
||||
{% set status_code = status_code|default(500) %}
|
||||
|
||||
{% block title %}Fehler {{ status_code }}{% endblock %}
|
||||
{% block heading %}Ein Fehler ist aufgetreten{% endblock %}
|
||||
{% block message %}Die Anfrage konnte nicht verarbeitet werden. Falls der Fehler erneut auftritt, prüfe bitte die System-Logs.{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
{% if app.user and is_granted('ROLE_CHAT_USER') %}
|
||||
<a class="btn btn-info" href="{{ path('chat_index') }}">
|
||||
<i class="bi bi-chat-dots"></i> Zum Chat
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if app.user and is_granted('ROLE_ADMIN_AREA') %}
|
||||
<a class="btn btn-outline-light" href="{{ path('admin_dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Zum Adminbereich
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if not app.user %}
|
||||
<a class="btn btn-info" href="{{ path('chat_login') }}">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Zur Anmeldung
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,9 @@
|
||||
{% extends 'error/access_denied.html.twig' %}
|
||||
|
||||
{% set status_code = status_code|default(403) %}
|
||||
{% set area_label = area_label|default('Anwendung') %}
|
||||
{% set required_role = required_role|default('passende Berechtigung') %}
|
||||
{% set message = message|default('Für diese Seite fehlen deinem Benutzerkonto die notwendigen Rechte.') %}
|
||||
{% set current_user_identifier = current_user_identifier|default(app.user ? app.user.userIdentifier : null) %}
|
||||
{% set home_route = home_route|default(app.user and is_granted('ROLE_CHAT_USER') ? 'chat_index' : (app.user and is_granted('ROLE_ADMIN_AREA') ? 'admin_dashboard' : null)) %}
|
||||
{% set home_label = home_label|default(home_route == 'chat_index' ? 'Zum Chat' : (home_route == 'admin_dashboard' ? 'Zum Admin-Dashboard' : null)) %}
|
||||
29
templates/bundles/TwigBundle/Exception/error404.html.twig
Normal file
29
templates/bundles/TwigBundle/Exception/error404.html.twig
Normal file
@@ -0,0 +1,29 @@
|
||||
{% extends 'error/layout.html.twig' %}
|
||||
|
||||
{% set status_code = status_code|default(404) %}
|
||||
|
||||
{% block title %}Seite nicht gefunden · Fehler 404{% endblock %}
|
||||
{% block icon %}<i class="bi bi-signpost-split fs-3 text-info"></i>{% endblock %}
|
||||
{% block heading %}Seite nicht gefunden{% endblock %}
|
||||
{% block message %}Die angeforderte Seite existiert nicht oder wurde verschoben.{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
{% if app.user and is_granted('ROLE_CHAT_USER') %}
|
||||
<a class="btn btn-info" href="{{ path('chat_index') }}">
|
||||
<i class="bi bi-chat-dots"></i> Zum Chat
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if app.user and is_granted('ROLE_ADMIN_AREA') %}
|
||||
<a class="btn btn-outline-light" href="{{ path('admin_dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Zum Adminbereich
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if not app.user %}
|
||||
<a class="btn btn-info" href="{{ path('chat_login') }}">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Zur Chat-Anmeldung
|
||||
</a>
|
||||
<a class="btn btn-outline-light" href="{{ path('admin_login') }}">
|
||||
<i class="bi bi-shield-lock"></i> Zur Admin-Anmeldung
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
27
templates/bundles/TwigBundle/Exception/error500.html.twig
Normal file
27
templates/bundles/TwigBundle/Exception/error500.html.twig
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends 'error/layout.html.twig' %}
|
||||
|
||||
{% set status_code = status_code|default(500) %}
|
||||
|
||||
{% block title %}Systemfehler · Fehler 500{% endblock %}
|
||||
{% block icon %}<i class="bi bi-bug fs-3 text-danger"></i>{% endblock %}
|
||||
{% block heading %}Systemfehler{% endblock %}
|
||||
{% block message %}Es ist ein unerwarteter Fehler aufgetreten. Bitte versuche es erneut oder prüfe die System-Logs im Adminbereich.{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
{% if app.user and is_granted('ROLE_ADMIN_AREA') %}
|
||||
<a class="btn btn-info" href="{{ path('admin_system_logs_index') }}">
|
||||
<i class="bi bi-terminal"></i> System-Logs öffnen
|
||||
</a>
|
||||
<a class="btn btn-outline-light" href="{{ path('admin_dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Zum Adminbereich
|
||||
</a>
|
||||
{% elseif app.user and is_granted('ROLE_CHAT_USER') %}
|
||||
<a class="btn btn-info" href="{{ path('chat_index') }}">
|
||||
<i class="bi bi-chat-dots"></i> Zum Chat
|
||||
</a>
|
||||
{% else %}
|
||||
<a class="btn btn-info" href="{{ path('chat_login') }}">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Zur Chat-Anmeldung
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
50
templates/error/access_denied.html.twig
Normal file
50
templates/error/access_denied.html.twig
Normal file
@@ -0,0 +1,50 @@
|
||||
{% extends 'error/layout.html.twig' %}
|
||||
|
||||
{% block title %}Zugriff verweigert · Fehler {{ status_code|default(403) }}{% endblock %}
|
||||
|
||||
{% block icon %}<i class="bi bi-shield-lock fs-3 text-warning"></i>{% endblock %}
|
||||
|
||||
{% block heading %}Zugriff verweigert{% endblock %}
|
||||
|
||||
{% block message %}
|
||||
{{ message|default('Für diesen Bereich fehlen deinem Benutzerkonto die notwendigen Rechte.') }}
|
||||
{% endblock %}
|
||||
|
||||
{% block details %}
|
||||
<div class="alert bg-warning border-secondary text-dark mb-0">
|
||||
<div class="row g-3 small">
|
||||
<div class="col-md-6">
|
||||
<div class="text-dark">Bereich</div>
|
||||
<div class="fw-semibold">{{ area_label|default('Anwendung') }}</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-dark">Benötigte Rolle</div>
|
||||
<code class="text-dark">{{ required_role|default('ROLE_USER') }}</code>
|
||||
</div>
|
||||
{% if current_user_identifier|default(null) %}
|
||||
<div class="col-12">
|
||||
<div class="text-dark">Angemeldeter Benutzer</div>
|
||||
<div>{{ current_user_identifier }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
{% if home_route|default(null) and home_label|default(null) %}
|
||||
<a class="btn btn-info" href="{{ path(home_route) }}">
|
||||
<i class="bi bi-arrow-left-circle"></i> {{ home_label }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
{% if logout_route|default(null) %}
|
||||
<a class="btn btn-outline-warning" href="{{ path(logout_route) }}">
|
||||
<i class="bi bi-box-arrow-right"></i> Abmelden und anderen Benutzer verwenden
|
||||
</a>
|
||||
{% elseif login_route|default(null) %}
|
||||
<a class="btn btn-outline-light" href="{{ path(login_route) }}">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Zur Anmeldung
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
55
templates/error/layout.html.twig
Normal file
55
templates/error/layout.html.twig
Normal file
@@ -0,0 +1,55 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}Fehler {{ status_code|default(500) }}{% endblock %}</title>
|
||||
<link href="/assets/styles/bootstrap.min.css" rel="stylesheet"/>
|
||||
<link href="/assets/styles/base.css" rel="stylesheet"/>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<link rel="shortcut icon" href="https://www.mitho-media.de/media/fc/16/42/1667224106/favicon.ico?ts=1767609928">
|
||||
</head>
|
||||
<body class="bg-dark text-light min-vh-100 d-flex align-items-center">
|
||||
<div class="container py-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-lg-8 col-xl-7">
|
||||
<div class="card bg-black border-secondary text-light shadow-lg">
|
||||
<div class="card-body p-4 p-md-5">
|
||||
<div class="d-flex align-items-center gap-3 mb-4">
|
||||
<div class="rounded-circle border border-info d-flex align-items-center justify-content-center"
|
||||
style="width: 56px; height: 56px;">
|
||||
{% block icon %}<i class="bi bi-exclamation-triangle fs-3 text-info"></i>{% endblock %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-info small text-uppercase fw-semibold">
|
||||
Fehler {{ status_code|default(500) }}
|
||||
</div>
|
||||
<h1 class="h3 mb-0">{% block heading %}Ein Fehler ist aufgetreten{% endblock %}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="lead mb-4">{% block message %}Die Anfrage konnte nicht verarbeitet werden.{% endblock %}</p>
|
||||
|
||||
{% block details %}{% endblock %}
|
||||
|
||||
<div class="d-flex flex-wrap gap-2 mt-4">
|
||||
{% block actions %}
|
||||
<a class="btn btn-info" href="{{ path('chat_index') }}">
|
||||
<i class="bi bi-chat-dots"></i> Zum Chat
|
||||
</a>
|
||||
<a class="btn btn-outline-light" href="{{ path('admin_dashboard') }}">
|
||||
<i class="bi bi-speedometer2"></i> Zum Adminbereich
|
||||
</a>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center text-secondary small mt-3">
|
||||
RAG-System · powered by mitho®
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user