261 lines
No EOL
8.9 KiB
Python
261 lines
No EOL
8.9 KiB
Python
"""
|
|
Interface to the Gateway system.
|
|
Manages users and mandates for authentication.
|
|
"""
|
|
|
|
import os
|
|
import logging
|
|
from typing import Dict, Any, List, Optional, Union
|
|
import importlib
|
|
from passlib.context import CryptContext
|
|
|
|
from connectors.connectorDbJson import DatabaseConnector
|
|
from modules.configuration import APP_CONFIG
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Password-Hashing
|
|
pwdContext = CryptContext(schemes=["argon2"], deprecated="auto")
|
|
|
|
|
|
class GatewayInterface:
|
|
"""
|
|
Interface to the Gateway system.
|
|
Manages users and mandates.
|
|
"""
|
|
|
|
def __init__(self, mandateId: int = None, userId: int = None):
|
|
"""
|
|
Initializes the Gateway Interface with optional mandate and user context.
|
|
|
|
Args:
|
|
mandateId: ID of the current mandate (optional)
|
|
userId: ID of the current user (optional)
|
|
"""
|
|
# Context can be empty during initialization
|
|
self.mandateId = mandateId
|
|
self.userId = userId
|
|
|
|
# Import data model module
|
|
try:
|
|
self.modelModule = importlib.import_module("modules.gatewayModel")
|
|
logger.info("gatewayModel successfully imported")
|
|
except ImportError as e:
|
|
logger.error(f"Error importing gatewayModel: {e}")
|
|
raise
|
|
|
|
# Initialize database
|
|
self._initializeDatabase()
|
|
|
|
def _initializeDatabase(self):
|
|
"""
|
|
Initializes the database with minimal objects
|
|
"""
|
|
|
|
self.db = DatabaseConnector(
|
|
dbHost=APP_CONFIG.get("DB_SYSTEM_HOST"),
|
|
dbDatabase=APP_CONFIG.get("DB_SYSTEM_DATABASE"),
|
|
dbUser=APP_CONFIG.get("DB_SYSTEM_USER"),
|
|
dbPassword=APP_CONFIG.get("DB_SYSTEM_PASSWORD_SECRET"),
|
|
mandateId=self.mandateId if self.mandateId else 0,
|
|
userId=self.userId if self.userId else 0
|
|
)
|
|
|
|
# Create Root mandate if needed
|
|
existingMandateId = self.getInitialId("mandates")
|
|
mandates = self.db.getRecordset("mandates")
|
|
if existingMandateId is None or not mandates:
|
|
logger.info("Creating Root mandate")
|
|
rootMandate = {
|
|
"name": "Root",
|
|
"language": "de"
|
|
}
|
|
createdMandate = self.db.recordCreate("mandates", rootMandate)
|
|
logger.info(f"Root mandate created with ID {createdMandate['id']}")
|
|
|
|
# Update mandate context
|
|
self.mandateId = createdMandate['id']
|
|
self.userId = createdMandate['userId']
|
|
|
|
# Recreate connector with correct context
|
|
self.db = DatabaseConnector(
|
|
dbHost=APP_CONFIG.get("DB_SYSTEM_HOST"),
|
|
dbDatabase=APP_CONFIG.get("DB_SYSTEM_DATABASE"),
|
|
dbUser=APP_CONFIG.get("DB_SYSTEM_USER"),
|
|
dbPassword=APP_CONFIG.get("DB_SYSTEM_PASSWORD_SECRET"),
|
|
mandateId=self.mandateId,
|
|
userId=self.userId
|
|
)
|
|
|
|
# Create Admin user if needed
|
|
existingUserId = self.getInitialId("users")
|
|
users = self.db.getRecordset("users")
|
|
if existingUserId is None or not users:
|
|
logger.info("Creating Admin user")
|
|
adminUser = {
|
|
"mandateId": self.mandateId,
|
|
"username": "admin",
|
|
"email": "admin@example.com",
|
|
"fullName": "Administrator",
|
|
"disabled": False,
|
|
"language": "de",
|
|
"privilege": "sysadmin", # SysAdmin privilege
|
|
"hashedPassword": self._getPasswordHash("admin") # Use a secure password in production!
|
|
}
|
|
createdUser = self.db.recordCreate("users", adminUser)
|
|
logger.info(f"Admin user created with ID {createdUser['id']}")
|
|
|
|
# Update user context
|
|
self.userId = createdUser['id']
|
|
|
|
# Recreate connector with correct context
|
|
self.db = DatabaseConnector(
|
|
dbHost=APP_CONFIG.get("DB_SYSTEM_HOST"),
|
|
dbDatabase=APP_CONFIG.get("DB_SYSTEM_DATABASE"),
|
|
dbUser=APP_CONFIG.get("DB_SYSTEM_USER"),
|
|
dbPassword=APP_CONFIG.get("DB_SYSTEM_PASSWORD_SECRET"),
|
|
mandateId=self.mandateId,
|
|
userId=self.userId
|
|
)
|
|
|
|
def getInitialId(self, table: str) -> Optional[int]:
|
|
"""Returns the initial ID for a table"""
|
|
return self.db.getInitialId(table)
|
|
|
|
def _getPasswordHash(self, password: str) -> str:
|
|
"""Creates a hash for a password"""
|
|
return pwdContext.hash(password)
|
|
|
|
def _verifyPassword(self, plainPassword: str, hashedPassword: str) -> bool:
|
|
"""Checks if the password matches the hash"""
|
|
return pwdContext.verify(plainPassword, hashedPassword)
|
|
|
|
def _getCurrentTimestamp(self) -> str:
|
|
"""Returns the current timestamp in ISO format"""
|
|
from datetime import datetime
|
|
return datetime.now().isoformat()
|
|
|
|
# Mandate methods
|
|
|
|
def getAllMandates(self) -> List[Dict[str, Any]]:
|
|
"""Returns all mandates"""
|
|
return self.db.getRecordset("mandates")
|
|
|
|
def getMandate(self, mandateId: int) -> Optional[Dict[str, Any]]:
|
|
"""Returns a mandate by its ID"""
|
|
mandates = self.db.getRecordset("mandates", recordFilter={"id": mandateId})
|
|
if mandates:
|
|
return mandates[0]
|
|
return None
|
|
|
|
def createMandate(self, name: str, language: str = "de") -> Dict[str, Any]:
|
|
"""Creates a new mandate"""
|
|
mandateData = {
|
|
"name": name,
|
|
"language": language
|
|
}
|
|
|
|
return self.db.recordCreate("mandates", mandateData)
|
|
|
|
# User methods
|
|
|
|
def getAllUsers(self) -> List[Dict[str, Any]]:
|
|
"""Returns all users"""
|
|
users = self.db.getRecordset("users")
|
|
# Remove password hashes from the response
|
|
for user in users:
|
|
if "hashedPassword" in user:
|
|
del user["hashedPassword"]
|
|
return users
|
|
|
|
def getUsersByMandate(self, mandateId: int) -> List[Dict[str, Any]]:
|
|
"""
|
|
Returns all users of a specific mandate
|
|
|
|
Args:
|
|
mandateId: The ID of the mandate
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: List of users in the mandate
|
|
"""
|
|
users = self.db.getRecordset("users", recordFilter={"mandateId": mandateId})
|
|
# Remove password hashes from the response
|
|
for user in users:
|
|
if "hashedPassword" in user:
|
|
del user["hashedPassword"]
|
|
return users
|
|
|
|
def getUserByUsername(self, username: str) -> Optional[Dict[str, Any]]:
|
|
"""Returns a user by username"""
|
|
users = self.db.getRecordset("users")
|
|
for user in users:
|
|
if user.get("username") == username:
|
|
return user
|
|
return None
|
|
|
|
def getUser(self, userId: int) -> Optional[Dict[str, Any]]:
|
|
"""Returns a user by ID"""
|
|
users = self.db.getRecordset("users", recordFilter={"id": userId})
|
|
if users:
|
|
user = users[0]
|
|
# Remove password hash from the API response
|
|
if "hashedPassword" in user:
|
|
userCopy = user.copy()
|
|
del userCopy["hashedPassword"]
|
|
return userCopy
|
|
return user
|
|
return None
|
|
|
|
def authenticateUser(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
Authenticates a user by username and password
|
|
|
|
Args:
|
|
username: The username
|
|
password: The password
|
|
|
|
Returns:
|
|
Optional[Dict[str, Any]]: The user data or None if authentication fails
|
|
"""
|
|
user = self.getUserByUsername(username)
|
|
|
|
if not user:
|
|
return None
|
|
|
|
if not self._verifyPassword(password, user.get("hashedPassword", "")):
|
|
return None
|
|
|
|
# Check if the user is disabled
|
|
if user.get("disabled", False):
|
|
return None
|
|
|
|
# Create a copy without password hash
|
|
authenticatedUser = {**user}
|
|
if "hashedPassword" in authenticatedUser:
|
|
del authenticatedUser["hashedPassword"]
|
|
|
|
return authenticatedUser
|
|
|
|
|
|
# Singleton factory for GatewayInterface instances per context
|
|
_gatewayInterfaces = {}
|
|
|
|
def getGatewayInterface(mandateId: int = None, userId: int = None) -> GatewayInterface:
|
|
"""
|
|
Returns a GatewayInterface instance for the specified context.
|
|
Reuses existing instances.
|
|
|
|
Args:
|
|
mandateId: ID of the mandate
|
|
userId: ID of the user
|
|
|
|
Returns:
|
|
GatewayInterface instance
|
|
"""
|
|
contextKey = f"{mandateId}_{userId}"
|
|
if contextKey not in _gatewayInterfaces:
|
|
_gatewayInterfaces[contextKey] = GatewayInterface(mandateId, userId)
|
|
return _gatewayInterfaces[contextKey]
|
|
|
|
# Initialize the interface
|
|
getGatewayInterface() |