gateway/modules/interfaces/interfaceBootstrap.py
2025-12-07 23:51:05 +01:00

964 lines
30 KiB
Python

"""
Centralized bootstrap interface for system initialization.
Contains all bootstrap logic including mandate, users, and RBAC rules.
"""
import logging
from typing import Optional, List, Dict, Any
from passlib.context import CryptContext
from modules.connectors.connectorDbPostgre import DatabaseConnector
from modules.shared.configuration import APP_CONFIG
from modules.datamodels.datamodelUam import (
Mandate,
UserInDB,
AuthAuthority,
)
from modules.datamodels.datamodelRbac import (
AccessRule,
AccessRuleContext,
Role,
)
from modules.datamodels.datamodelUam import AccessLevel
logger = logging.getLogger(__name__)
# Password-Hashing
pwdContext = CryptContext(schemes=["argon2"], deprecated="auto")
def initBootstrap(db: DatabaseConnector) -> None:
"""
Main bootstrap entry point - initializes all system components.
Args:
db: Database connector instance
"""
logger.info("Starting system bootstrap")
# Initialize root mandate
mandateId = initRootMandate(db)
# Initialize admin user
adminUserId = initAdminUser(db, mandateId)
# Initialize event user
eventUserId = initEventUser(db, mandateId)
# Initialize roles
initRoles(db)
# Initialize RBAC rules
initRbacRules(db)
# Assign initial user roles
if adminUserId and eventUserId:
assignInitialUserRoles(db, adminUserId, eventUserId)
logger.info("System bootstrap completed")
def initRootMandate(db: DatabaseConnector) -> Optional[str]:
"""
Creates the Root mandate if it doesn't exist.
Args:
db: Database connector instance
Returns:
Mandate ID if created or found, None otherwise
"""
existingMandates = db.getRecordset(Mandate)
if existingMandates:
mandateId = existingMandates[0].get("id")
logger.info(f"Root mandate already exists with ID {mandateId}")
return mandateId
logger.info("Creating Root mandate")
rootMandate = Mandate(name="Root", language="en", enabled=True)
createdMandate = db.recordCreate(Mandate, rootMandate)
mandateId = createdMandate.get("id")
logger.info(f"Root mandate created with ID {mandateId}")
return mandateId
def initAdminUser(db: DatabaseConnector, mandateId: Optional[str]) -> Optional[str]:
"""
Creates the Admin user if it doesn't exist.
Args:
db: Database connector instance
mandateId: Root mandate ID
Returns:
User ID if created or found, None otherwise
"""
existingUsers = db.getRecordset(UserInDB, recordFilter={"username": "admin"})
if existingUsers:
userId = existingUsers[0].get("id")
logger.info(f"Admin user already exists with ID {userId}")
return userId
logger.info("Creating Admin user")
adminUser = UserInDB(
mandateId=mandateId,
username="admin",
email="admin@example.com",
fullName="Administrator",
enabled=True,
language="en",
roleLabels=["sysadmin"],
authenticationAuthority=AuthAuthority.LOCAL,
hashedPassword=_getPasswordHash(APP_CONFIG.get("APP_INIT_PASS_ADMIN_SECRET")),
connections=[],
)
createdUser = db.recordCreate(UserInDB, adminUser)
userId = createdUser.get("id")
logger.info(f"Admin user created with ID {userId}")
return userId
def initEventUser(db: DatabaseConnector, mandateId: Optional[str]) -> Optional[str]:
"""
Creates the Event user if it doesn't exist.
Args:
db: Database connector instance
mandateId: Root mandate ID
Returns:
User ID if created or found, None otherwise
"""
existingUsers = db.getRecordset(UserInDB, recordFilter={"username": "event"})
if existingUsers:
userId = existingUsers[0].get("id")
logger.info(f"Event user already exists with ID {userId}")
return userId
logger.info("Creating Event user")
eventUser = UserInDB(
mandateId=mandateId,
username="event",
email="event@example.com",
fullName="Event",
enabled=True,
language="en",
roleLabels=["sysadmin"],
authenticationAuthority=AuthAuthority.LOCAL,
hashedPassword=_getPasswordHash(APP_CONFIG.get("APP_INIT_PASS_EVENT_SECRET")),
connections=[],
)
createdUser = db.recordCreate(UserInDB, eventUser)
userId = createdUser.get("id")
logger.info(f"Event user created with ID {userId}")
return userId
def initRoles(db: DatabaseConnector) -> None:
"""
Initialize standard roles if they don't exist.
Args:
db: Database connector instance
"""
logger.info("Initializing roles")
standardRoles = [
Role(
roleLabel="sysadmin",
description={"en": "System Administrator - Full access to all system resources", "fr": "Administrateur système - Accès complet à toutes les ressources"},
isSystemRole=True
),
Role(
roleLabel="admin",
description={"en": "Administrator - Manage users and resources within mandate scope", "fr": "Administrateur - Gérer les utilisateurs et ressources dans le périmètre du mandat"},
isSystemRole=True
),
Role(
roleLabel="user",
description={"en": "User - Standard user with access to own records", "fr": "Utilisateur - Utilisateur standard avec accès à ses propres enregistrements"},
isSystemRole=True
),
Role(
roleLabel="viewer",
description={"en": "Viewer - Read-only access to group records", "fr": "Visualiseur - Accès en lecture seule aux enregistrements du groupe"},
isSystemRole=True
),
]
existingRoles = db.getRecordset(Role)
existingRoleLabels = {role.get("roleLabel") for role in existingRoles}
for role in standardRoles:
if role.roleLabel not in existingRoleLabels:
try:
db.recordCreate(Role, role)
logger.info(f"Created role: {role.roleLabel}")
except Exception as e:
logger.warning(f"Error creating role {role.roleLabel}: {e}")
else:
logger.debug(f"Role {role.roleLabel} already exists")
logger.info("Roles initialization completed")
def initRbacRules(db: DatabaseConnector) -> None:
"""
Initialize RBAC rules if they don't exist.
Converts all UAM logic from interface*Access.py modules to RBAC rules.
Also checks for and adds missing rules for new tables.
Args:
db: Database connector instance
"""
existingRules = db.getRecordset(AccessRule)
if existingRules:
logger.info(f"RBAC rules already exist ({len(existingRules)} rules)")
# Check for missing rules for ChatWorkflow and Prompt tables
_addMissingTableRules(db, existingRules)
return
logger.info("Initializing RBAC rules")
# Create default role rules
createDefaultRoleRules(db)
# Create table-specific rules (converted from UAM logic)
createTableSpecificRules(db)
# Create UI context rules
createUiContextRules(db)
# Create RESOURCE context rules
createResourceContextRules(db)
logger.info("RBAC rules initialization completed")
def createDefaultRoleRules(db: DatabaseConnector) -> None:
"""
Create default role rules for generic access (item = null).
Args:
db: Database connector instance
"""
defaultRules = [
# SysAdmin Role - Full access to all
AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item=None,
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
),
# Admin Role - Group-level access
AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item=None,
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.NONE,
),
# User Role - My records only
AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item=None,
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
),
# Viewer Role - Read-only group access
AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item=None,
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
),
]
for rule in defaultRules:
db.recordCreate(AccessRule, rule)
logger.info(f"Created {len(defaultRules)} default role rules")
def createTableSpecificRules(db: DatabaseConnector) -> None:
"""
Create table-specific rules converted from UAM logic.
These rules override generic rules for specific tables.
Args:
db: Database connector instance
"""
tableRules = []
# Mandate table - Only sysadmin can access
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="Mandate",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="Mandate",
view=False,
read=AccessLevel.NONE,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="Mandate",
view=False,
read=AccessLevel.NONE,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="Mandate",
view=False,
read=AccessLevel.NONE,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# UserInDB table
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="UserInDB",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="UserInDB",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="UserInDB",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.MY,
delete=AccessLevel.NONE,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="UserInDB",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# UserConnection table
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="UserConnection",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="UserConnection",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="UserConnection",
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="UserConnection",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# DataNeutraliserConfig table
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="DataNeutraliserConfig",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="DataNeutraliserConfig",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="DataNeutraliserConfig",
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="DataNeutraliserConfig",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# DataNeutralizerAttributes table
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="DataNeutralizerAttributes",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="DataNeutralizerAttributes",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="DataNeutralizerAttributes",
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="DataNeutralizerAttributes",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# AuthEvent table
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="AuthEvent",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="AuthEvent",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="AuthEvent",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="AuthEvent",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# ChatWorkflow table - Users can access their own workflows
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="ChatWorkflow",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="ChatWorkflow",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="ChatWorkflow",
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="ChatWorkflow",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# Prompt table - Users can access their own prompts
tableRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.DATA,
item="Prompt",
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
tableRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.DATA,
item="Prompt",
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
tableRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.DATA,
item="Prompt",
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
tableRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.DATA,
item="Prompt",
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# Create all table-specific rules
for rule in tableRules:
db.recordCreate(AccessRule, rule)
logger.info(f"Created {len(tableRules)} table-specific rules")
def createUiContextRules(db: DatabaseConnector) -> None:
"""
Create UI context rules for controlling UI element visibility.
These rules control which UI components users can see based on their roles.
Args:
db: Database connector instance
"""
uiRules = []
# Generic UI rules - all roles can view UI by default
# Specific UI elements can override these with more restrictive rules
# Sysadmin - full UI access
uiRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.UI,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Admin - full UI access
uiRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.UI,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# User - full UI access
uiRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.UI,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Viewer - full UI access (can view but may have restricted actions)
uiRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.UI,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Create all UI context rules
for rule in uiRules:
db.recordCreate(AccessRule, rule)
logger.info(f"Created {len(uiRules)} UI context rules")
def createResourceContextRules(db: DatabaseConnector) -> None:
"""
Create RESOURCE context rules for controlling resource access (AI models, actions, etc.).
These rules control which resources users can access based on their roles.
Args:
db: Database connector instance
"""
resourceRules = []
# Generic resource rules - all roles can access resources by default
# Specific resources can override these with more restrictive rules
# Sysadmin - full resource access
resourceRules.append(AccessRule(
roleLabel="sysadmin",
context=AccessRuleContext.RESOURCE,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Admin - full resource access
resourceRules.append(AccessRule(
roleLabel="admin",
context=AccessRuleContext.RESOURCE,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# User - full resource access
resourceRules.append(AccessRule(
roleLabel="user",
context=AccessRuleContext.RESOURCE,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Viewer - full resource access (can view but may have restricted actions)
resourceRules.append(AccessRule(
roleLabel="viewer",
context=AccessRuleContext.RESOURCE,
item=None,
view=True,
read=None,
create=None,
update=None,
delete=None,
))
# Create all RESOURCE context rules
for rule in resourceRules:
db.recordCreate(AccessRule, rule)
logger.info(f"Created {len(resourceRules)} RESOURCE context rules")
def _addMissingTableRules(db: DatabaseConnector, existingRules: List[Dict[str, Any]]) -> None:
"""
Add missing RBAC rules for tables that were added after initial bootstrap.
Args:
db: Database connector instance
existingRules: List of existing AccessRule records
"""
# Check which tables already have rules
existingItems = {rule.get("item") for rule in existingRules if rule.get("context") == AccessRuleContext.DATA}
existingRoles = {rule.get("roleLabel") for rule in existingRules}
# Tables that need rules
requiredTables = ["ChatWorkflow", "Prompt"]
requiredRoles = ["sysadmin", "admin", "user", "viewer"]
newRules = []
for table in requiredTables:
if table not in existingItems:
logger.info(f"Adding missing RBAC rules for table {table}")
# ChatWorkflow rules
if table == "ChatWorkflow":
for roleLabel in requiredRoles:
if roleLabel == "sysadmin":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
elif roleLabel == "admin":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
elif roleLabel == "user":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
elif roleLabel == "viewer":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# Prompt rules (same as ChatWorkflow)
elif table == "Prompt":
for roleLabel in requiredRoles:
if roleLabel == "sysadmin":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.ALL,
create=AccessLevel.ALL,
update=AccessLevel.ALL,
delete=AccessLevel.ALL,
))
elif roleLabel == "admin":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.GROUP,
create=AccessLevel.GROUP,
update=AccessLevel.GROUP,
delete=AccessLevel.GROUP,
))
elif roleLabel == "user":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.MY,
create=AccessLevel.MY,
update=AccessLevel.MY,
delete=AccessLevel.MY,
))
elif roleLabel == "viewer":
newRules.append(AccessRule(
roleLabel=roleLabel,
context=AccessRuleContext.DATA,
item=table,
view=True,
read=AccessLevel.MY,
create=AccessLevel.NONE,
update=AccessLevel.NONE,
delete=AccessLevel.NONE,
))
# Create missing rules
if newRules:
for rule in newRules:
db.recordCreate(AccessRule, rule)
logger.info(f"Added {len(newRules)} missing RBAC rules")
def assignInitialUserRoles(db: DatabaseConnector, adminUserId: str, eventUserId: str) -> None:
"""
Assign initial roles to admin and event users.
Args:
db: Database connector instance
adminUserId: Admin user ID
eventUserId: Event user ID
"""
# Set context to admin user for bootstrap operations
originalUserId = db.userId if hasattr(db, 'userId') else None
try:
if adminUserId:
db.updateContext(adminUserId)
# Update admin user with sysadmin role
adminUser = db.getRecordset(UserInDB, recordFilter={"id": adminUserId})
if adminUser:
adminUserData = adminUser[0]
roleLabels = adminUserData.get("roleLabels") or []
if "sysadmin" not in roleLabels:
adminUserData["roleLabels"] = roleLabels + ["sysadmin"]
db.recordModify(UserInDB, adminUserId, adminUserData)
logger.info(f"Assigned sysadmin role to admin user {adminUserId}")
# Update event user with sysadmin role
eventUser = db.getRecordset(UserInDB, recordFilter={"id": eventUserId})
if eventUser:
eventUserData = eventUser[0]
roleLabels = eventUserData.get("roleLabels") or []
if "sysadmin" not in roleLabels:
eventUserData["roleLabels"] = roleLabels + ["sysadmin"]
db.recordModify(UserInDB, eventUserId, eventUserData)
logger.info(f"Assigned sysadmin role to event user {eventUserId}")
finally:
# Restore original context if it existed
if originalUserId:
db.updateContext(originalUserId)
elif hasattr(db, 'userId'):
# If original was None/empty, just set it directly
db.userId = originalUserId
def _getPasswordHash(password: Optional[str]) -> Optional[str]:
"""
Hash a password using Argon2.
Args:
password: Plain text password
Returns:
Hashed password or None if password is None
"""
if password is None:
return None
return pwdContext.hash(password)