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(...)):