From 65128db7133338c81b80e19048b5203ffd054189 Mon Sep 17 00:00:00 2001 From: patrick-motsch Date: Fri, 13 Feb 2026 17:26:56 +0100 Subject: [PATCH] fix: use original session user for bridge callbacks instead of system user (RBAC) Co-authored-by: Cursor --- .../features/teamsbot/routeFeatureTeamsbot.py | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/modules/features/teamsbot/routeFeatureTeamsbot.py b/modules/features/teamsbot/routeFeatureTeamsbot.py index 19d495a5..9821fc6c 100644 --- a/modules/features/teamsbot/routeFeatureTeamsbot.py +++ b/modules/features/teamsbot/routeFeatureTeamsbot.py @@ -363,11 +363,22 @@ async def bridgeStatusCallback( logger.info(f"Bridge status callback: session={sessionId}, status={status}") try: - # Update session status (bridge callbacks have no user context) + # Load the original user who started the session (has RBAC roles in mandate) from modules.datamodels.datamodelUam import User + from modules.interfaces.interfaceDbApp import getRootInterface + systemUser = User(id="system", username="system", email="system@poweron.swiss") interface = interfaceDb.getInterface(systemUser, featureInstanceId=instanceId) + # Look up original user from session for consistent context + session = interface.getSession(sessionId) + startedByUserId = session.get("startedByUserId") if session else None + if startedByUserId: + rootInterface = getRootInterface() + originalUser = rootInterface.getUser(startedByUserId) + if originalUser: + interface = interfaceDb.getInterface(originalUser, featureInstanceId=instanceId) + updates = {"status": status} if errorMessage: updates["errorMessage"] = errorMessage @@ -411,16 +422,27 @@ async def bridgeAudioWebsocket( config = _getInstanceConfig(instanceId) logger.info(f"Bridge audio WebSocket config loaded: session={sessionId}") + # Load the original user who started the session (has RBAC roles in mandate) + # Bridge callbacks have no HTTP auth, so we reconstruct the user context from the session record. from modules.datamodels.datamodelUam import User - systemUser = User(id="system", username="system", email="system@poweron.swiss") + from modules.interfaces.interfaceDbApp import getRootInterface - # Look up mandateId from the session record (needed for AI billing context) + systemUser = User(id="system", username="system", email="system@poweron.swiss") sessionInterface = interfaceDb.getInterface(systemUser, featureInstanceId=instanceId) session = sessionInterface.getSession(sessionId) mandateId = session.get("mandateId") if session else None + startedByUserId = session.get("startedByUserId") if session else None - service = TeamsbotService(systemUser, mandateId, instanceId, config) - logger.info(f"Bridge audio WebSocket service created: session={sessionId}, mandateId={mandateId}") + # Look up the original user (getRootInterface uses admin context, can load any user) + rootInterface = getRootInterface() + originalUser = rootInterface.getUser(startedByUserId) if startedByUserId else None + + if not originalUser: + logger.warning(f"Could not load original user {startedByUserId}, falling back to system user") + originalUser = systemUser + + service = TeamsbotService(originalUser, mandateId, instanceId, config) + logger.info(f"Bridge audio WebSocket service created: session={sessionId}, mandateId={mandateId}, user={originalUser.id}") await service.handleAudioStream(websocket, sessionId) except WebSocketDisconnect: