prod azure 1.0.12
This commit is contained in:
parent
8fe1c75288
commit
25234cff33
5 changed files with 602 additions and 27 deletions
|
|
@ -230,28 +230,19 @@ class AgentEmail(AgentBase):
|
|||
# Add document name to contents
|
||||
documentContents.append(f"\n\n--- {docName} ---\n")
|
||||
|
||||
# Process contents
|
||||
hasAttachment = False
|
||||
for content in doc.get("contents", []):
|
||||
# Add extracted text to document contents
|
||||
if content.get("dataExtracted"):
|
||||
documentContents.append(content.get("dataExtracted", ""))
|
||||
|
||||
# Prepare attachment if it has content data
|
||||
if content.get("data"):
|
||||
# Check if this content should be an attachment
|
||||
# Typically files like PDFs, images, etc.
|
||||
contentType = content.get("contentType", "")
|
||||
if (not contentType.startswith("text/") or
|
||||
contentType in ["application/pdf", "application/msword"]):
|
||||
hasAttachment = True
|
||||
|
||||
# If document has content to attach, add to attachments
|
||||
if hasAttachment:
|
||||
# Check if document has data to attach
|
||||
if doc.get("data"):
|
||||
# Add to attachments
|
||||
attachments.append({
|
||||
"name": docName,
|
||||
"document": doc
|
||||
})
|
||||
|
||||
# Add document name to contents
|
||||
documentContents.append(f"Document attached: {docName}")
|
||||
else:
|
||||
# If no data, just add the name
|
||||
documentContents.append(f"Document referenced: {docName}")
|
||||
|
||||
return "\n".join(documentContents), attachments
|
||||
|
||||
|
|
@ -481,7 +472,7 @@ class AgentEmail(AgentBase):
|
|||
def _createGraphDraftEmail(self, access_token, recipient, subject, body, attachments=None):
|
||||
"""
|
||||
Create a draft email using Microsoft Graph API with fixed attachment handling.
|
||||
Uses the complete document data for attachments.
|
||||
Uses the document data directly for attachments.
|
||||
|
||||
Args:
|
||||
access_token: Microsoft Graph access token
|
||||
|
|
@ -524,13 +515,13 @@ class AgentEmail(AgentBase):
|
|||
|
||||
logger.info(f"Processing attachment: {file_name}")
|
||||
|
||||
# Get the document data
|
||||
# Get the document data directly
|
||||
file_content = doc.get('data')
|
||||
if not file_content:
|
||||
logger.warning(f"No data found for attachment: {file_name}")
|
||||
continue
|
||||
|
||||
# Get content type from document
|
||||
# Get content type and base64 flag
|
||||
content_type = doc.get('contentType', 'application/octet-stream')
|
||||
is_base64 = doc.get('base64Encoded', False)
|
||||
|
||||
|
|
@ -556,18 +547,21 @@ class AgentEmail(AgentBase):
|
|||
'@odata.type': '#microsoft.graph.fileAttachment',
|
||||
'name': file_name,
|
||||
'contentType': content_type,
|
||||
'contentBytes': file_content
|
||||
'contentBytes': file_content,
|
||||
'isInline': False,
|
||||
'size': len(base64.b64decode(file_content)) if file_content else 0
|
||||
}
|
||||
email_data['attachments'].append(attachment_data)
|
||||
logger.info(f"Successfully added attachment: {file_name}")
|
||||
|
||||
# Try to create draft using drafts folder endpoint (Option 1)
|
||||
# Try to create draft using drafts folder endpoint
|
||||
try:
|
||||
logger.info("Attempting to create draft email using drafts folder endpoint")
|
||||
logger.info(f"Email data structure: subject={subject}, recipient={recipient}, " +
|
||||
f"has_attachments={bool(email_data.get('attachments'))}, " +
|
||||
f"attachment_count={len(email_data.get('attachments', []))}")
|
||||
f"has_attachments={bool(email_data.get('attachments'))}, " +
|
||||
f"attachment_count={len(email_data.get('attachments', []))}")
|
||||
|
||||
# First create the draft message
|
||||
response = requests.post(
|
||||
'https://graph.microsoft.com/v1.0/me/mailFolders/drafts/messages',
|
||||
headers=headers,
|
||||
|
|
@ -580,7 +574,7 @@ class AgentEmail(AgentBase):
|
|||
else:
|
||||
logger.error(f"Drafts folder method failed: {response.status_code} - {response.text}")
|
||||
|
||||
# Try fallback method with messages endpoint (Option 2)
|
||||
# Try fallback method with messages endpoint
|
||||
logger.info("Trying fallback with messages endpoint")
|
||||
response = requests.post(
|
||||
'https://graph.microsoft.com/v1.0/me/messages',
|
||||
|
|
|
|||
|
|
@ -393,7 +393,11 @@ class GatewayInterface:
|
|||
|
||||
def authenticateUser(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
||||
"""Authenticates a user by username and password."""
|
||||
# Instead of using UAM filtering, directly get user from database
|
||||
# Clear the users table from cache and reload it
|
||||
if "users" in self.db._tablesCache:
|
||||
del self.db._tablesCache["users"]
|
||||
|
||||
# Get fresh user data
|
||||
users = self.db.getRecordset("users")
|
||||
user = next((u for u in users if u.get("username") == username), None)
|
||||
|
||||
|
|
|
|||
522
static/37_gatewayInterface.py
Normal file
522
static/37_gatewayInterface.py
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
"""
|
||||
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."""
|
||||
# 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()
|
||||
|
||||
# Load user information
|
||||
self.currentUser = self._getCurrentUserInfo()
|
||||
|
||||
# Initialize standard records if needed
|
||||
self._initRecords()
|
||||
|
||||
def _getCurrentUserInfo(self) -> Dict[str, Any]:
|
||||
"""Gets information about the current user including privileges."""
|
||||
# For initialization, set default values
|
||||
userInfo = {
|
||||
"id": self.userId,
|
||||
"mandateId": self.mandateId,
|
||||
"privilege": "user", # Default privilege level
|
||||
"language": "en"
|
||||
}
|
||||
|
||||
# Try to load actual user info if IDs are provided
|
||||
if self.userId:
|
||||
userRecords = self.db.getRecordset("users", recordFilter={"id": self.userId})
|
||||
if userRecords:
|
||||
user = userRecords[0]
|
||||
userInfo["privilege"] = user.get("privilege", "user")
|
||||
userInfo["language"] = user.get("language", "en")
|
||||
|
||||
return userInfo
|
||||
|
||||
def _initializeDatabase(self):
|
||||
"""Initializes the database connection."""
|
||||
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
|
||||
)
|
||||
|
||||
def _initRecords(self):
|
||||
"""Initializes standard records in the database if they don't exist."""
|
||||
self._initRootMandate()
|
||||
self._initAdminUser()
|
||||
|
||||
def _initRootMandate(self):
|
||||
"""Creates the Root mandate if it doesn't exist."""
|
||||
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']
|
||||
|
||||
def _initAdminUser(self):
|
||||
"""Creates the Admin user if it doesn't exist."""
|
||||
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",
|
||||
"hashedPassword": self._getPasswordHash("The 1st Poweron 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']
|
||||
|
||||
def _uam(self, table: str, recordset: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Unified user access management function that filters data based on user privileges.
|
||||
|
||||
Args:
|
||||
table: Name of the table
|
||||
recordset: Recordset to filter based on access rules
|
||||
|
||||
Returns:
|
||||
Filtered recordset based on user privilege level
|
||||
"""
|
||||
userPrivilege = self.currentUser.get("privilege", "user")
|
||||
|
||||
# Apply filtering based on privilege
|
||||
if userPrivilege == "sysadmin":
|
||||
return recordset # System admins see all records
|
||||
elif userPrivilege == "admin":
|
||||
# Admins see records in their mandate
|
||||
return [r for r in recordset if r.get("mandateId") == self.mandateId]
|
||||
else: # Regular users
|
||||
# Users only see records they own within their mandate
|
||||
return [r for r in recordset
|
||||
if r.get("mandateId") == self.mandateId and r.get("userId") == self.userId]
|
||||
|
||||
def _canModify(self, table: str, recordId: Optional[int] = None) -> bool:
|
||||
"""
|
||||
Checks if the current user can modify (create/update/delete) records in a table.
|
||||
|
||||
Args:
|
||||
table: Name of the table
|
||||
recordId: Optional record ID for specific record check
|
||||
|
||||
Returns:
|
||||
Boolean indicating permission
|
||||
"""
|
||||
userPrivilege = self.currentUser.get("privilege", "user")
|
||||
|
||||
# System admins can modify anything
|
||||
if userPrivilege == "sysadmin":
|
||||
return True
|
||||
|
||||
# Check specific record permissions
|
||||
if recordId is not None:
|
||||
# Get the record to check ownership
|
||||
records = self.db.getRecordset(table, recordFilter={"id": recordId})
|
||||
if not records:
|
||||
return False
|
||||
|
||||
record = records[0]
|
||||
|
||||
# Admins can modify anything in their mandate
|
||||
if userPrivilege == "admin" and record.get("mandateId") == self.mandateId:
|
||||
# Exception: Can't modify Root mandate unless you are a sysadmin
|
||||
if table == "mandates" and recordId == 1 and userPrivilege != "sysadmin":
|
||||
return False
|
||||
return True
|
||||
|
||||
# Users can only modify their own records
|
||||
if (record.get("mandateId") == self.mandateId and
|
||||
record.get("userId") == self.userId):
|
||||
return True
|
||||
|
||||
return False
|
||||
else:
|
||||
# For general table modify permission (e.g., create)
|
||||
# Admins can create anything in their mandate
|
||||
if userPrivilege == "admin":
|
||||
return True
|
||||
|
||||
# Regular users can create most entities
|
||||
if table == "mandates":
|
||||
return False # Regular users can't create mandates
|
||||
return True
|
||||
|
||||
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 mandates based on user access level."""
|
||||
allMandates = self.db.getRecordset("mandates")
|
||||
return self._uam("mandates", allMandates)
|
||||
|
||||
def getMandate(self, mandateId: int) -> Optional[Dict[str, Any]]:
|
||||
"""Returns a mandate by ID if user has access."""
|
||||
mandates = self.db.getRecordset("mandates", recordFilter={"id": mandateId})
|
||||
if not mandates:
|
||||
return None
|
||||
|
||||
filteredMandates = self._uam("mandates", mandates)
|
||||
return filteredMandates[0] if filteredMandates else None
|
||||
|
||||
def createMandate(self, name: str, language: str = "de") -> Dict[str, Any]:
|
||||
"""Creates a new mandate if user has permission."""
|
||||
if not self._canModify("mandates"):
|
||||
raise PermissionError("No permission to create mandates")
|
||||
|
||||
mandateData = {
|
||||
"name": name,
|
||||
"language": language
|
||||
}
|
||||
|
||||
return self.db.recordCreate("mandates", mandateData)
|
||||
|
||||
def updateMandate(self, mandateId: int, mandateData: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Updates a mandate if user has access."""
|
||||
# Check if the mandate exists and user has access
|
||||
mandate = self.getMandate(mandateId)
|
||||
if not mandate:
|
||||
raise ValueError(f"Mandate with ID {mandateId} not found")
|
||||
|
||||
if not self._canModify("mandates", mandateId):
|
||||
raise PermissionError(f"No permission to update mandate {mandateId}")
|
||||
|
||||
# Update the mandate
|
||||
return self.db.recordModify("mandates", mandateId, mandateData)
|
||||
|
||||
def deleteMandate(self, mandateId: int) -> bool:
|
||||
"""
|
||||
Deletes a mandate and all associated users and data if user has permission.
|
||||
"""
|
||||
# Check if the mandate exists and user has access
|
||||
mandate = self.getMandate(mandateId)
|
||||
if not mandate:
|
||||
return False
|
||||
|
||||
if not self._canModify("mandates", mandateId):
|
||||
raise PermissionError(f"No permission to delete mandate {mandateId}")
|
||||
|
||||
# Check if it's the initial mandate
|
||||
initialMandateId = self.getInitialId("mandates")
|
||||
if initialMandateId is not None and mandateId == initialMandateId:
|
||||
logger.warning(f"Attempt to delete the Root mandate was prevented")
|
||||
return False
|
||||
|
||||
# Find all users of the mandate
|
||||
users = self.getUsersByMandate(mandateId)
|
||||
|
||||
# Delete all users of the mandate and their associated data
|
||||
for user in users:
|
||||
self.deleteUser(user["id"])
|
||||
|
||||
# Delete the mandate
|
||||
success = self.db.recordDelete("mandates", mandateId)
|
||||
|
||||
if success:
|
||||
logger.info(f"Mandate with ID {mandateId} was successfully deleted")
|
||||
else:
|
||||
logger.error(f"Error deleting mandate with ID {mandateId}")
|
||||
|
||||
return success
|
||||
|
||||
# User methods
|
||||
|
||||
def getAllUsers(self) -> List[Dict[str, Any]]:
|
||||
"""Returns users based on user access level."""
|
||||
allUsers = self.db.getRecordset("users")
|
||||
filteredUsers = self._uam("users", allUsers)
|
||||
|
||||
# Remove password hashes
|
||||
for user in filteredUsers:
|
||||
if "hashedPassword" in user:
|
||||
del user["hashedPassword"]
|
||||
|
||||
return filteredUsers
|
||||
|
||||
def getUsersByMandate(self, mandateId: int) -> List[Dict[str, Any]]:
|
||||
"""Returns users for a specific mandate if user has access."""
|
||||
# First check if user has access to the mandate
|
||||
mandate = self.getMandate(mandateId)
|
||||
if not mandate:
|
||||
return []
|
||||
|
||||
# Get users for this mandate
|
||||
users = self.db.getRecordset("users", recordFilter={"mandateId": mandateId})
|
||||
filteredUsers = self._uam("users", users)
|
||||
|
||||
# Remove password hashes
|
||||
for user in filteredUsers:
|
||||
if "hashedPassword" in user:
|
||||
del user["hashedPassword"]
|
||||
|
||||
return filteredUsers
|
||||
|
||||
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 if user has access."""
|
||||
users = self.db.getRecordset("users", recordFilter={"id": userId})
|
||||
if not users:
|
||||
return None
|
||||
|
||||
filteredUsers = self._uam("users", users)
|
||||
if not filteredUsers:
|
||||
return None
|
||||
|
||||
user = filteredUsers[0]
|
||||
|
||||
# Remove password hash
|
||||
if "hashedPassword" in user:
|
||||
userCopy = user.copy()
|
||||
del userCopy["hashedPassword"]
|
||||
return userCopy
|
||||
|
||||
return user
|
||||
|
||||
def createUser(self, username: str, password: str, email: str = None,
|
||||
fullName: str = None, language: str = "de", mandateId: int = None,
|
||||
disabled: bool = False, privilege: str = "user") -> Dict[str, Any]:
|
||||
"""Creates a new user if current user has permission."""
|
||||
# Check if the username already exists
|
||||
existingUser = self.getUserByUsername(username)
|
||||
if existingUser:
|
||||
raise ValueError(f"User '{username}' already exists")
|
||||
|
||||
# Use the provided mandateId or the current context
|
||||
userMandateId = mandateId if mandateId is not None else self.mandateId
|
||||
|
||||
# Check if user has access to the mandate
|
||||
if userMandateId != self.mandateId and self.currentUser.get("privilege") != "sysadmin":
|
||||
raise PermissionError(f"No permission to create users in mandate {userMandateId}")
|
||||
|
||||
if not self._canModify("users"):
|
||||
raise PermissionError("No permission to create users")
|
||||
|
||||
# Check privilege escalation
|
||||
if (privilege == "sysadmin" or
|
||||
(privilege == "admin" and self.currentUser.get("privilege") == "user")):
|
||||
raise PermissionError(f"Cannot create user with higher privilege: {privilege}")
|
||||
|
||||
userData = {
|
||||
"mandateId": userMandateId,
|
||||
"username": username,
|
||||
"email": email,
|
||||
"fullName": fullName,
|
||||
"disabled": disabled,
|
||||
"language": language,
|
||||
"privilege": privilege,
|
||||
"hashedPassword": self._getPasswordHash(password)
|
||||
}
|
||||
|
||||
createdUser = self.db.recordCreate("users", userData)
|
||||
|
||||
# Remove password hash from the response
|
||||
if "hashedPassword" in createdUser:
|
||||
del createdUser["hashedPassword"]
|
||||
|
||||
return createdUser
|
||||
|
||||
def authenticateUser(self, username: str, password: str) -> Optional[Dict[str, Any]]:
|
||||
"""Authenticates a user by username and password."""
|
||||
# Instead of using UAM filtering, directly get user from database
|
||||
users = self.db.getRecordset("users")
|
||||
user = next((u for u in users if u.get("username") == username), None)
|
||||
|
||||
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
|
||||
|
||||
|
||||
def updateUser(self, userId: int, userData: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Updates a user if current user has permission."""
|
||||
# Check if the user exists and current user has access
|
||||
user = self.getUser(userId)
|
||||
if not user:
|
||||
# Try to get the raw user record for admin access check
|
||||
users = self.db.getRecordset("users", recordFilter={"id": userId})
|
||||
if not users:
|
||||
raise ValueError(f"User with ID {userId} not found")
|
||||
|
||||
# Check if current user is admin/sysadmin
|
||||
if not self._canModify("users", userId):
|
||||
raise PermissionError(f"No permission to update user {userId}")
|
||||
|
||||
user = users[0]
|
||||
|
||||
# Check privilege escalation
|
||||
if "privilege" in userData:
|
||||
currentPrivilege = self.currentUser.get("privilege")
|
||||
targetPrivilege = userData["privilege"]
|
||||
|
||||
if (targetPrivilege == "sysadmin" and currentPrivilege != "sysadmin") or (
|
||||
targetPrivilege == "admin" and currentPrivilege == "user"):
|
||||
raise PermissionError(f"Cannot escalate privilege to {targetPrivilege}")
|
||||
|
||||
# If the password is being changed, hash it
|
||||
if "password" in userData:
|
||||
userData["hashedPassword"] = self._getPasswordHash(userData["password"])
|
||||
del userData["password"]
|
||||
|
||||
# Update the user
|
||||
updatedUser = self.db.recordModify("users", userId, userData)
|
||||
|
||||
# Remove password hash from the response
|
||||
if "hashedPassword" in updatedUser:
|
||||
del updatedUser["hashedPassword"]
|
||||
|
||||
return updatedUser
|
||||
|
||||
def disableUser(self, userId: int) -> Dict[str, Any]:
|
||||
"""Disables a user if current user has permission."""
|
||||
return self.updateUser(userId, {"disabled": True})
|
||||
|
||||
def enableUser(self, userId: int) -> Dict[str, Any]:
|
||||
"""Enables a user if current user has permission."""
|
||||
return self.updateUser(userId, {"disabled": False})
|
||||
|
||||
def _deleteUserReferencedData(self, userId: int) -> None:
|
||||
"""Deletes all data associated with a user."""
|
||||
# Delete user attributes
|
||||
try:
|
||||
attributes = self.db.getRecordset("attributes", recordFilter={"userId": userId})
|
||||
for attribute in attributes:
|
||||
self.db.recordDelete("attributes", attribute["id"])
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting attributes for user {userId}: {e}")
|
||||
|
||||
logger.info(f"All referenced data for user {userId} has been deleted")
|
||||
|
||||
def deleteUser(self, userId: int) -> bool:
|
||||
"""Deletes a user and all associated data if current user has permission."""
|
||||
# Check if the user exists
|
||||
users = self.db.getRecordset("users", recordFilter={"id": userId})
|
||||
if not users:
|
||||
return False
|
||||
|
||||
# Check if current user has permission
|
||||
if not self._canModify("users", userId):
|
||||
raise PermissionError(f"No permission to delete user {userId}")
|
||||
|
||||
# Check if it's the initial user
|
||||
initialUserId = self.getInitialId("users")
|
||||
if initialUserId is not None and userId == initialUserId:
|
||||
logger.warning("Attempt to delete the Root Admin was prevented")
|
||||
return False
|
||||
|
||||
# Delete all data associated with the user
|
||||
self._deleteUserReferencedData(userId)
|
||||
|
||||
# Delete the user
|
||||
success = self.db.recordDelete("users", userId)
|
||||
|
||||
if success:
|
||||
logger.info(f"User with ID {userId} was successfully deleted")
|
||||
else:
|
||||
logger.error(f"Error deleting user with ID {userId}")
|
||||
|
||||
return success
|
||||
|
||||
|
||||
# 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.
|
||||
"""
|
||||
contextKey = f"{mandateId}_{userId}"
|
||||
if contextKey not in _gatewayInterfaces:
|
||||
_gatewayInterfaces[contextKey] = GatewayInterface(mandateId, userId)
|
||||
return _gatewayInterfaces[contextKey]
|
||||
|
||||
# Initialize an instance
|
||||
getGatewayInterface()
|
||||
49
static/38_email_preview.html
Normal file
49
static/38_email_preview.html
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Email Preview: Review of Attached Python Modules for Attribute and Gateway Management</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; }
|
||||
.email-container { max-width: 600px; margin: 20px auto; background-color: white; border: 1px solid #ddd; border-radius: 5px; overflow: hidden; }
|
||||
.email-header { background-color: #f0f0f0; padding: 15px; border-bottom: 1px solid #ddd; }
|
||||
.email-content { padding: 20px; }
|
||||
.email-footer { background-color: #f0f0f0; padding: 15px; border-top: 1px solid #ddd; font-size: 12px; color: #666; }
|
||||
.field { margin-bottom: 10px; }
|
||||
.field-label { font-weight: bold; color: #555; }
|
||||
.email-body { margin-top: 20px; padding-top: 20px; border-top: 1px solid #eee; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="email-container">
|
||||
<div class="email-header">
|
||||
<h2>Email Template Preview</h2>
|
||||
</div>
|
||||
<div class="email-content">
|
||||
<div class="field">
|
||||
<div class="field-label">To:</div>
|
||||
<div>devteam@example.com</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="field-label">Subject:</div>
|
||||
<div>Review of Attached Python Modules for Attribute and Gateway Management</div>
|
||||
</div>
|
||||
<div class="email-body">
|
||||
<html>
|
||||
<body>
|
||||
<p>Hello Development Team,</p>
|
||||
<p>Please find attached the Python modules <strong>'defAttributes.py'</strong> and <strong>'gatewayInterface.py'</strong>. The <em>'defAttributes.py'</em> file defines a Pydantic model for attribute definitions and includes a function to convert it into a list of <code>AttributeDefinition</code> objects. The <em>'gatewayInterface.py'</em> file serves as an interface to the Gateway system, managing users and mandates.</p>
|
||||
<p>Kindly review the attached files and provide your feedback.</p>
|
||||
<p>Best regards,<br>[Your Name]</p>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</div>
|
||||
<div class="email-footer">
|
||||
<p>This is a preview of the email template. The actual email may appear differently in various email clients.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
6
static/39_email_template.json
Normal file
6
static/39_email_template.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"recipient": "devteam@example.com",
|
||||
"subject": "Review of Attached Python Modules for Attribute and Gateway Management",
|
||||
"plainBody": "Hello Development Team,\n\nPlease find attached the Python modules 'defAttributes.py' and 'gatewayInterface.py'. The 'defAttributes.py' file defines a Pydantic model for attribute definitions and includes a function to convert it into a list of AttributeDefinition objects. The 'gatewayInterface.py' file serves as an interface to the Gateway system, managing users and mandates.\n\nKindly review the attached files and provide your feedback.\n\nBest regards,\n[Your Name]",
|
||||
"htmlBody": "<html>\n<body>\n <p>Hello Development Team,</p>\n <p>Please find attached the Python modules <strong>'defAttributes.py'</strong> and <strong>'gatewayInterface.py'</strong>. The <em>'defAttributes.py'</em> file defines a Pydantic model for attribute definitions and includes a function to convert it into a list of <code>AttributeDefinition</code> objects. The <em>'gatewayInterface.py'</em> file serves as an interface to the Gateway system, managing users and mandates.</p>\n <p>Kindly review the attached files and provide your feedback.</p>\n <p>Best regards,<br>[Your Name]</p>\n</body>\n</html>"
|
||||
}
|
||||
Loading…
Reference in a new issue