uam update

This commit is contained in:
ValueOn AG 2025-05-16 17:14:34 +02:00
parent bf986b4d71
commit 1261e54509
3 changed files with 104 additions and 130 deletions

View file

@ -154,14 +154,40 @@ class DatabaseConnector:
path = self._getTablePath(table)
try:
# Log the path and data being saved
logger.debug(f"Attempting to save table {table} to {path}")
logger.debug(f"Data to save: {json.dumps(data, indent=2)}")
# Check if directory exists and is writable
dir_path = os.path.dirname(path)
if not os.path.exists(dir_path):
logger.error(f"Directory does not exist: {dir_path}")
return False
if not os.access(dir_path, os.W_OK):
logger.error(f"Directory is not writable: {dir_path}")
return False
# Check if file exists and is writable
if os.path.exists(path) and not os.access(path, os.W_OK):
logger.error(f"File exists but is not writable: {path}")
return False
with open(path, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# Verify the file was written correctly
if not os.path.exists(path):
logger.error(f"File was not created after write: {path}")
return False
# Update the cache
self._tablesCache[table] = data
logger.debug(f"Successfully saved table {table}")
return True
except Exception as e:
logger.error(f"Error saving table {table}: {e}")
logger.error(f"Error saving table {table}: {str(e)}")
logger.error(f"Error type: {type(e).__name__}")
logger.error(f"Error details: {e.__dict__ if hasattr(e, '__dict__') else 'No details available'}")
return False
def _applyRecordFilter(self, records: List[Dict[str, Any]], recordFilter: Dict[str, Any] = None) -> List[Dict[str, Any]]:

View file

@ -279,8 +279,10 @@ class GatewayInterface:
users = self.db.getRecordset("users")
for user in users:
if user.get("username") == username:
# Return a copy of the user with all fields, including hashedPassword
return user.copy()
# Log the fields present in the user record
logger.debug(f"Found user {username} with fields: {list(user.keys())}")
# Return a complete copy of the user record with all fields
return {**user} # Use dict unpacking to ensure we get a complete copy with all fields
logger.debug(f"No user found with username {username}")
return None
@ -341,10 +343,7 @@ class GatewayInterface:
createdUser = self.db.recordCreate("users", userData)
# Remove password hash from the response
if "hashedPassword" in createdUser:
del createdUser["hashedPassword"]
# Return the complete user record
return createdUser
def authenticateUser(self, username: str, password: str) -> Optional[Dict[str, Any]]:

View file

@ -1,8 +1,11 @@
from fastapi import APIRouter, HTTPException, Depends, Body, Path
from fastapi import APIRouter, HTTPException, Depends, Body, Path, Request
from typing import List, Dict, Any, Optional
from fastapi import status
from datetime import datetime
from dataclasses import dataclass
import logging
import time
import traceback
# Import auth module
from modules.auth import getCurrentActiveUser, getUserContext
@ -11,6 +14,9 @@ from modules.auth import getCurrentActiveUser, getUserContext
from modules.gatewayInterface import getGatewayInterface
from modules.gatewayModel import User
# Set up logger
logger = logging.getLogger(__name__)
# Determine all attributes of the model
def getModelAttributes(modelClass):
return [attr for attr in dir(modelClass)
@ -66,145 +72,88 @@ async def getUsers(currentUser: Dict[str, Any] = Depends(getCurrentActiveUser)):
@router.post("/register", response_model=Dict[str, Any])
async def registerUser(userData: dict = Body(...)):
"""Register a new user"""
# Add debug logging to see what's coming in
import logging
logger = logging.getLogger(__name__)
logger.info(f"Registration request data: {userData}")
# Get the initial IDs for mandate and admin user
adminGateway = getGatewayInterface()
# Get ID of the root mandate - we'll use this for new users
rootMandateId = adminGateway.getInitialId("mandates")
adminUserId = adminGateway.getInitialId("users")
logger.info(f"Root mandate ID: {rootMandateId}, Admin user ID: {adminUserId}")
if not rootMandateId or not adminUserId:
logger.error("System initialization error: Missing root mandate or admin user")
raise HTTPException(
status_code=500,
detail="System is not properly initialized with root mandate and admin user"
)
# Use a gateway with admin context for user creation
gateway = getGatewayInterface(rootMandateId, adminUserId)
if "username" not in userData or "password" not in userData:
logger.error("Missing required fields in registration data")
raise HTTPException(status_code=400, detail="Username and password required")
async def registerUser(request: Request):
"""Register a new user."""
try:
# Create user data - explicitly set fields
userCreateData = {
"username": userData["username"],
"password": userData["password"],
"mandateId": rootMandateId, # Use the Root mandate
# Get request data
data = await request.json()
logger.info(f"Registration request data: {data}")
# Get root mandate and admin user IDs
rootMandateId = 1 # Root mandate is always ID 1
adminUserId = 1 # Admin user is always ID 1
# Create a new gateway interface instance with admin context
adminGateway = getGatewayInterface(rootMandateId, adminUserId)
# Check required fields
if not data.get("username") or not data.get("password"):
logger.error("Missing required fields in registration request")
raise HTTPException(status_code=400, detail="Username and password are required")
# Create user data
userData = {
"username": data["username"],
"password": data["password"],
"email": data.get("email"),
"fullName": data.get("fullName"),
"language": data.get("language", "de"),
"mandateId": rootMandateId,
"disabled": False,
"privilege": "user",
"language": userData.get("language", "de")
"privilege": "user"
}
# Explicitly add optional fields - only if they exist and are not empty
if "email" in userData and userData["email"]:
userCreateData["email"] = userData["email"]
if "fullName" in userData and userData["fullName"]:
userCreateData["fullName"] = userData["fullName"]
logger.info(f"Attempting to create user with data: {userCreateData}")
# First check if user already exists
existingUser = gateway.getUserByUsername(userData["username"])
if existingUser:
logger.error(f"User {userData['username']} already exists")
raise HTTPException(
status_code=400,
detail=f"User {userData['username']} already exists"
)
# Create the user
newUser = gateway.createUser(**userCreateData)
logger.info(f"User created successfully: {newUser}")
logger.info(f"Attempting to create user with data: {userData}")
createdUser = adminGateway.createUser(**userData)
logger.info(f"User created successfully: {createdUser}")
# Wait a short moment to ensure database consistency
import time
# Add a small delay to ensure database consistency
time.sleep(0.5)
# Verify that the password was properly stored
createdUser = gateway.getUserByUsername(userData["username"])
logger.info(f"Retrieved created user: {createdUser}")
if not createdUser:
logger.error("User creation verification failed: User not found after creation")
raise HTTPException(
status_code=500,
detail="Failed to verify user creation. Please try again."
)
# Verify the user was created and password was stored
if "hashedPassword" not in createdUser:
logger.error("User creation verification failed: Password not stored")
# If password wasn't stored, delete the user and raise an error
if createdUser:
logger.info(f"Attempting to delete user {createdUser['id']} due to missing password")
try:
gateway.deleteUser(createdUser["id"])
logger.info(f"Successfully deleted user {createdUser['id']} after password storage failure")
except Exception as deleteError:
logger.error(f"Failed to delete user after password storage failure: {str(deleteError)}")
raise HTTPException(
status_code=500,
detail="Failed to store password securely. Please try again."
)
logger.error("Password not stored in user record")
# Try to delete the user
try:
adminGateway.deleteUser(createdUser["id"])
logger.info("Successfully deleted user after password storage failure")
except Exception as e:
logger.error(f"Failed to delete user after password storage failure: {str(e)}")
raise HTTPException(status_code=500, detail="Password storage failed")
# Final verification - try to authenticate the user
try:
authResult = gateway.authenticateUser(userData["username"], userData["password"])
if not authResult:
logger.error("Final verification failed: Could not authenticate newly created user")
# Delete the user if authentication fails
if createdUser:
try:
gateway.deleteUser(createdUser["id"])
logger.info(f"Successfully deleted user {createdUser['id']} after authentication failure")
except Exception as deleteError:
logger.error(f"Failed to delete user after authentication failure: {str(deleteError)}")
raise HTTPException(
status_code=500,
detail="Failed to verify user authentication. Please try again."
)
except Exception as authError:
logger.error(f"Authentication verification failed: {str(authError)}")
# Delete the user if authentication fails
if createdUser:
try:
gateway.deleteUser(createdUser["id"])
logger.info(f"Successfully deleted user {createdUser['id']} after authentication error")
except Exception as deleteError:
logger.error(f"Failed to delete user after authentication error: {str(deleteError)}")
raise HTTPException(
status_code=500,
detail="Failed to verify user authentication. Please try again."
)
logger.info("User verification successful")
# Test authentication
authResult = adminGateway.authenticateUser(userData["username"], userData["password"])
if not authResult:
logger.error("Authentication test failed after user creation")
# Try to delete the user
try:
# adminGateway.deleteUser(createdUser["id"])
logger.info("Successfully NOT deleted user after authentication test failure")
except Exception as e:
logger.error(f"Failed to delete user after authentication test failure: {str(e)}")
raise HTTPException(status_code=500, detail="Authentication test failed")
logger.info("User registration completed successfully")
return newUser
logger.info("Authentication test successful")
# Return success response
return {
"message": "User registered successfully",
"userId": createdUser["id"]
}
except ValueError as e:
logger.error(f"ValueError in registration: {str(e)}")
logger.error(f"Validation error during registration: {str(e)}")
raise HTTPException(status_code=400, detail=str(e))
except PermissionError as e:
logger.error(f"PermissionError in registration: {str(e)}")
logger.error(f"Permission error during registration: {str(e)}")
raise HTTPException(status_code=403, detail=str(e))
except Exception as e:
import traceback
logger.error(f"Unexpected error in registration: {str(e)}")
logger.error("Full traceback:")
logger.error(f"Unexpected error during registration: {str(e)}")
logger.error(traceback.format_exc())
logger.error(f"Error type: {type(e).__name__}")
logger.error(f"Error args: {e.args}")
raise HTTPException(status_code=500, detail=f"Registration failed: {str(e)}")
raise HTTPException(status_code=500, detail="Internal server error")
@router.post("/register-with-msal", response_model=Dict[str, Any])
async def registerUserWithMsal(userData: dict = Body(...)):