198 lines
No EOL
7.6 KiB
Python
198 lines
No EOL
7.6 KiB
Python
from fastapi import APIRouter, HTTPException, Depends, Body, status, Response
|
|
from fastapi.responses import FileResponse
|
|
from fastapi.security import OAuth2PasswordRequestForm
|
|
from fastapi.staticfiles import StaticFiles
|
|
from typing import Dict, Any
|
|
from datetime import timedelta
|
|
import pathlib
|
|
import os
|
|
import logging
|
|
|
|
from modules.shared.configuration import APP_CONFIG
|
|
from modules.security.auth import (
|
|
createAccessToken,
|
|
getCurrentActiveUser,
|
|
getUserContext,
|
|
ACCESS_TOKEN_EXPIRE_MINUTES
|
|
)
|
|
import modules.interfaces.gatewayModel as gatewayModel
|
|
from modules.interfaces.gatewayInterface import getGatewayInterface
|
|
|
|
router = APIRouter()
|
|
|
|
# Static folder setup - using absolute path from app root
|
|
baseDir = pathlib.Path(__file__).parent.parent.parent # Go up to gateway root
|
|
staticFolder = baseDir / "static"
|
|
os.makedirs(staticFolder, exist_ok=True)
|
|
|
|
# Mount static files
|
|
router.mount("/static", StaticFiles(directory=str(staticFolder), html=True), name="static")
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@router.get("/favicon.ico")
|
|
async def favicon():
|
|
return FileResponse(str(staticFolder / "favicon.ico"), media_type="image/x-icon")
|
|
|
|
@router.get("/", tags=["General"])
|
|
async def root():
|
|
"""API status endpoint"""
|
|
return {"status": "online", "message": "Data Platform API is active"}
|
|
|
|
@router.get("/api/test", tags=["General"])
|
|
async def getTest():
|
|
return f"Status: OK. Alowed origins: {APP_CONFIG.get('APP_ALLOWED_ORIGINS')}"
|
|
|
|
@router.options("/{fullPath:path}", tags=["General"])
|
|
async def optionsRoute(fullPath: str):
|
|
return Response(status_code=200)
|
|
|
|
@router.get("/api/environment", tags=["General"])
|
|
async def get_environment():
|
|
"""Get environment configuration for frontend"""
|
|
return {
|
|
"apiBaseUrl": APP_CONFIG.get("APP_API_URL", ""),
|
|
"environment": APP_CONFIG.get("APP_ENV", "development"),
|
|
"instanceLabel": APP_CONFIG.get("APP_ENV_LABEL", "Development"),
|
|
# Add other environment variables the frontend might need
|
|
}
|
|
|
|
@router.post("/api/token", response_model=gatewayModel.Token, tags=["General"])
|
|
async def loginForAccessToken(formData: OAuth2PasswordRequestForm = Depends()):
|
|
# Get root mandate and admin user IDs
|
|
adminGateway = getGatewayInterface()
|
|
rootMandateId = adminGateway.getInitialId("mandates")
|
|
adminUserId = adminGateway.getInitialId("users")
|
|
|
|
if not rootMandateId or not adminUserId:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="System is not properly initialized with root mandate and admin user"
|
|
)
|
|
|
|
# Create a new gateway interface instance with admin context
|
|
adminGateway = getGatewayInterface(rootMandateId, adminUserId)
|
|
|
|
try:
|
|
# Authenticate user
|
|
user = adminGateway.authenticateUser(formData.username, formData.password)
|
|
|
|
# Create token with mandate ID and user ID
|
|
accessTokenExpires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
|
|
accessToken = createAccessToken(
|
|
data={
|
|
"sub": user["username"],
|
|
"_mandateId": str(user["_mandateId"]), # Ensure string
|
|
"_userId": str(user["id"]) # Ensure string
|
|
},
|
|
expiresDelta=accessTokenExpires
|
|
)
|
|
|
|
logger.info(f"User {user['username']} successfully logged in with context: _mandateId={user['_mandateId']}, _userId={user['id']}")
|
|
return {"accessToken": accessToken, "tokenType": "bearer"}
|
|
except ValueError as e:
|
|
# Handle authentication errors
|
|
error_msg = str(e)
|
|
logger.warning(f"Authentication failed for user {formData.username}: {error_msg}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
detail=error_msg,
|
|
headers={"WWW-Authenticate": "Bearer"},
|
|
)
|
|
except Exception as e:
|
|
# Handle other errors
|
|
error_msg = f"Login failed: {str(e)}"
|
|
logger.error(f"Unexpected error during login for user {formData.username}: {error_msg}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=error_msg
|
|
)
|
|
|
|
@router.get("/api/user/me", response_model=Dict[str, Any], tags=["General"])
|
|
async def readUserMe(currentUser: Dict[str, Any] = Depends(getCurrentActiveUser)):
|
|
return currentUser
|
|
|
|
@router.post("/api/users/register", response_model=Dict[str, Any], tags=["General"])
|
|
async def registerUser(userData: Dict[str, Any]):
|
|
"""Register a new user."""
|
|
try:
|
|
logger.info("Received registration request")
|
|
logger.info(f"Raw userData type: {type(userData)}")
|
|
logger.info(f"Raw userData content: {userData}")
|
|
|
|
# Get root mandate and admin user IDs
|
|
adminGateway = getGatewayInterface()
|
|
rootMandateId = adminGateway.getInitialId("mandates")
|
|
adminUserId = adminGateway.getInitialId("users")
|
|
|
|
if not rootMandateId or not adminUserId:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="System is not properly initialized with root mandate and admin user"
|
|
)
|
|
|
|
# Create a new gateway interface instance with admin context
|
|
adminGateway = getGatewayInterface(rootMandateId, adminUserId)
|
|
|
|
# Check required fields
|
|
if not userData or not isinstance(userData, dict):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Invalid user data format"
|
|
)
|
|
|
|
if not userData.get("username") or not userData.get("password"):
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail="Username and password are required"
|
|
)
|
|
|
|
# Create user data with mandate ID
|
|
userData = {
|
|
"username": userData["username"],
|
|
"password": userData["password"],
|
|
"email": userData.get("email"),
|
|
"fullName": userData.get("fullName"),
|
|
"language": userData.get("language", "de"),
|
|
"_mandateId": rootMandateId,
|
|
"disabled": False,
|
|
"privilege": "user"
|
|
}
|
|
|
|
# Create the user
|
|
createdUser = adminGateway.createUser(**userData)
|
|
|
|
if not createdUser:
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Failed to create user"
|
|
)
|
|
|
|
# Clear the users table from cache to ensure fresh data
|
|
if hasattr(adminGateway.db, '_tablesCache') and "users" in adminGateway.db._tablesCache:
|
|
del adminGateway.db._tablesCache["users"]
|
|
|
|
# Return the created user (without password)
|
|
if "hashedPassword" in createdUser:
|
|
del createdUser["hashedPassword"]
|
|
return createdUser
|
|
except ValueError as e:
|
|
logger.error(f"ValueError during registration: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
detail=str(e)
|
|
)
|
|
except PermissionError as e:
|
|
logger.error(f"PermissionError during registration: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
detail=str(e)
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"Error during user registration: {str(e)}")
|
|
logger.error(f"Error type: {type(e)}")
|
|
logger.error(f"Error details: {e.__dict__ if hasattr(e, '__dict__') else 'No details available'}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail="Failed to register user"
|
|
) |