feat(rbac): add roles and permissions
This commit is contained in:
parent
86a32a942c
commit
694b7bd09a
37 changed files with 2682 additions and 218 deletions
411
ARCHITECTURE.md
Normal file
411
ARCHITECTURE.md
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
# Olympus CRM Architektur
|
||||
|
||||
## Projektueberblick
|
||||
|
||||
Olympus CRM ist als mehrschichtige CRM-Anwendung aufgebaut. Die Architektur trennt klar zwischen Browser, Frontend/BFF, Backend-API und Datenbank.
|
||||
|
||||
Die zentrale Regel lautet:
|
||||
|
||||
**Der Browser kommuniziert ausschliesslich mit Athena. Athena kommuniziert serverseitig mit Hermes. Hermes kommuniziert mit PostgreSQL.**
|
||||
|
||||
Diese Trennung ist verbindlich, besonders fuer Authentifizierung, API-Zugriffe und spaetere Modul-Erweiterungen wie Kunden, Projekte, Lieferanten, Kontakte und Mitarbeiter.
|
||||
|
||||
## Systemkomponenten
|
||||
|
||||
### Athena: Next.js 16 Frontend
|
||||
|
||||
Athena liegt unter `frontend/athena` und ist das Frontend sowie die BFF-Schicht des Systems.
|
||||
|
||||
Aufgaben:
|
||||
|
||||
- rendert die Benutzeroberflaeche mit Next.js 16 App Router
|
||||
- stellt eigene API-Routes unter `/api/...` bereit
|
||||
- setzt und loescht das HttpOnly-Auth-Cookie
|
||||
- ruft Hermes serverseitig auf
|
||||
- schuetzt Seiten ueber `proxy.ts`
|
||||
|
||||
Der Browser darf keine Hermes-URLs direkt aufrufen.
|
||||
|
||||
### Hermes: FastAPI Backend
|
||||
|
||||
Hermes liegt unter `backend/hermes` und stellt die interne Backend-API bereit.
|
||||
|
||||
Aufgaben:
|
||||
|
||||
- validiert Requests mit Pydantic v2
|
||||
- verarbeitet Fachlogik und Persistenz
|
||||
- prueft JWT Bearer Tokens
|
||||
- stellt REST-Endpunkte bereit
|
||||
- nutzt SQLAlchemy 2.x fuer Datenbankzugriffe
|
||||
- setzt keine Browser-Cookies
|
||||
|
||||
Hermes bleibt Cookie-frei. Authentifizierte Hermes-Endpunkte erwarten `Authorization: Bearer <jwt>`.
|
||||
|
||||
### PostgreSQL Datenbank
|
||||
|
||||
PostgreSQL ist die relationale Datenbank fuer Olympus CRM. Hermes greift ueber SQLAlchemy darauf zu.
|
||||
|
||||
Schemaaenderungen erfolgen ueber Alembic-Migrationen. Manuelle Datenbankeingriffe sind nur in Notfaellen zulaessig und muessen nachvollziehbar dokumentiert werden.
|
||||
|
||||
### Docker Compose
|
||||
|
||||
Die gemeinsame Docker-Compose-Konfiguration liegt im Projektroot in `docker-compose.yml`.
|
||||
|
||||
Sie verbindet:
|
||||
|
||||
- `hermes`
|
||||
- `athena`
|
||||
- das konfigurierte Docker-Netzwerk
|
||||
- die notwendigen Runtime-Umgebungsvariablen
|
||||
|
||||
## BFF-Architektur
|
||||
|
||||
Olympus nutzt eine Backend-for-Frontend-Architektur.
|
||||
|
||||
Regeln:
|
||||
|
||||
- Browser spricht ausschliesslich mit Athena.
|
||||
- Athena spricht serverseitig mit Hermes.
|
||||
- Hermes setzt keine Browser-Cookies.
|
||||
- Cross-Origin-Cookies werden nicht verwendet.
|
||||
- Frontend-API-Zugriffe laufen ueber relative Athena-Routen wie `/api/users`.
|
||||
|
||||
Der Datenfluss bleibt dadurch kontrolliert, sicher und leichter hinter Reverse Proxies betreibbar.
|
||||
|
||||
## Authentifizierungsfluss
|
||||
|
||||
### Login
|
||||
|
||||
1. Der Browser sendet Benutzername und Passwort an Athena: `POST /api/login`.
|
||||
2. Athena ruft serverseitig Hermes auf: `POST /auth/login`.
|
||||
3. Hermes validiert die Zugangsdaten.
|
||||
4. Hermes gibt JWT, Ablaufzeit und Benutzerdaten als JSON zurueck.
|
||||
5. Athena setzt das JWT in einem HttpOnly-Cookie.
|
||||
6. Der Browser wird auf eine geschuetzte Seite weitergeleitet.
|
||||
|
||||
Hermes setzt kein Cookie.
|
||||
|
||||
### Logout
|
||||
|
||||
1. Der Browser sendet `POST /api/logout` an Athena.
|
||||
2. Athena loescht das HttpOnly-Cookie.
|
||||
3. Der Benutzer wird zur Login-Seite gefuehrt.
|
||||
|
||||
### HttpOnly Cookie
|
||||
|
||||
Das Auth-Cookie wird von Athena gesetzt.
|
||||
|
||||
Eigenschaften:
|
||||
|
||||
- `HttpOnly`
|
||||
- `SameSite=Lax`
|
||||
- `Path=/`
|
||||
- `Secure` in Produktion
|
||||
- Ablaufzeit orientiert sich an der JWT-Ablaufzeit
|
||||
|
||||
Tokens duerfen nicht in `localStorage` oder `sessionStorage` gespeichert werden.
|
||||
|
||||
### JWT
|
||||
|
||||
Hermes erzeugt JWT Access Tokens.
|
||||
|
||||
Wichtige Claims:
|
||||
|
||||
- `sub`: Benutzername
|
||||
- `exp`: Ablaufzeit
|
||||
- `iat`: Ausstellungszeitpunkt
|
||||
- `iss`: konfigurierter Issuer
|
||||
- `type`: Token-Typ, aktuell `access`
|
||||
|
||||
### Issuer
|
||||
|
||||
Der Issuer wird ueber `JWT_ISSUER` konfiguriert. Hermes prueft den Issuer bei der Token-Validierung.
|
||||
|
||||
### Ablaufzeit
|
||||
|
||||
Die Ablaufzeit wird ueber `ACCESS_TOKEN_EXPIRE_MINUTES` konfiguriert. Athena nutzt die von Hermes gelieferte Ablaufzeit fuer das Cookie.
|
||||
|
||||
### `proxy.ts`
|
||||
|
||||
Athena schuetzt Routen ueber `frontend/athena/proxy.ts`.
|
||||
|
||||
Aufgaben:
|
||||
|
||||
- geschuetzte Bereiche ohne Cookie auf `/login` umleiten
|
||||
- eingeloggte Benutzer von `/login` auf `/dashboard` umleiten
|
||||
|
||||
## API-Datenfluss
|
||||
|
||||
Standardfluss:
|
||||
|
||||
```text
|
||||
Browser -> Athena /api/... -> Hermes -> PostgreSQL
|
||||
```
|
||||
|
||||
Beispiel Benutzerliste:
|
||||
|
||||
```text
|
||||
Browser
|
||||
GET /api/users
|
||||
|
||||
Athena
|
||||
GET {HERMES_INTERNAL_URL}/users
|
||||
Authorization: Bearer <jwt aus HttpOnly Cookie>
|
||||
|
||||
Hermes
|
||||
SQLAlchemy Query gegen PostgreSQL
|
||||
```
|
||||
|
||||
Athena ist die einzige API-Oberflaeche fuer den Browser.
|
||||
|
||||
## RBAC: Rollen und Berechtigungen
|
||||
|
||||
Olympus verwendet ein serverseitiges RBAC-System als Grundlage fuer alle CRM-Module.
|
||||
|
||||
Konzepte:
|
||||
|
||||
- Benutzer besitzen genau eine primaere Rolle.
|
||||
- Rollen besitzen mehrere Berechtigungen.
|
||||
- Berechtigungen sind stabile String-Keys wie `users.read`.
|
||||
- Hermes prueft Berechtigungen serverseitig ueber Dependencies.
|
||||
- Athena darf Permissions fuer UI-Sichtbarkeit nutzen, aber niemals als Auth-Quelle.
|
||||
|
||||
Tabellen:
|
||||
|
||||
- `roles`
|
||||
- `permissions`
|
||||
- `role_permissions`
|
||||
- `users.role_id`
|
||||
|
||||
Das alte `users.role`-Feld bleibt vorerst als Legacy-Kompatibilitaet erhalten. Neue Logik verwendet `users.role_id` und die `roles`-Beziehung.
|
||||
|
||||
### Standardrollen
|
||||
|
||||
- `administrator`
|
||||
- `management`
|
||||
- `sales`
|
||||
- `technician`
|
||||
- `support`
|
||||
- `warehouse`
|
||||
- `guest`
|
||||
|
||||
`administrator` erhaelt alle Berechtigungen. `management` erhaelt mindestens Lesezugriff auf Dashboard, Benutzer, Kunden, Projekte und Tickets. `guest` erhaelt minimal `dashboard.read`.
|
||||
|
||||
### Standardberechtigungen
|
||||
|
||||
Benutzer:
|
||||
|
||||
- `users.read`
|
||||
- `users.create`
|
||||
- `users.update`
|
||||
- `users.delete`
|
||||
- `users.password.update`
|
||||
|
||||
Rollen:
|
||||
|
||||
- `roles.read`
|
||||
- `roles.create`
|
||||
- `roles.update`
|
||||
- `roles.delete`
|
||||
- `roles.assign`
|
||||
|
||||
Kunden:
|
||||
|
||||
- `customers.read`
|
||||
- `customers.create`
|
||||
- `customers.update`
|
||||
- `customers.delete`
|
||||
|
||||
Projekte:
|
||||
|
||||
- `projects.read`
|
||||
- `projects.create`
|
||||
- `projects.update`
|
||||
- `projects.delete`
|
||||
|
||||
Tickets:
|
||||
|
||||
- `tickets.read`
|
||||
- `tickets.create`
|
||||
- `tickets.update`
|
||||
- `tickets.delete`
|
||||
|
||||
Dashboard und System:
|
||||
|
||||
- `dashboard.read`
|
||||
- `system.settings.read`
|
||||
- `system.settings.update`
|
||||
|
||||
### Berechtigungspruefung
|
||||
|
||||
Zentrale Hermes-Dependencies:
|
||||
|
||||
- `get_current_user`
|
||||
- `get_current_active_user`
|
||||
- `require_permission("permission.name")`
|
||||
- `require_any_permission([...])`
|
||||
- `require_all_permissions([...])`
|
||||
|
||||
Backend-Endpunkte muessen immer selbst pruefen. Frontend-Helfer wie `hasPermission` dienen nur dazu, Buttons oder Navigation auszublenden.
|
||||
|
||||
### Rollen-API
|
||||
|
||||
Hermes:
|
||||
|
||||
- `GET /roles`
|
||||
- `GET /roles/{id}`
|
||||
- `POST /roles`
|
||||
- `PUT /roles/{id}`
|
||||
- `DELETE /roles/{id}`
|
||||
- `PUT /roles/{id}/permissions`
|
||||
- `GET /permissions`
|
||||
|
||||
Athena stellt entsprechende BFF-Routen unter `/api/roles`, `/api/permissions` und `/api/me` bereit.
|
||||
|
||||
## Benutzerverwaltung
|
||||
|
||||
Die Benutzerverwaltung ist das erste Enterprise-Modul und dient als Vorlage fuer weitere Module.
|
||||
|
||||
Funktionen:
|
||||
|
||||
- Benutzerliste
|
||||
- Detailseite
|
||||
- Erstellen
|
||||
- Bearbeiten
|
||||
- Loeschen
|
||||
- Passwort separat aendern
|
||||
- Suche, Filter, Sortierung und Pagination im Frontend
|
||||
|
||||
Backend-Endpunkte in Hermes:
|
||||
|
||||
- `GET /users`
|
||||
- `GET /users/{id}`
|
||||
- `POST /users`
|
||||
- `PUT /users/{id}`
|
||||
- `PUT /users/{id}/password`
|
||||
- `DELETE /users/{id}`
|
||||
|
||||
Athena stellt die passenden BFF-Routen unter `/api/users` und `/api/users/[id]` bereit.
|
||||
|
||||
Diese Endpunkte sind per RBAC geschuetzt:
|
||||
|
||||
- `GET /users` und `GET /users/{id}`: `users.read`
|
||||
- `POST /users`: `users.create` und fuer Rollenzuweisung `roles.assign`
|
||||
- `PUT /users/{id}`: `users.update`; Rollenwechsel benoetigt `roles.assign`
|
||||
- `PUT /users/{id}/password`: `users.password.update`
|
||||
- `DELETE /users/{id}`: `users.delete`
|
||||
|
||||
### Passwortaenderung
|
||||
|
||||
Wenn beim Bearbeiten kein Passwort uebergeben wird, bleibt das bestehende Passwort unveraendert.
|
||||
|
||||
Wenn ein Passwort gesetzt wird, wird es in Hermes mit `pwdlib` neu gehasht.
|
||||
|
||||
### Self-Delete-Schutz
|
||||
|
||||
Der aktuell angemeldete Benutzer darf sich nicht selbst loeschen. Hermes verhindert dies serverseitig.
|
||||
|
||||
### Rollenmodell
|
||||
|
||||
Benutzer besitzen eine primaere RBAC-Rolle ueber `role_id`. Die Rolle bestimmt die serverseitigen Berechtigungen.
|
||||
|
||||
## Verzeichnisstruktur
|
||||
|
||||
```text
|
||||
backend/hermes
|
||||
app/
|
||||
api/
|
||||
core/
|
||||
db/
|
||||
models/
|
||||
repositories/
|
||||
schemas/
|
||||
alembic/
|
||||
alembic.ini
|
||||
dockerfile
|
||||
pyproject.toml
|
||||
|
||||
frontend/athena
|
||||
app/
|
||||
api/
|
||||
roles/
|
||||
users/
|
||||
components/
|
||||
lib/
|
||||
types/
|
||||
proxy.ts
|
||||
Dockerfile
|
||||
package.json
|
||||
|
||||
docker-compose.yml
|
||||
```
|
||||
|
||||
## Umgebungsvariablen
|
||||
|
||||
### Hermes
|
||||
|
||||
`DATABASE_URL`
|
||||
: PostgreSQL-Verbindungsstring.
|
||||
|
||||
`SECRET_KEY`
|
||||
: Signierschluessel fuer JWTs. Muss geheim bleiben.
|
||||
|
||||
`JWT_ISSUER`
|
||||
: Erwarteter JWT-Issuer, z. B. `hermes`.
|
||||
|
||||
`ACCESS_TOKEN_EXPIRE_MINUTES`
|
||||
: Ablaufzeit fuer Access Tokens in Minuten.
|
||||
|
||||
### Athena
|
||||
|
||||
`HERMES_INTERNAL_URL`
|
||||
: Serverseitige URL, ueber die Athena Hermes erreicht.
|
||||
|
||||
`AUTH_COOKIE_SECURE`
|
||||
: Steuert das `Secure`-Flag des Cookies. In Produktion `true`.
|
||||
|
||||
## Datenbankmigrationen
|
||||
|
||||
Olympus verwendet Alembic fuer Datenbankmigrationen.
|
||||
|
||||
Regeln:
|
||||
|
||||
- Schemaaenderungen immer ueber Alembic.
|
||||
- Migrationen muessen ins Repository aufgenommen werden.
|
||||
- Vor Deployment `alembic upgrade head` testen.
|
||||
- Keine manuellen DB-Aenderungen ausser in Notfaellen.
|
||||
|
||||
Typischer Ablauf:
|
||||
|
||||
```bash
|
||||
cd backend/hermes
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
## Sicherheitsprinzipien
|
||||
|
||||
Verbindliche Regeln:
|
||||
|
||||
- Keine Tokens im `localStorage`.
|
||||
- Keine Tokens im `sessionStorage`.
|
||||
- Keine Browserzugriffe auf Hermes.
|
||||
- Keine Cross-Origin-Cookies.
|
||||
- Hermes bleibt Cookie-frei.
|
||||
- Athena setzt HttpOnly Cookies.
|
||||
- Mutierende Athena-API-Routes pruefen Same-Origin.
|
||||
- Hermes prueft JWTs serverseitig.
|
||||
- Hermes prueft RBAC-Berechtigungen serverseitig.
|
||||
- Secrets gehoeren in Umgebungsvariablen, nicht in den Code.
|
||||
|
||||
## Produktionshinweise
|
||||
|
||||
Fuer Produktion gilt:
|
||||
|
||||
- HTTPS ist erforderlich.
|
||||
- `AUTH_COOKIE_SECURE=true` setzen.
|
||||
- Reverse Proxy ist moeglich und soll Forwarded Header korrekt setzen.
|
||||
- `HERMES_INTERNAL_URL` muss serverseitig erreichbar sein.
|
||||
- `SECRET_KEY` muss stark, geheim und stabil sein.
|
||||
- Datenbankmigrationen vor App-Rollout ausfuehren.
|
||||
|
||||
Lokaler HTTP-Betrieb ist nur fuer Entwicklung gedacht. In Produktion duerfen keine unsicheren Cookie-Einstellungen verwendet werden.
|
||||
Loading…
Add table
Add a link
Reference in a new issue