From 1261e545090bac9d4f78e19ec53fa7c7fb665211 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Fri, 16 May 2025 17:14:34 +0200
Subject: [PATCH] uam update
---
connectors/connectorDbJson.py | 28 ++++-
modules/gatewayInterface.py | 11 +-
routes/routeUsers.py | 195 +++++++++++++---------------------
3 files changed, 104 insertions(+), 130 deletions(-)
diff --git a/connectors/connectorDbJson.py b/connectors/connectorDbJson.py
index de8f8b63..bd2ddd13 100644
--- a/connectors/connectorDbJson.py
+++ b/connectors/connectorDbJson.py
@@ -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]]:
diff --git a/modules/gatewayInterface.py b/modules/gatewayInterface.py
index 2ab0da0f..7a236768 100644
--- a/modules/gatewayInterface.py
+++ b/modules/gatewayInterface.py
@@ -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]]:
diff --git a/routes/routeUsers.py b/routes/routeUsers.py
index d73c52af..f7fa02c3 100644
--- a/routes/routeUsers.py
+++ b/routes/routeUsers.py
@@ -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(...)):