full CRUD for all objects
This commit is contained in:
parent
55b4bbaa72
commit
3094007d6b
10 changed files with 627 additions and 90 deletions
|
|
@ -5,5 +5,12 @@
|
||||||
"language": "de",
|
"language": "de",
|
||||||
"mandate_id": 1,
|
"mandate_id": 1,
|
||||||
"user_id": 1
|
"user_id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Mandant von test",
|
||||||
|
"language": "de",
|
||||||
|
"mandate_id": 1,
|
||||||
|
"user_id": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -7,7 +7,20 @@
|
||||||
"full_name": "Administrator",
|
"full_name": "Administrator",
|
||||||
"disabled": false,
|
"disabled": false,
|
||||||
"language": "de",
|
"language": "de",
|
||||||
|
"privilege": "sysadmin",
|
||||||
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$tJYSAmDs/T+HEALg3Nsbww$2HnCW69CkCkk82fgRhzjYtdNu3b9G7NmSJZmbUQVDOs",
|
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$tJYSAmDs/T+HEALg3Nsbww$2HnCW69CkCkk82fgRhzjYtdNu3b9G7NmSJZmbUQVDOs",
|
||||||
"user_id": 1
|
"user_id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"mandate_id": 2,
|
||||||
|
"username": "test",
|
||||||
|
"email": "web@pamocreate.com",
|
||||||
|
"full_name": "Test User",
|
||||||
|
"disabled": false,
|
||||||
|
"language": "de",
|
||||||
|
"privilege": "user",
|
||||||
|
"hashed_password": "$argon2id$v=19$m=65536,t=3,p=4$3fufM+bc+19r7V3rvVfKmQ$CrJCc6r44jO4DxMHs8E61ayupBeUi6a+65VH0Q1cGo8",
|
||||||
|
"user_id": 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"mandate_id": 1,
|
"mandate_id": 1,
|
||||||
"user_id": 1,
|
"user_id": 1,
|
||||||
"name": "Hugo",
|
"name": "Hugo C",
|
||||||
"type": "Transformation",
|
"type": "Transformation",
|
||||||
"workspace_id": 1,
|
"workspace_id": "1",
|
||||||
"capabilities": "ccc",
|
"capabilities": "ccc",
|
||||||
"description": "ddd"
|
"description": "ddd"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from typing import Dict, Any
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
import pathlib
|
||||||
|
|
||||||
# Import interfaces
|
# Import interfaces
|
||||||
from modules.gateway_interface import get_gateway_interface
|
from modules.gateway_interface import get_gateway_interface
|
||||||
|
|
@ -26,15 +27,6 @@ from auth import (
|
||||||
# Import models - generisch importieren
|
# Import models - generisch importieren
|
||||||
import modules.gateway_model as gateway_model
|
import modules.gateway_model as gateway_model
|
||||||
|
|
||||||
# Import router modules
|
|
||||||
from routes.attributes import router as attributes_router
|
|
||||||
from routes.workspace import router as workspace_router
|
|
||||||
from routes.agent import router as agent_router
|
|
||||||
from routes.file import router as file_router
|
|
||||||
from routes.prompt import router as prompt_router
|
|
||||||
from routes.workflow import router as workflow_router
|
|
||||||
from routes.mandate import router as mandate_router
|
|
||||||
|
|
||||||
# Konfiguration des Loggers
|
# Konfiguration des Loggers
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.INFO,
|
level=logging.INFO,
|
||||||
|
|
@ -56,14 +48,17 @@ app.add_middleware(
|
||||||
max_age=86400 # Erhöhung des Caching für Preflight-Anfragen
|
max_age=86400 # Erhöhung des Caching für Preflight-Anfragen
|
||||||
)
|
)
|
||||||
|
|
||||||
# Statischer Folder für Frontend
|
# Statischer Folder für Frontend - mit absolutem Pfad arbeiten
|
||||||
os.makedirs(os.path.join(os.getcwd(), "static"), exist_ok=True)
|
base_dir = pathlib.Path(__file__).parent
|
||||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
static_folder = base_dir / "static"
|
||||||
|
os.makedirs(static_folder, exist_ok=True)
|
||||||
|
app.mount("/static", StaticFiles(directory=str(static_folder)), name="static")
|
||||||
|
|
||||||
# Add a specific route for favicon.ico
|
# Add a specific route for favicon.ico
|
||||||
@app.get("/favicon.ico", include_in_schema=False)
|
@app.get("/favicon.ico", include_in_schema=False)
|
||||||
async def favicon():
|
async def favicon():
|
||||||
return FileResponse("static/favicon.ico")
|
favicon_path = static_folder / "favicon.ico"
|
||||||
|
return FileResponse(str(favicon_path))
|
||||||
|
|
||||||
# Generelle Elements
|
# Generelle Elements
|
||||||
@app.get("/", tags=["General"])
|
@app.get("/", tags=["General"])
|
||||||
|
|
@ -75,7 +70,7 @@ async def root():
|
||||||
async def get_test():
|
async def get_test():
|
||||||
return "OK 1.1"
|
return "OK 1.1"
|
||||||
|
|
||||||
@app.options("/{full_path:path}")
|
@app.options("/{full_path:path}", tags=["General"])
|
||||||
async def options_route(full_path: str):
|
async def options_route(full_path: str):
|
||||||
return Response(status_code=200)
|
return Response(status_code=200)
|
||||||
|
|
||||||
|
|
@ -107,48 +102,36 @@ async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends(
|
||||||
|
|
||||||
return {"access_token": access_token, "token_type": "bearer"}
|
return {"access_token": access_token, "token_type": "bearer"}
|
||||||
|
|
||||||
|
|
||||||
# Benutzerregistrierung
|
|
||||||
@app.post("/api/users/register", response_model=gateway_model.User, tags=["General"])
|
|
||||||
async def register_user(user_data: dict = Body(...), current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
|
||||||
"""Neuen Benutzer registrieren"""
|
|
||||||
# Nur Benutzer des gleichen Mandanten dürfen erstellt werden
|
|
||||||
mandate_id, user_id = await get_user_context(current_user)
|
|
||||||
|
|
||||||
# Gateway-Interface mit Benutzerkontext initialisieren
|
|
||||||
gateway = get_gateway_interface(mandate_id, user_id)
|
|
||||||
|
|
||||||
if "username" not in user_data or "password" not in user_data:
|
|
||||||
raise HTTPException(status_code=400, detail="Benutzername und Passwort erforderlich")
|
|
||||||
|
|
||||||
try:
|
|
||||||
new_user = gateway.create_user(
|
|
||||||
username=user_data["username"],
|
|
||||||
password=user_data["password"],
|
|
||||||
email=user_data.get("email"),
|
|
||||||
full_name=user_data.get("full_name"),
|
|
||||||
language=user_data.get("language", "de")
|
|
||||||
)
|
|
||||||
return new_user
|
|
||||||
except ValueError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
|
|
||||||
|
|
||||||
# Benutzerinfo abrufen
|
# Benutzerinfo abrufen
|
||||||
@app.get("/api/users/me", response_model=Dict[str, Any], tags=["General"])
|
@app.get("/api/users/me", response_model=Dict[str, Any], tags=["General"])
|
||||||
async def read_users_me(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
async def read_users_me(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
||||||
return current_user
|
return current_user
|
||||||
|
|
||||||
|
|
||||||
# Alle Router einbinden
|
# Alle Router einbinden
|
||||||
app.include_router(attributes_router)
|
from routes.mandate import router as mandate_router
|
||||||
app.include_router(workspace_router)
|
|
||||||
app.include_router(agent_router)
|
|
||||||
app.include_router(file_router)
|
|
||||||
app.include_router(prompt_router)
|
|
||||||
app.include_router(workflow_router)
|
|
||||||
app.include_router(mandate_router)
|
app.include_router(mandate_router)
|
||||||
|
|
||||||
|
from routes.users import router as users_router
|
||||||
|
app.include_router(users_router)
|
||||||
|
|
||||||
|
from routes.attributes import router as attributes_router
|
||||||
|
app.include_router(attributes_router)
|
||||||
|
|
||||||
|
from routes.workspace import router as workspace_router
|
||||||
|
app.include_router(workspace_router)
|
||||||
|
|
||||||
|
from routes.agent import router as agent_router
|
||||||
|
app.include_router(agent_router)
|
||||||
|
|
||||||
|
from routes.file import router as file_router
|
||||||
|
app.include_router(file_router)
|
||||||
|
|
||||||
|
from routes.prompt import router as prompt_router
|
||||||
|
app.include_router(prompt_router)
|
||||||
|
|
||||||
|
from routes.workflow import router as workflow_router
|
||||||
|
app.include_router(workflow_router)
|
||||||
|
|
||||||
|
|
||||||
# Event handler beim Herunterfahren
|
# Event handler beim Herunterfahren
|
||||||
@app.on_event("shutdown")
|
@app.on_event("shutdown")
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ class GatewayInterface:
|
||||||
"full_name": "Administrator",
|
"full_name": "Administrator",
|
||||||
"disabled": False,
|
"disabled": False,
|
||||||
"language": "de",
|
"language": "de",
|
||||||
|
"privilege": "sysadmin", # SysAdmin-Berechtigung
|
||||||
"hashed_password": self._get_password_hash("admin") # In der Produktion ein sicheres Passwort verwenden!
|
"hashed_password": self._get_password_hash("admin") # In der Produktion ein sicheres Passwort verwenden!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,11 +140,94 @@ class GatewayInterface:
|
||||||
|
|
||||||
return self.db.record_create("mandates", mandate_data)
|
return self.db.record_create("mandates", mandate_data)
|
||||||
|
|
||||||
|
def update_mandate(self, mandate_id: int, mandate_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Aktualisiert einen bestehenden Mandanten
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mandate_id: Die ID des zu aktualisierenden Mandanten
|
||||||
|
mandate_data: Die zu aktualisierenden Mandantendaten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: Die aktualisierten Mandantendaten
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Wenn der Mandant nicht gefunden wurde
|
||||||
|
"""
|
||||||
|
# Prüfe, ob der Mandant existiert
|
||||||
|
mandate = self.get_mandate(mandate_id)
|
||||||
|
if not mandate:
|
||||||
|
raise ValueError(f"Mandant mit ID {mandate_id} nicht gefunden")
|
||||||
|
|
||||||
|
# Aktualisiere den Mandanten
|
||||||
|
updated_mandate = self.db.record_modify("mandates", mandate_id, mandate_data)
|
||||||
|
|
||||||
|
return updated_mandate
|
||||||
|
|
||||||
|
def delete_mandate(self, mandate_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Löscht einen Mandanten und alle damit verbundenen Benutzer und Daten
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mandate_id: Die ID des zu löschenden Mandanten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True, wenn der Mandant erfolgreich gelöscht wurde, sonst False
|
||||||
|
"""
|
||||||
|
# Prüfe, ob der Mandant existiert
|
||||||
|
mandate = self.get_mandate(mandate_id)
|
||||||
|
if not mandate:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Root-Mandant darf nicht gelöscht werden
|
||||||
|
if mandate_id == 1:
|
||||||
|
logger.warning("Versuch, den Root-Mandanten zu löschen, wurde verhindert")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Finde alle Benutzer des Mandanten
|
||||||
|
users = self.get_users_by_mandate(mandate_id)
|
||||||
|
|
||||||
|
# Lösche alle Benutzer des Mandanten und ihre zugehörigen Daten
|
||||||
|
for user in users:
|
||||||
|
self.delete_user(user["id"])
|
||||||
|
|
||||||
|
# Lösche den Mandanten
|
||||||
|
success = self.db.record_delete("mandates", mandate_id)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
logger.info(f"Mandant mit ID {mandate_id} wurde erfolgreich gelöscht")
|
||||||
|
else:
|
||||||
|
logger.error(f"Fehler beim Löschen des Mandanten mit ID {mandate_id}")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
# Benutzer-Methoden
|
# Benutzer-Methoden
|
||||||
|
|
||||||
def get_all_users(self) -> List[Dict[str, Any]]:
|
def get_all_users(self) -> List[Dict[str, Any]]:
|
||||||
"""Gibt alle Benutzer des aktuellen Mandanten zurück"""
|
"""Gibt alle Benutzer zurück"""
|
||||||
return self.db.get_recordset("users")
|
users = self.db.get_recordset("users")
|
||||||
|
# Entferne die Passwort-Hashes aus der Rückgabe
|
||||||
|
for user in users:
|
||||||
|
if "hashed_password" in user:
|
||||||
|
del user["hashed_password"]
|
||||||
|
return users
|
||||||
|
|
||||||
|
def get_users_by_mandate(self, mandate_id: int) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Gibt alle Benutzer eines bestimmten Mandanten zurück
|
||||||
|
|
||||||
|
Args:
|
||||||
|
mandate_id: Die ID des Mandanten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[Dict[str, Any]]: Liste der Benutzer des Mandanten
|
||||||
|
"""
|
||||||
|
users = self.db.get_recordset("users", record_filter={"mandate_id": mandate_id})
|
||||||
|
# Entferne die Passwort-Hashes aus der Rückgabe
|
||||||
|
for user in users:
|
||||||
|
if "hashed_password" in user:
|
||||||
|
del user["hashed_password"]
|
||||||
|
return users
|
||||||
|
|
||||||
def get_user_by_username(self, username: str) -> Optional[Dict[str, Any]]:
|
def get_user_by_username(self, username: str) -> Optional[Dict[str, Any]]:
|
||||||
"""Gibt einen Benutzer anhand seines Benutzernamens zurück"""
|
"""Gibt einen Benutzer anhand seines Benutzernamens zurück"""
|
||||||
|
|
@ -157,12 +241,37 @@ class GatewayInterface:
|
||||||
"""Gibt einen Benutzer anhand seiner ID zurück"""
|
"""Gibt einen Benutzer anhand seiner ID zurück"""
|
||||||
users = self.db.get_recordset("users", record_filter={"id": user_id})
|
users = self.db.get_recordset("users", record_filter={"id": user_id})
|
||||||
if users:
|
if users:
|
||||||
return users[0]
|
user = users[0]
|
||||||
|
# Entferne das Passwort-Hash aus der Rückgabe für die API
|
||||||
|
if "hashed_password" in user:
|
||||||
|
user_copy = user.copy()
|
||||||
|
del user_copy["hashed_password"]
|
||||||
|
return user_copy
|
||||||
|
return user
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_user(self, username: str, password: str, email: str = None,
|
def create_user(self, username: str, password: str, email: str = None,
|
||||||
full_name: str = None, language: str = "de") -> Dict[str, Any]:
|
full_name: str = None, language: str = "de", mandate_id: int = None,
|
||||||
"""Erstellt einen neuen Benutzer"""
|
disabled: bool = False, privilege: str = "user") -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Erstellt einen neuen Benutzer
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: Der Benutzername
|
||||||
|
password: Das Passwort
|
||||||
|
email: Die E-Mail-Adresse (optional)
|
||||||
|
full_name: Der vollständige Name (optional)
|
||||||
|
language: Die bevorzugte Sprache (Standard: "de")
|
||||||
|
mandate_id: Die ID des Mandanten (optional)
|
||||||
|
disabled: Ob der Benutzer deaktiviert ist (Standard: False)
|
||||||
|
privilege: Die Berechtigungsstufe (Standard: "user")
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: Die erstellten Benutzerdaten
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Wenn der Benutzername bereits existiert
|
||||||
|
"""
|
||||||
# Prüfe, ob der Benutzername bereits existiert
|
# Prüfe, ob der Benutzername bereits existiert
|
||||||
existing_user = self.get_user_by_username(username)
|
existing_user = self.get_user_by_username(username)
|
||||||
if existing_user:
|
if existing_user:
|
||||||
|
|
@ -174,14 +283,18 @@ class GatewayInterface:
|
||||||
if users:
|
if users:
|
||||||
next_id = max(user["id"] for user in users) + 1
|
next_id = max(user["id"] for user in users) + 1
|
||||||
|
|
||||||
|
# Verwende den übergebenen mandate_id oder den aktuellen Kontext
|
||||||
|
user_mandate_id = mandate_id if mandate_id is not None else self.mandate_id
|
||||||
|
|
||||||
user_data = {
|
user_data = {
|
||||||
"id": next_id,
|
"id": next_id,
|
||||||
"mandate_id": self.mandate_id,
|
"mandate_id": user_mandate_id,
|
||||||
"username": username,
|
"username": username,
|
||||||
"email": email,
|
"email": email,
|
||||||
"full_name": full_name,
|
"full_name": full_name,
|
||||||
"disabled": False,
|
"disabled": disabled,
|
||||||
"language": language,
|
"language": language,
|
||||||
|
"privilege": privilege, # Neue Eigenschaft
|
||||||
"hashed_password": self._get_password_hash(password)
|
"hashed_password": self._get_password_hash(password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,7 +307,16 @@ class GatewayInterface:
|
||||||
return created_user
|
return created_user
|
||||||
|
|
||||||
def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
def authenticate_user(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
||||||
"""Authentifiziert einen Benutzer anhand von Benutzername und Passwort"""
|
"""
|
||||||
|
Authentifiziert einen Benutzer anhand von Benutzername und Passwort
|
||||||
|
|
||||||
|
Args:
|
||||||
|
username: Der Benutzername
|
||||||
|
password: Das Passwort
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Optional[Dict[str, Any]]: Die Benutzerdaten oder None, wenn die Authentifizierung fehlschlägt
|
||||||
|
"""
|
||||||
user = self.get_user_by_username(username)
|
user = self.get_user_by_username(username)
|
||||||
|
|
||||||
if not user:
|
if not user:
|
||||||
|
|
@ -203,6 +325,10 @@ class GatewayInterface:
|
||||||
if not self._verify_password(password, user.get("hashed_password", "")):
|
if not self._verify_password(password, user.get("hashed_password", "")):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# Prüfe, ob der Benutzer deaktiviert ist
|
||||||
|
if user.get("disabled", False):
|
||||||
|
return None
|
||||||
|
|
||||||
# Erstelle eine Kopie ohne Passwort-Hash
|
# Erstelle eine Kopie ohne Passwort-Hash
|
||||||
authenticated_user = {**user}
|
authenticated_user = {**user}
|
||||||
if "hashed_password" in authenticated_user:
|
if "hashed_password" in authenticated_user:
|
||||||
|
|
@ -211,12 +337,26 @@ class GatewayInterface:
|
||||||
return authenticated_user
|
return authenticated_user
|
||||||
|
|
||||||
def update_user(self, user_id: int, user_data: Dict[str, Any]) -> Dict[str, Any]:
|
def update_user(self, user_id: int, user_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Aktualisiert einen Benutzer"""
|
"""
|
||||||
# Hole den aktuellen Benutzer
|
Aktualisiert einen Benutzer
|
||||||
user = self.get_user(user_id)
|
|
||||||
if not user:
|
Args:
|
||||||
|
user_id: Die ID des zu aktualisierenden Benutzers
|
||||||
|
user_data: Die zu aktualisierenden Benutzerdaten
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: Die aktualisierten Benutzerdaten
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: Wenn der Benutzer nicht gefunden wurde
|
||||||
|
"""
|
||||||
|
# Hole den aktuellen Benutzer mit Hash-Passwort (direkt aus der DB)
|
||||||
|
users = self.db.get_recordset("users", record_filter={"id": user_id})
|
||||||
|
if not users:
|
||||||
raise ValueError(f"Benutzer mit ID {user_id} nicht gefunden")
|
raise ValueError(f"Benutzer mit ID {user_id} nicht gefunden")
|
||||||
|
|
||||||
|
user = users[0]
|
||||||
|
|
||||||
# Wenn das Passwort geändert werden soll, hashe es
|
# Wenn das Passwort geändert werden soll, hashe es
|
||||||
if "password" in user_data:
|
if "password" in user_data:
|
||||||
user_data["hashed_password"] = self._get_password_hash(user_data["password"])
|
user_data["hashed_password"] = self._get_password_hash(user_data["password"])
|
||||||
|
|
@ -238,6 +378,92 @@ class GatewayInterface:
|
||||||
def enable_user(self, user_id: int) -> Dict[str, Any]:
|
def enable_user(self, user_id: int) -> Dict[str, Any]:
|
||||||
"""Aktiviert einen Benutzer"""
|
"""Aktiviert einen Benutzer"""
|
||||||
return self.update_user(user_id, {"disabled": False})
|
return self.update_user(user_id, {"disabled": False})
|
||||||
|
|
||||||
|
def _delete_user_referenced_data(self, user_id: int) -> None:
|
||||||
|
"""
|
||||||
|
Löscht alle Daten, die mit einem Benutzer verbunden sind
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: Die ID des Benutzers
|
||||||
|
"""
|
||||||
|
# Hier werden alle Tabellen durchsucht und alle Einträge gelöscht,
|
||||||
|
# die auf diesen Benutzer verweisen
|
||||||
|
|
||||||
|
# Workspaces des Benutzers finden und löschen
|
||||||
|
try:
|
||||||
|
workspaces = self.db.get_recordset("workspaces", record_filter={"user_id": user_id})
|
||||||
|
for workspace in workspaces:
|
||||||
|
# Agenten in diesem Workspace löschen
|
||||||
|
agents = self.db.get_recordset("agents", record_filter={"workspace_id": workspace["id"]})
|
||||||
|
for agent in agents:
|
||||||
|
# Hier könnten weitere abhängige Objekte gelöscht werden
|
||||||
|
self.db.record_delete("agents", agent["id"])
|
||||||
|
|
||||||
|
# Dateien in diesem Workspace löschen
|
||||||
|
files = self.db.get_recordset("files", record_filter={"workspace_id": workspace["id"]})
|
||||||
|
for file in files:
|
||||||
|
self.db.record_delete("files", file["id"])
|
||||||
|
|
||||||
|
# Prompts in diesem Workspace löschen
|
||||||
|
prompts = self.db.get_recordset("prompts", record_filter={"workspace_id": workspace["id"]})
|
||||||
|
for prompt in prompts:
|
||||||
|
self.db.record_delete("prompts", prompt["id"])
|
||||||
|
|
||||||
|
# Workflows in diesem Workspace löschen
|
||||||
|
workflows = self.db.get_recordset("workflows", record_filter={"workspace_id": workspace["id"]})
|
||||||
|
for workflow in workflows:
|
||||||
|
self.db.record_delete("workflows", workflow["id"])
|
||||||
|
|
||||||
|
# Den Workspace selbst löschen
|
||||||
|
self.db.record_delete("workspaces", workspace["id"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Löschen der Workspaces für Benutzer {user_id}: {e}")
|
||||||
|
|
||||||
|
# Attribute des Benutzers löschen
|
||||||
|
try:
|
||||||
|
attributes = self.db.get_recordset("attributes", record_filter={"user_id": user_id})
|
||||||
|
for attribute in attributes:
|
||||||
|
self.db.record_delete("attributes", attribute["id"])
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Löschen der Attribute für Benutzer {user_id}: {e}")
|
||||||
|
|
||||||
|
# Weitere Tabellen, die auf den Benutzer verweisen könnten
|
||||||
|
# (Je nach Datenbankstruktur der Anwendung)
|
||||||
|
|
||||||
|
logger.info(f"Alle referenzierten Daten für Benutzer {user_id} wurden gelöscht")
|
||||||
|
|
||||||
|
def delete_user(self, user_id: int) -> bool:
|
||||||
|
"""
|
||||||
|
Löscht einen Benutzer und alle damit verbundenen Daten
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: Die ID des zu löschenden Benutzers
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True, wenn der Benutzer erfolgreich gelöscht wurde, sonst False
|
||||||
|
"""
|
||||||
|
# Prüfe, ob der Benutzer existiert
|
||||||
|
users = self.db.get_recordset("users", record_filter={"id": user_id})
|
||||||
|
if not users:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Root-Admin (ID 1) darf nicht gelöscht werden
|
||||||
|
if user_id == 1:
|
||||||
|
logger.warning("Versuch, den Root-Admin zu löschen, wurde verhindert")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Lösche alle mit dem Benutzer verbundenen Daten
|
||||||
|
self._delete_user_referenced_data(user_id)
|
||||||
|
|
||||||
|
# Lösche den Benutzer
|
||||||
|
success = self.db.record_delete("users", user_id)
|
||||||
|
|
||||||
|
if success:
|
||||||
|
logger.info(f"Benutzer mit ID {user_id} wurde erfolgreich gelöscht")
|
||||||
|
else:
|
||||||
|
logger.error(f"Fehler beim Löschen des Benutzers mit ID {user_id}")
|
||||||
|
|
||||||
|
return success
|
||||||
|
|
||||||
|
|
||||||
# Singleton-Factory für GatewayInterface-Instanzen pro Kontext
|
# Singleton-Factory für GatewayInterface-Instanzen pro Kontext
|
||||||
|
|
@ -253,6 +479,5 @@ def get_gateway_interface(mandate_id: int = None, user_id: int = None) -> Gatewa
|
||||||
_gateway_interfaces[context_key] = GatewayInterface(mandate_id, user_id)
|
_gateway_interfaces[context_key] = GatewayInterface(mandate_id, user_id)
|
||||||
return _gateway_interfaces[context_key]
|
return _gateway_interfaces[context_key]
|
||||||
|
|
||||||
#Init
|
# Init
|
||||||
get_gateway_interface()
|
get_gateway_interface()
|
||||||
|
|
||||||
|
|
@ -40,8 +40,9 @@ class User(BaseModel):
|
||||||
username: str = Field(description="Benutzername für die Anmeldung")
|
username: str = Field(description="Benutzername für die Anmeldung")
|
||||||
email: Optional[str] = Field(None, description="E-Mail-Adresse des Benutzers")
|
email: Optional[str] = Field(None, description="E-Mail-Adresse des Benutzers")
|
||||||
full_name: Optional[str] = Field(None, description="Vollständiger Name des Benutzers")
|
full_name: Optional[str] = Field(None, description="Vollständiger Name des Benutzers")
|
||||||
disabled: Optional[bool] = Field(False, description="Gibt an, ob der Benutzer deaktiviert ist")
|
|
||||||
language: str = Field(description="Bevorzugte Sprache des Benutzers")
|
language: str = Field(description="Bevorzugte Sprache des Benutzers")
|
||||||
|
disabled: Optional[bool] = Field(False, description="Gibt an, ob der Benutzer deaktiviert ist")
|
||||||
|
privilege: str = Field(description="Berechtigungsstufe") #sysadmin,admin,user
|
||||||
|
|
||||||
label: Label = Field(
|
label: Label = Field(
|
||||||
default=Label(default="Benutzer", translations={"en": "User", "fr": "Utilisateur"}),
|
default=Label(default="Benutzer", translations={"en": "User", "fr": "Utilisateur"}),
|
||||||
|
|
@ -55,8 +56,9 @@ class User(BaseModel):
|
||||||
"username": Label(default="Benutzername", translations={"en": "Username", "fr": "Nom d'utilisateur"}),
|
"username": Label(default="Benutzername", translations={"en": "Username", "fr": "Nom d'utilisateur"}),
|
||||||
"email": Label(default="E-Mail", translations={"en": "Email", "fr": "E-mail"}),
|
"email": Label(default="E-Mail", translations={"en": "Email", "fr": "E-mail"}),
|
||||||
"full_name": Label(default="Vollständiger Name", translations={"en": "Full name", "fr": "Nom complet"}),
|
"full_name": Label(default="Vollständiger Name", translations={"en": "Full name", "fr": "Nom complet"}),
|
||||||
|
"language": Label(default="Sprache", translations={"en": "Language", "fr": "Langue"}),
|
||||||
"disabled": Label(default="Deaktiviert", translations={"en": "Disabled", "fr": "Désactivé"}),
|
"disabled": Label(default="Deaktiviert", translations={"en": "Disabled", "fr": "Désactivé"}),
|
||||||
"language": Label(default="Sprache", translations={"en": "Language", "fr": "Langue"})
|
"privilege": Label(default="Berechtigungsstufe", translations={"en": "Access level", "fr": "Niveau d'accès"}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,6 @@ class Prompt(BaseModel):
|
||||||
mandate_id: int = Field(description="ID des zugehörigen Mandanten")
|
mandate_id: int = Field(description="ID des zugehörigen Mandanten")
|
||||||
user_id: int = Field(description="ID des Erstellers")
|
user_id: int = Field(description="ID des Erstellers")
|
||||||
content: str = Field(description="Inhalt des Prompts")
|
content: str = Field(description="Inhalt des Prompts")
|
||||||
created_at: str = Field(description="Erstellungszeitpunkt")
|
|
||||||
|
|
||||||
label: Label = Field(
|
label: Label = Field(
|
||||||
default=Label(default="Prompt", translations={"en": "Prompt", "fr": "Invite"}),
|
default=Label(default="Prompt", translations={"en": "Prompt", "fr": "Invite"}),
|
||||||
|
|
@ -96,7 +95,6 @@ class Prompt(BaseModel):
|
||||||
"mandate_id": Label(default="Mandanten-ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}),
|
"mandate_id": Label(default="Mandanten-ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}),
|
||||||
"user_id": Label(default="Benutzer-ID", translations={"en": "User ID", "fr": "ID d'utilisateur"}),
|
"user_id": Label(default="Benutzer-ID", translations={"en": "User ID", "fr": "ID d'utilisateur"}),
|
||||||
"content": Label(default="Inhalt", translations={"en": "Content", "fr": "Contenu"}),
|
"content": Label(default="Inhalt", translations={"en": "Content", "fr": "Contenu"}),
|
||||||
"created_at": Label(default="Erstellt am", translations={"en": "Created at", "fr": "Créé le"})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -106,7 +104,6 @@ class Workspace(BaseModel):
|
||||||
mandate_id: int = Field(description="ID des zugehörigen Mandanten")
|
mandate_id: int = Field(description="ID des zugehörigen Mandanten")
|
||||||
user_id: int = Field(description="ID des Erstellers")
|
user_id: int = Field(description="ID des Erstellers")
|
||||||
name: str = Field(description="Name des Workspaces")
|
name: str = Field(description="Name des Workspaces")
|
||||||
created_at: str = Field(description="Erstellungszeitpunkt")
|
|
||||||
prompts: List[Prompt] = Field(default=[], description="Liste der Prompts im Workspace")
|
prompts: List[Prompt] = Field(default=[], description="Liste der Prompts im Workspace")
|
||||||
agents: List[Agent] = Field(default=[], description="Liste der Agenten im Workspace")
|
agents: List[Agent] = Field(default=[], description="Liste der Agenten im Workspace")
|
||||||
dataObjectReferences: List[int] = Field(default=[], description="Referenzen zu Datenobjekten")
|
dataObjectReferences: List[int] = Field(default=[], description="Referenzen zu Datenobjekten")
|
||||||
|
|
@ -122,7 +119,6 @@ class Workspace(BaseModel):
|
||||||
"mandate_id": Label(default="Mandanten-ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}),
|
"mandate_id": Label(default="Mandanten-ID", translations={"en": "Mandate ID", "fr": "ID de mandat"}),
|
||||||
"user_id": Label(default="Benutzer-ID", translations={"en": "User ID", "fr": "ID d'utilisateur"}),
|
"user_id": Label(default="Benutzer-ID", translations={"en": "User ID", "fr": "ID d'utilisateur"}),
|
||||||
"name": Label(default="Name", translations={"en": "Name", "fr": "Nom"}),
|
"name": Label(default="Name", translations={"en": "Name", "fr": "Nom"}),
|
||||||
"created_at": Label(default="Erstellt am", translations={"en": "Created at", "fr": "Créé le"}),
|
|
||||||
"prompts": Label(default="Prompts", translations={"en": "Prompts", "fr": "Invites"}),
|
"prompts": Label(default="Prompts", translations={"en": "Prompts", "fr": "Invites"}),
|
||||||
"agents": Label(default="Agenten", translations={"en": "Agents", "fr": "Agents"}),
|
"agents": Label(default="Agenten", translations={"en": "Agents", "fr": "Agents"}),
|
||||||
"dataObjectReferences": Label(default="Datenobjekte", translations={"en": "Data Objects", "fr": "Objets de données"})
|
"dataObjectReferences": Label(default="Datenobjekte", translations={"en": "Data Objects", "fr": "Objets de données"})
|
||||||
|
|
|
||||||
|
|
@ -33,16 +33,6 @@ router = APIRouter(
|
||||||
responses={404: {"description": "Not found"}}
|
responses={404: {"description": "Not found"}}
|
||||||
)
|
)
|
||||||
|
|
||||||
@router.get("/test", response_model=str)
|
|
||||||
async def get_entity_test(s:str):
|
|
||||||
return s+s
|
|
||||||
|
|
||||||
@router.options("/{entity_type}")
|
|
||||||
async def options_entity_attributes(
|
|
||||||
entity_type: str = Path(..., description="Typ der Entität (z.B. workspace, agent, prompt)")
|
|
||||||
):
|
|
||||||
return Response(status_code=200)
|
|
||||||
|
|
||||||
@router.get("/{entity_type}", response_model=List[AttributeDefinition])
|
@router.get("/{entity_type}", response_model=List[AttributeDefinition])
|
||||||
async def get_entity_attributes(
|
async def get_entity_attributes(
|
||||||
entity_type: str = Path(..., description="Typ der Entität (z.B. workspace, agent, prompt)"),
|
entity_type: str = Path(..., description="Typ der Entität (z.B. workspace, agent, prompt)"),
|
||||||
|
|
@ -108,4 +98,12 @@ async def get_entity_attributes(
|
||||||
for agent in agents
|
for agent in agents
|
||||||
]
|
]
|
||||||
|
|
||||||
return visible_attributes
|
return visible_attributes
|
||||||
|
|
||||||
|
|
||||||
|
@router.options("/{entity_type}")
|
||||||
|
async def options_entity_attributes(
|
||||||
|
entity_type: str = Path(..., description="Typ der Entität (z.B. workspace, agent, prompt)")
|
||||||
|
):
|
||||||
|
return Response(status_code=200)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from fastapi import APIRouter, HTTPException, Depends, Body
|
from fastapi import APIRouter, HTTPException, Depends, Body, Path
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
from fastapi import status
|
from fastapi import status
|
||||||
|
|
||||||
|
|
@ -17,13 +17,18 @@ router = APIRouter(
|
||||||
|
|
||||||
@router.get("", response_model=List[Dict[str, Any]])
|
@router.get("", response_model=List[Dict[str, Any]])
|
||||||
async def get_mandates(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
async def get_mandates(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
||||||
"""Alle verfügbaren Mandanten abrufen (nur für Admin-Benutzer)"""
|
"""Alle verfügbaren Mandanten abrufen (nur für SysAdmin-Benutzer)"""
|
||||||
mandate_id, user_id = await get_user_context(current_user)
|
mandate_id, user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
# Gateway-Interface mit Benutzerkontext initialisieren
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
gateway = get_gateway_interface(mandate_id, user_id)
|
gateway = get_gateway_interface(mandate_id, user_id)
|
||||||
|
|
||||||
# TODO: Hier sollte eine Berechtigungsprüfung erfolgen
|
# Berechtigungsprüfung
|
||||||
|
if current_user.get("privilege") != "sysadmin":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Nur System-Administratoren können alle Mandanten abrufen"
|
||||||
|
)
|
||||||
|
|
||||||
return gateway.get_all_mandates()
|
return gateway.get_all_mandates()
|
||||||
|
|
||||||
|
|
@ -33,13 +38,18 @@ async def create_mandate(
|
||||||
mandate: Dict[str, Any] = Body(...),
|
mandate: Dict[str, Any] = Body(...),
|
||||||
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
):
|
):
|
||||||
"""Einen neuen Mandanten erstellen (nur für Admin-Benutzer)"""
|
"""Einen neuen Mandanten erstellen (nur für SysAdmin-Benutzer)"""
|
||||||
mandate_id, user_id = await get_user_context(current_user)
|
mandate_id, user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
# Gateway-Interface mit Benutzerkontext initialisieren
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
gateway = get_gateway_interface(mandate_id, user_id)
|
gateway = get_gateway_interface(mandate_id, user_id)
|
||||||
|
|
||||||
# TODO: Hier sollte eine Berechtigungsprüfung erfolgen
|
# Berechtigungsprüfung
|
||||||
|
if current_user.get("privilege") != "sysadmin":
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Nur System-Administratoren können Mandanten erstellen"
|
||||||
|
)
|
||||||
|
|
||||||
new_mandate = gateway.create_mandate(
|
new_mandate = gateway.create_mandate(
|
||||||
name=mandate.get("name", "Neuer Mandant"),
|
name=mandate.get("name", "Neuer Mandant"),
|
||||||
|
|
@ -60,8 +70,17 @@ async def get_mandate(
|
||||||
# Gateway-Interface mit Benutzerkontext initialisieren
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
gateway = get_gateway_interface(user_mandate_id, user_id)
|
gateway = get_gateway_interface(user_mandate_id, user_id)
|
||||||
|
|
||||||
# TODO: Hier sollte eine Berechtigungsprüfung erfolgen
|
# Berechtigungsprüfung
|
||||||
# Nur Admins oder Benutzer des gleichen Mandanten dürfen den Mandanten sehen
|
# Admin darf nur seinen eigenen Mandanten sehen, SysAdmin alle
|
||||||
|
is_admin = current_user.get("privilege") == "admin"
|
||||||
|
is_sysadmin = current_user.get("privilege") == "sysadmin"
|
||||||
|
is_own_mandate = user_mandate_id == mandate_id
|
||||||
|
|
||||||
|
if (is_admin and not is_own_mandate) and not is_sysadmin:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Abrufen dieses Mandanten"
|
||||||
|
)
|
||||||
|
|
||||||
mandate = gateway.get_mandate(mandate_id)
|
mandate = gateway.get_mandate(mandate_id)
|
||||||
if not mandate:
|
if not mandate:
|
||||||
|
|
@ -70,4 +89,84 @@ async def get_mandate(
|
||||||
detail=f"Mandant mit ID {mandate_id} nicht gefunden"
|
detail=f"Mandant mit ID {mandate_id} nicht gefunden"
|
||||||
)
|
)
|
||||||
|
|
||||||
return mandate
|
return mandate
|
||||||
|
|
||||||
|
@router.post("/{mandate_id}", response_model=Dict[str, Any])
|
||||||
|
async def update_mandate(
|
||||||
|
mandate_id: int = Path(..., description="ID des zu aktualisierenden Mandanten"),
|
||||||
|
mandate_data: Dict[str, Any] = Body(..., description="Aktualisierte Mandantendaten"),
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Einen bestehenden Mandanten aktualisieren"""
|
||||||
|
user_mandate_id, user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(user_mandate_id, user_id)
|
||||||
|
|
||||||
|
# Mandant existiert?
|
||||||
|
mandate = gateway.get_mandate(mandate_id)
|
||||||
|
if not mandate:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Mandant mit ID {mandate_id} nicht gefunden"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
is_admin = current_user.get("privilege") == "admin"
|
||||||
|
is_sysadmin = current_user.get("privilege") == "sysadmin"
|
||||||
|
is_own_mandate = user_mandate_id == mandate_id
|
||||||
|
|
||||||
|
if (is_admin and not is_own_mandate) and not is_sysadmin:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Aktualisieren dieses Mandanten"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mandant aktualisieren
|
||||||
|
updated_mandate = gateway.update_mandate(
|
||||||
|
mandate_id=mandate_id,
|
||||||
|
mandate_data=mandate_data
|
||||||
|
)
|
||||||
|
|
||||||
|
return updated_mandate
|
||||||
|
|
||||||
|
@router.delete("/{mandate_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
async def delete_mandate(
|
||||||
|
mandate_id: int = Path(..., description="ID des zu löschenden Mandanten"),
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Einen Mandanten löschen, inklusive aller zugehörigen Benutzer und referenzierten Objekte"""
|
||||||
|
user_mandate_id, user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(user_mandate_id, user_id)
|
||||||
|
|
||||||
|
# Mandant existiert?
|
||||||
|
mandate = gateway.get_mandate(mandate_id)
|
||||||
|
if not mandate:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Mandant mit ID {mandate_id} nicht gefunden"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
is_admin = current_user.get("privilege") == "admin"
|
||||||
|
is_sysadmin = current_user.get("privilege") == "sysadmin"
|
||||||
|
is_own_mandate = user_mandate_id == mandate_id
|
||||||
|
|
||||||
|
if (is_admin and not is_own_mandate) and not is_sysadmin:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Löschen dieses Mandanten"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Mandant löschen
|
||||||
|
success = gateway.delete_mandate(mandate_id)
|
||||||
|
if not success:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Fehler beim Löschen des Mandanten mit ID {mandate_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Kein Inhalt zurückgeben bei erfolgreichem Löschen
|
||||||
|
return None
|
||||||
214
gwserver/routes/users.py
Normal file
214
gwserver/routes/users.py
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
from fastapi import APIRouter, HTTPException, Depends, Body, Path
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
from fastapi import status
|
||||||
|
|
||||||
|
# Import auth module
|
||||||
|
from auth import get_current_active_user, get_user_context
|
||||||
|
|
||||||
|
# Import interfaces
|
||||||
|
from modules.gateway_interface import get_gateway_interface
|
||||||
|
|
||||||
|
# Router für Benutzer-Endpunkte erstellen
|
||||||
|
router = APIRouter(
|
||||||
|
prefix="/api/users",
|
||||||
|
tags=["Users"],
|
||||||
|
responses={404: {"description": "Not found"}}
|
||||||
|
)
|
||||||
|
|
||||||
|
@router.get("", response_model=List[Dict[str, Any]])
|
||||||
|
async def get_users(current_user: Dict[str, Any] = Depends(get_current_active_user)):
|
||||||
|
"""Alle verfügbaren Benutzer abrufen (nur für Admin/SysAdmin-Benutzer)"""
|
||||||
|
mandate_id, user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(mandate_id, user_id)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
if current_user.get("privilege") not in ["admin", "sysadmin"]:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Abrufen der Benutzerliste"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Admin sieht nur Benutzer des eigenen Mandanten, SysAdmin sieht alle
|
||||||
|
if current_user.get("privilege") == "admin":
|
||||||
|
return gateway.get_users_by_mandate(mandate_id)
|
||||||
|
else: # sysadmin
|
||||||
|
return gateway.get_all_users()
|
||||||
|
|
||||||
|
@router.post("/register", response_model=Dict[str, Any])
|
||||||
|
async def register_user(user_data: dict = Body(...)):
|
||||||
|
"""Neuen Benutzer registrieren"""
|
||||||
|
# Bei der Registrierung keinen Benutzerkontext verwenden
|
||||||
|
gateway = get_gateway_interface()
|
||||||
|
|
||||||
|
if "username" not in user_data or "password" not in user_data:
|
||||||
|
raise HTTPException(status_code=400, detail="Benutzername und Passwort erforderlich")
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_mandate = gateway.create_mandate(
|
||||||
|
name=f"Mandant von {user_data['username']}",
|
||||||
|
language=user_data.get("language", "de")
|
||||||
|
)
|
||||||
|
|
||||||
|
new_user = gateway.create_user(
|
||||||
|
username=user_data["username"],
|
||||||
|
password=user_data["password"],
|
||||||
|
email=user_data.get("email"),
|
||||||
|
full_name=user_data.get("full_name"),
|
||||||
|
language=user_data.get("language", "de"),
|
||||||
|
mandate_id=new_mandate["id"],
|
||||||
|
disabled=False,
|
||||||
|
privilege="user"
|
||||||
|
)
|
||||||
|
return new_user
|
||||||
|
except ValueError as e:
|
||||||
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
|
|
||||||
|
@router.get("/{user_id}", response_model=Dict[str, Any])
|
||||||
|
async def get_user(
|
||||||
|
user_id: int,
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Einen bestimmten Benutzer abrufen"""
|
||||||
|
mandate_id, current_user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(mandate_id, current_user_id)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
# Benutzer darf nur sich selbst abrufen, Admin nur Benutzer des eigenen Mandanten, SysAdmin alle
|
||||||
|
user_to_get = gateway.get_user(user_id)
|
||||||
|
if not user_to_get:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Benutzer mit ID {user_id} nicht gefunden"
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_id == current_user_id:
|
||||||
|
# Benutzer darf sich selbst abrufen
|
||||||
|
pass
|
||||||
|
elif current_user.get("privilege") == "admin" and user_to_get.get("mandate_id") == mandate_id:
|
||||||
|
# Admin darf Benutzer des eigenen Mandanten abrufen
|
||||||
|
pass
|
||||||
|
elif current_user.get("privilege") == "sysadmin":
|
||||||
|
# SysAdmin darf alle Benutzer abrufen
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Abrufen dieses Benutzers"
|
||||||
|
)
|
||||||
|
|
||||||
|
return user_to_get
|
||||||
|
|
||||||
|
@router.post("/{user_id}", response_model=Dict[str, Any])
|
||||||
|
async def update_user(
|
||||||
|
user_id: int = Path(..., description="ID des zu aktualisierenden Benutzers"),
|
||||||
|
user_data: Dict[str, Any] = Body(..., description="Aktualisierte Benutzerdaten"),
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Einen bestehenden Benutzer aktualisieren"""
|
||||||
|
mandate_id, current_user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(mandate_id, current_user_id)
|
||||||
|
|
||||||
|
# Benutzer existiert?
|
||||||
|
user_to_update = gateway.get_user(user_id)
|
||||||
|
if not user_to_update:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Benutzer mit ID {user_id} nicht gefunden"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
is_self_update = user_id == current_user_id
|
||||||
|
is_admin = current_user.get("privilege") == "admin"
|
||||||
|
is_sysadmin = current_user.get("privilege") == "sysadmin"
|
||||||
|
same_mandate = user_to_update.get("mandate_id") == mandate_id
|
||||||
|
|
||||||
|
# Filtere erlaubte Felder je nach Berechtigungsstufe
|
||||||
|
allowed_fields = {"username", "email", "full_name", "language"}
|
||||||
|
sensitive_fields = {"mandate_id", "disabled", "privilege"}
|
||||||
|
|
||||||
|
# Prüfe, ob sensitive Felder geändert werden sollen
|
||||||
|
sensitive_update = any(field in user_data for field in sensitive_fields)
|
||||||
|
|
||||||
|
if is_self_update and sensitive_update:
|
||||||
|
# Normale Benutzer dürfen ihre sensitiven Daten nicht ändern
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Ändern sensitiver Benutzerdaten"
|
||||||
|
)
|
||||||
|
elif is_admin and sensitive_update and not same_mandate:
|
||||||
|
# Admins dürfen sensitive Daten nur für Benutzer des eigenen Mandanten ändern
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Ändern sensitiver Daten für Benutzer anderer Mandanten"
|
||||||
|
)
|
||||||
|
elif not (is_self_update or (is_admin and same_mandate) or is_sysadmin):
|
||||||
|
# Keine Berechtigung für andere Fälle
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Aktualisieren dieses Benutzers"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Entferne nicht erlaubte Felder für normale Benutzer
|
||||||
|
if not (is_admin or is_sysadmin):
|
||||||
|
user_data = {k: v for k, v in user_data.items() if k in allowed_fields}
|
||||||
|
|
||||||
|
# User-Daten aktualisieren
|
||||||
|
updated_user = gateway.update_user(user_id, user_data)
|
||||||
|
return updated_user
|
||||||
|
|
||||||
|
@router.delete("/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
|
||||||
|
async def delete_user(
|
||||||
|
user_id: int = Path(..., description="ID des zu löschenden Benutzers"),
|
||||||
|
current_user: Dict[str, Any] = Depends(get_current_active_user)
|
||||||
|
):
|
||||||
|
"""Einen Benutzer löschen"""
|
||||||
|
mandate_id, current_user_id = await get_user_context(current_user)
|
||||||
|
|
||||||
|
# Gateway-Interface mit Benutzerkontext initialisieren
|
||||||
|
gateway = get_gateway_interface(mandate_id, current_user_id)
|
||||||
|
|
||||||
|
# Benutzer existiert?
|
||||||
|
user_to_delete = gateway.get_user(user_id)
|
||||||
|
if not user_to_delete:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_404_NOT_FOUND,
|
||||||
|
detail=f"Benutzer mit ID {user_id} nicht gefunden"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Berechtigungsprüfung
|
||||||
|
is_self_delete = user_id == current_user_id
|
||||||
|
is_admin = current_user.get("privilege") == "admin"
|
||||||
|
is_sysadmin = current_user.get("privilege") == "sysadmin"
|
||||||
|
same_mandate = user_to_delete.get("mandate_id") == mandate_id
|
||||||
|
|
||||||
|
if is_self_delete:
|
||||||
|
# Benutzer darf sich selbst löschen
|
||||||
|
pass
|
||||||
|
elif is_admin and same_mandate:
|
||||||
|
# Admin darf Benutzer des eigenen Mandanten löschen
|
||||||
|
pass
|
||||||
|
elif is_sysadmin:
|
||||||
|
# SysAdmin darf alle Benutzer löschen
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
|
detail="Keine Berechtigung zum Löschen dieses Benutzers"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Benutzer und alle referenzierten Objekte löschen
|
||||||
|
success = gateway.delete_user(user_id)
|
||||||
|
if not success:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
|
detail=f"Fehler beim Löschen des Benutzers mit ID {user_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Kein Inhalt zurückgeben bei erfolgreichem Löschen
|
||||||
|
return None
|
||||||
Loading…
Reference in a new issue