add chat user roles and login
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
This patch intentionally removes public/index.html.
|
Important when applying this patch manually:
|
||||||
|
|
||||||
Reason:
|
Delete public/index.html from the target installation.
|
||||||
If public/index.html remains in the document root, many web servers serve it before public/index.php and the Symfony route / will not reach App\Controller\Chat\ChatController.
|
|
||||||
|
|
||||||
When applying this patch manually, delete:
|
The chat root route is now handled by Symfony via App\Controller\Chat\ChatController.
|
||||||
public/index.html
|
If public/index.html remains on disk, the web server may still serve the static file
|
||||||
|
before Symfony handles /, which would bypass the chat firewall and ROLE_CHAT_USER check.
|
||||||
|
|||||||
@@ -11,11 +11,12 @@ security:
|
|||||||
|
|
||||||
firewalls:
|
firewalls:
|
||||||
|
|
||||||
# 🔐 Admin zuerst!
|
# Admin area: same user provider, separate /admin route space.
|
||||||
admin:
|
admin:
|
||||||
pattern: ^/admin
|
pattern: ^/admin
|
||||||
lazy: true
|
lazy: true
|
||||||
provider: app_user_provider
|
provider: app_user_provider
|
||||||
|
context: retriex_user_area
|
||||||
|
|
||||||
form_login:
|
form_login:
|
||||||
login_path: admin_login
|
login_path: admin_login
|
||||||
@@ -31,17 +32,49 @@ security:
|
|||||||
lifetime: 604800
|
lifetime: 604800
|
||||||
path: /admin
|
path: /admin
|
||||||
|
|
||||||
# 🌍 Alles andere ist public (Chat etc.)
|
# Chat area: same user provider, separate route space and role gate.
|
||||||
|
chat:
|
||||||
|
pattern: ^/(?:$|chat(?:/|$)|ask-jobs(?:/|$)|ask-sse(?:/|$)|history(?:/|$)|chat-messages/frontend$)
|
||||||
|
lazy: true
|
||||||
|
provider: app_user_provider
|
||||||
|
context: retriex_user_area
|
||||||
|
|
||||||
|
form_login:
|
||||||
|
login_path: chat_login
|
||||||
|
check_path: chat_login
|
||||||
|
default_target_path: chat_index
|
||||||
|
|
||||||
|
logout:
|
||||||
|
path: chat_logout
|
||||||
|
target: chat_login
|
||||||
|
|
||||||
|
remember_me:
|
||||||
|
secret: '%kernel.secret%'
|
||||||
|
lifetime: 604800
|
||||||
|
path: /
|
||||||
|
|
||||||
|
# Everything outside Admin and Chat remains public/static.
|
||||||
main:
|
main:
|
||||||
pattern: ^/
|
pattern: ^/
|
||||||
security: false
|
security: false
|
||||||
|
|
||||||
role_hierarchy:
|
role_hierarchy:
|
||||||
ROLE_SUPER_ADMIN: [ROLE_KNOWLEDGE_ADMIN, ROLE_EDITOR, ROLE_USER]
|
ROLE_SUPER_ADMIN: [ROLE_KNOWLEDGE_ADMIN, ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
ROLE_KNOWLEDGE_ADMIN: [ROLE_EDITOR, ROLE_USER]
|
ROLE_KNOWLEDGE_ADMIN: [ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
ROLE_EDITOR: [ROLE_USER]
|
ROLE_EDITOR: [ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
|
ROLE_ADMIN_AREA: [ROLE_USER]
|
||||||
|
ROLE_CHAT_USER: [ROLE_USER]
|
||||||
|
|
||||||
access_control:
|
access_control:
|
||||||
- { path: ^/admin/login$, roles: PUBLIC_ACCESS }
|
- { path: ^/admin/login$, roles: PUBLIC_ACCESS }
|
||||||
- { path: ^/admin/logout$, roles: PUBLIC_ACCESS }
|
- { path: ^/admin/logout$, roles: PUBLIC_ACCESS }
|
||||||
- { path: ^/admin, roles: ROLE_USER }
|
- { path: ^/admin, roles: ROLE_ADMIN_AREA }
|
||||||
|
|
||||||
|
- { path: ^/chat/login$, roles: PUBLIC_ACCESS }
|
||||||
|
- { path: ^/chat/logout$, roles: PUBLIC_ACCESS }
|
||||||
|
- { path: ^/$, roles: ROLE_CHAT_USER }
|
||||||
|
- { path: ^/chat$, roles: ROLE_CHAT_USER }
|
||||||
|
- { path: ^/ask-jobs, roles: ROLE_CHAT_USER }
|
||||||
|
- { path: ^/ask-sse, roles: ROLE_CHAT_USER }
|
||||||
|
- { path: ^/history, roles: ROLE_CHAT_USER }
|
||||||
|
- { path: ^/chat-messages/frontend$, roles: ROLE_CHAT_USER }
|
||||||
|
|||||||
156
patch_history/RETRIEX_PATCH_88_CHAT_SECURITY_ROLES_README.md
Normal file
156
patch_history/RETRIEX_PATCH_88_CHAT_SECURITY_ROLES_README.md
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
# RetrieX Patch p88 - Chat Security Roles
|
||||||
|
|
||||||
|
## Zweck
|
||||||
|
|
||||||
|
p88 fuehrt eine getrennte Rollen-/Security-Schicht fuer den Chat ein, ohne eine zweite Userverwaltung anzulegen. Chat und Admin nutzen weiterhin denselben Symfony-User-Provider (`App\Entity\User`), werden aber ueber unterschiedliche Bereichsrollen freigeschaltet.
|
||||||
|
|
||||||
|
Der Patch baut auf p87 auf: Der Chat wird bereits ueber einen eigenen Symfony-Controller gerendert. p88 schuetzt diesen Chat-Einstieg und die zugehoerigen Chat-API-Endpunkte nun mit `ROLE_CHAT_USER`.
|
||||||
|
|
||||||
|
## Architekturentscheidung
|
||||||
|
|
||||||
|
Die Userverwaltung bleibt zentral:
|
||||||
|
|
||||||
|
- Entity: `App\Entity\User`
|
||||||
|
- Provider: `app_user_provider`
|
||||||
|
- Rollenfeld: bestehendes JSON-Feld `roles`
|
||||||
|
- Command: bestehender `mto:agent:user:create`
|
||||||
|
|
||||||
|
Die Bereichsberechtigungen werden getrennt:
|
||||||
|
|
||||||
|
- `ROLE_CHAT_USER` fuer Chat und Chat-API-Endpunkte
|
||||||
|
- `ROLE_ADMIN_AREA` fuer den Adminbereich
|
||||||
|
- `ROLE_USER` bleibt nur technische Basisrolle fuer eingeloggte User
|
||||||
|
|
||||||
|
Wichtig: `ROLE_USER` ist keine Adminberechtigung mehr. Das ist noetig, weil `User::getRoles()` jedem User automatisch `ROLE_USER` gibt.
|
||||||
|
|
||||||
|
## Aenderungen
|
||||||
|
|
||||||
|
### `config/packages/security.yaml`
|
||||||
|
|
||||||
|
- Admin-Firewall bleibt auf `^/admin` begrenzt.
|
||||||
|
- Neue Chat-Firewall fuer:
|
||||||
|
- `/`
|
||||||
|
- `/chat`
|
||||||
|
- `/chat/login`
|
||||||
|
- `/chat/logout`
|
||||||
|
- `/ask-jobs...`
|
||||||
|
- `/ask-sse...`
|
||||||
|
- `/history...`
|
||||||
|
- `/chat-messages/frontend`
|
||||||
|
- Beide Firewalls verwenden denselben Provider `app_user_provider`.
|
||||||
|
- Beide Firewalls verwenden denselben Security-Context `retriex_user_area`, damit die zentrale Userverwaltung und Session sauber gemeinsam genutzt werden koennen.
|
||||||
|
- Adminzugriff verlangt jetzt `ROLE_ADMIN_AREA` statt `ROLE_USER`.
|
||||||
|
- Chatzugriff verlangt `ROLE_CHAT_USER`.
|
||||||
|
|
||||||
|
### Neue Rollenlogik
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ROLE_SUPER_ADMIN: [ROLE_KNOWLEDGE_ADMIN, ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
|
ROLE_KNOWLEDGE_ADMIN: [ROLE_EDITOR, ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
|
ROLE_EDITOR: [ROLE_ADMIN_AREA, ROLE_CHAT_USER, ROLE_USER]
|
||||||
|
ROLE_ADMIN_AREA: [ROLE_USER]
|
||||||
|
ROLE_CHAT_USER: [ROLE_USER]
|
||||||
|
```
|
||||||
|
|
||||||
|
Damit gilt:
|
||||||
|
|
||||||
|
- Chat-only User: `ROLE_CHAT_USER`
|
||||||
|
- Admin-only User: `ROLE_ADMIN_AREA`
|
||||||
|
- Editor/Knowledge/Super Admins behalten Adminzugriff und bekommen Chatzugriff ueber Hierarchie
|
||||||
|
- `ROLE_USER` alleine reicht fuer keinen Bereich
|
||||||
|
|
||||||
|
### Neuer Chat-Login
|
||||||
|
|
||||||
|
Neue Dateien:
|
||||||
|
|
||||||
|
- `src/Controller/Chat/SecurityController.php`
|
||||||
|
- `templates/chat/security/login.html.twig`
|
||||||
|
|
||||||
|
Routen:
|
||||||
|
|
||||||
|
- `/chat/login`
|
||||||
|
- `/chat/logout`
|
||||||
|
|
||||||
|
Der Controller liegt bewusst unter `App\Controller\Chat`, nicht unter `App\Controller\Admin`.
|
||||||
|
|
||||||
|
### Admin-Login-Hardening
|
||||||
|
|
||||||
|
`src/Controller/Admin/SecurityController.php` leitet eingeloggte User nur noch direkt zum Admin-Dashboard weiter, wenn sie `ROLE_ADMIN_AREA` haben. Ein reiner Chat-User wird dadurch nicht versehentlich in eine Admin-Weiterleitung geschickt.
|
||||||
|
|
||||||
|
### Admin-Endpoint-Hardening
|
||||||
|
|
||||||
|
`src/Controller/Admin/IngestJobController.php` nutzt fuer den Status-Endpunkt jetzt `ROLE_ADMIN_AREA` statt `ROLE_USER`.
|
||||||
|
|
||||||
|
### User-Create-Command
|
||||||
|
|
||||||
|
`src/Command/CreateUserCommand.php` wurde angepasst:
|
||||||
|
|
||||||
|
- Beschreibung neutralisiert: erstellt nun einen Application-User, nicht nur Admin-User
|
||||||
|
- Rollenauswahl erweitert um `ROLE_ADMIN_AREA` und `ROLE_CHAT_USER`
|
||||||
|
- `ROLE_USER` aus der Auswahl entfernt, weil diese Rolle automatisch gesetzt wird und alleine keinen Bereich freischalten soll
|
||||||
|
|
||||||
|
## Bewusst nicht geaendert
|
||||||
|
|
||||||
|
- Keine neue User-Entity
|
||||||
|
- Keine neue User-Tabelle
|
||||||
|
- Keine neue Migration
|
||||||
|
- Keine Aenderung an RAG, Retrieval, Scoring, Ranking, Shop-Matching oder Prompt-Logik
|
||||||
|
- Keine Verschiebung bestehender Ask-/SSE-/History-Controller
|
||||||
|
- Keine Aenderung an `public/assets/js/base.js`
|
||||||
|
- Keine Aenderung an Chat-Frontend-Verhalten ausser Loginpflicht
|
||||||
|
|
||||||
|
## Hinweis zu `public/index.html`
|
||||||
|
|
||||||
|
Wie in p87 muss `public/index.html` geloescht sein. Wenn die Datei liegen bleibt, kann der Webserver `/` weiterhin statisch bedienen und Symfony samt Chat-Firewall umgehen.
|
||||||
|
|
||||||
|
Im Full-ZIP ist `public/index.html` entfernt. Im Patch-only-ZIP liegt zusaetzlich `DELETE_PUBLIC_INDEX_HTML.txt` als manueller Hinweis.
|
||||||
|
|
||||||
|
## Lokale Checks
|
||||||
|
|
||||||
|
Ausgefuehrt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php -l src/Controller/Chat/SecurityController.php
|
||||||
|
php -l src/Controller/Chat/ChatController.php
|
||||||
|
php -l src/Controller/Admin/SecurityController.php
|
||||||
|
php -l src/Controller/Admin/IngestJobController.php
|
||||||
|
php -l src/Command/CreateUserCommand.php
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import yaml
|
||||||
|
data = yaml.safe_load(Path('config/packages/security.yaml').read_text())
|
||||||
|
assert data['security']['access_control'][2]['roles'] == 'ROLE_ADMIN_AREA'
|
||||||
|
assert data['security']['access_control'][5]['roles'] == 'ROLE_CHAT_USER'
|
||||||
|
assert not Path('public/index.html').exists()
|
||||||
|
print('p88 structural checks OK')
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
|
||||||
|
Ergebnis:
|
||||||
|
|
||||||
|
- PHP lint OK
|
||||||
|
- YAML parse OK
|
||||||
|
- Adminbereich ist nicht mehr ueber `ROLE_USER` freigeschaltet
|
||||||
|
- Chatbereich ist ueber `ROLE_CHAT_USER` freigeschaltet
|
||||||
|
- `public/index.html` ist im Full-ZIP entfernt
|
||||||
|
|
||||||
|
## Noch in Zielumgebung ausfuehren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
php bin/console cache:clear
|
||||||
|
php bin/console debug:router | grep -E 'chat_login|chat_logout|chat_index|admin_login|admin_dashboard'
|
||||||
|
php bin/console debug:config security
|
||||||
|
php bin/console mto:agent:config:validate
|
||||||
|
php bin/console mto:agent:regression:test
|
||||||
|
php bin/console mto:agent:config:audit-source --details
|
||||||
|
php bin/console mto:agent:config:audit-patterns --details
|
||||||
|
```
|
||||||
|
|
||||||
|
Manuelle Smoke-Tests:
|
||||||
|
|
||||||
|
1. Ohne Login `/` aufrufen: Redirect zu `/chat/login`.
|
||||||
|
2. User mit `ROLE_CHAT_USER` anlegen und einloggen: `/` und `/chat` oeffnen den Chat.
|
||||||
|
3. Chat-User ruft `/admin` auf: kein Adminzugriff.
|
||||||
|
4. Bestehender `ROLE_SUPER_ADMIN`, `ROLE_KNOWLEDGE_ADMIN` oder `ROLE_EDITOR` kann weiterhin `/admin` nutzen.
|
||||||
|
5. Admin-/Editor-User kann den Chat nutzen.
|
||||||
|
6. `/ask-jobs`, `/ask-sse/{jobId}`, `/history`, `/history/delete`, `/chat-messages/frontend` funktionieren nach Chat-Login.
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="de">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title></title>
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
|
|
||||||
<!-- Markdown + Sanitizer -->
|
|
||||||
<link href="/assets/styles/bootstrap.min.css" rel="stylesheet"/>
|
|
||||||
<link rel="stylesheet" href="/assets/styles/base.css">
|
|
||||||
<link rel="shortcut icon" href="https://www.mitho-media.de/media/fc/16/42/1667224106/favicon.ico?ts=1767609928">
|
|
||||||
|
|
||||||
<script src="/assets/js/bootstrap.bundle.min.js"></script>
|
|
||||||
<script src="/assets/js/marked.min.js"></script>
|
|
||||||
<script src="/assets/js/purify.min.js"></script>
|
|
||||||
<script src="/assets/js/base.js"></script>
|
|
||||||
</head>
|
|
||||||
<body class="bg-black">
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div class="d-flex">
|
|
||||||
<img src="/assets/img/logo.png" style="max-height: 20px;">
|
|
||||||
</div>
|
|
||||||
<div class="text-info fw-bold" style="font-size: 12px" data-chat-message-text="ui.header_title"></div>
|
|
||||||
</div>
|
|
||||||
<img src="/assets/img/logo.svg" style="max-height: 20px;">
|
|
||||||
<div class="spacer"></div>
|
|
||||||
|
|
||||||
<button id="clear" class="btn btn-trans" data-chat-message-text="ui.buttons.clear"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-trash-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M2.5 1a1 1 0 0 0-1 1v1a1 1 0 0 0 1 1H3v9a2 2 0 0 0 2 2h6a2 2 0 0 0 2-2V4h.5a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1H10a1 1 0 0 0-1-1H7a1 1 0 0 0-1 1zm3 4a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 .5-.5M8 5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-1 0v-7A.5.5 0 0 1 8 5m3 .5v7a.5.5 0 0 1-1 0v-7a.5.5 0 0 1 1 0"/>
|
|
||||||
</svg></button>
|
|
||||||
</div>
|
|
||||||
<div id="ai-cloud" class="ai-cloud d-none"></div>
|
|
||||||
<div id="chat" class="chat"></div>
|
|
||||||
|
|
||||||
<div id="retriex-chat-options" class="retriex-chat-options p-2" data-chat-message-aria-label="ui.options.aria_label">
|
|
||||||
<label class="retriex-option-toggle" for="toggle-retriex-cards">
|
|
||||||
<input id="toggle-retriex-cards" type="checkbox">
|
|
||||||
<span data-chat-message-text="ui.options.status_info"></span>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-area">
|
|
||||||
<textarea id="prompt" class="form-control bg-dark" data-chat-message-placeholder="ui.input.prompt_placeholder"></textarea>
|
|
||||||
<button id="send" class="btn btn-trans" data-chat-message-text="ui.buttons.send"></button>
|
|
||||||
<button id="abort" class="btn btn-trans" disabled data-chat-message-text="ui.buttons.abort"></button>
|
|
||||||
</div>
|
|
||||||
<div class="small mt-2 text-center text-secondary" data-chat-message-text="ui.footer_disclaimer"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -16,7 +16,7 @@ use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
|
|||||||
|
|
||||||
#[AsCommand(
|
#[AsCommand(
|
||||||
name: 'mto:agent:user:create',
|
name: 'mto:agent:user:create',
|
||||||
description: 'Creates a new admin user'
|
description: 'Creates a new application user'
|
||||||
)]
|
)]
|
||||||
class CreateUserCommand extends Command
|
class CreateUserCommand extends Command
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,8 @@ class CreateUserCommand extends Command
|
|||||||
'ROLE_SUPER_ADMIN',
|
'ROLE_SUPER_ADMIN',
|
||||||
'ROLE_KNOWLEDGE_ADMIN',
|
'ROLE_KNOWLEDGE_ADMIN',
|
||||||
'ROLE_EDITOR',
|
'ROLE_EDITOR',
|
||||||
'ROLE_USER',
|
'ROLE_ADMIN_AREA',
|
||||||
|
'ROLE_CHAT_USER',
|
||||||
],
|
],
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ final class IngestJobController extends AbstractController
|
|||||||
)]
|
)]
|
||||||
public function status(string $id, EntityManagerInterface $em): JsonResponse
|
public function status(string $id, EntityManagerInterface $em): JsonResponse
|
||||||
{
|
{
|
||||||
$this->denyAccessUnlessGranted('ROLE_USER');
|
$this->denyAccessUnlessGranted('ROLE_ADMIN_AREA');
|
||||||
|
|
||||||
$job = $this->findJob($id, $em);
|
$job = $this->findJob($id, $em);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ final class SecurityController extends AbstractController
|
|||||||
public function login(AuthenticationUtils $authUtils): Response
|
public function login(AuthenticationUtils $authUtils): Response
|
||||||
{
|
{
|
||||||
// Wenn bereits eingeloggt → direkt ins Dashboard
|
// Wenn bereits eingeloggt → direkt ins Dashboard
|
||||||
if ($this->getUser()) {
|
if ($this->getUser() !== null && $this->isGranted('ROLE_ADMIN_AREA')) {
|
||||||
return $this->redirectToRoute('admin_dashboard');
|
return $this->redirectToRoute('admin_dashboard');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
src/Controller/Chat/SecurityController.php
Normal file
39
src/Controller/Chat/SecurityController.php
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Controller\Chat;
|
||||||
|
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Login/logout endpoints for the chat area.
|
||||||
|
*
|
||||||
|
* The chat uses the existing App\Entity\User provider and only adds a separate
|
||||||
|
* route space plus ROLE_CHAT_USER access control. It intentionally stays outside
|
||||||
|
* App\Controller\Admin so Chat and Admin can evolve independently.
|
||||||
|
*/
|
||||||
|
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')) {
|
||||||
|
return $this->redirectToRoute('chat_index');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render('chat/security/login.html.twig', [
|
||||||
|
'last_username' => $authUtils->getLastUsername(),
|
||||||
|
'error' => $authUtils->getLastAuthenticationError(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/chat/logout', name: 'chat_logout', methods: ['GET'])]
|
||||||
|
public function logout(): void
|
||||||
|
{
|
||||||
|
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||||
|
}
|
||||||
|
}
|
||||||
68
templates/chat/security/login.html.twig
Normal file
68
templates/chat/security/login.html.twig
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Chat Login</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<link href="{{ asset('assets/styles/bootstrap.min.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-black text-light">
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row justify-content-center align-items-center" style="min-height:100vh;">
|
||||||
|
<div class="col-12 col-md-5 col-lg-4">
|
||||||
|
<div class="card bg-black border-secondary text-info">
|
||||||
|
<div class="card-body">
|
||||||
|
<header class="text-center mb-4">
|
||||||
|
<img src="{{ asset('assets/img/logo.png') }}" style="max-width: 100px;" alt="">
|
||||||
|
<h1 class="h5 mt-3 mb-1 text-info">RetrieX Chat</h1>
|
||||||
|
<div class="small text-secondary">Login fuer den Chatbereich</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{% if error %}
|
||||||
|
<div class="alert alert-danger">
|
||||||
|
{{ error.messageKey|trans(error.messageData, 'security') }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<form method="post" action="{{ path('chat_login') }}">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">E-Mail</label>
|
||||||
|
<input class="form-control bg-dark text-light border-secondary"
|
||||||
|
name="_username"
|
||||||
|
value="{{ last_username }}"
|
||||||
|
autocomplete="email"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label">Passwort</label>
|
||||||
|
<input class="form-control bg-dark text-light border-secondary"
|
||||||
|
type="password"
|
||||||
|
name="_password"
|
||||||
|
autocomplete="current-password"
|
||||||
|
required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input type="hidden"
|
||||||
|
name="_csrf_token"
|
||||||
|
value="{{ csrf_token('authenticate') }}">
|
||||||
|
|
||||||
|
<button class="btn btn-outline-info w-100" type="submit">
|
||||||
|
In den Chat einloggen
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="text-center mt-3 small text-secondary">
|
||||||
|
powered by mitho® 2026
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user