fix: load system bot credentials from DB and pass to browser bot for authenticated join
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
ad254aafb1
commit
a3c92ae8d5
2 changed files with 41 additions and 17 deletions
|
|
@ -195,20 +195,41 @@ async def startSession(
|
||||||
userId = str(context.user.id)
|
userId = str(context.user.id)
|
||||||
effectiveConfig = _getEffectiveConfig(instanceId, userId, interface)
|
effectiveConfig = _getEffectiveConfig(instanceId, userId, interface)
|
||||||
|
|
||||||
# Determine effective join mode and bot name.
|
# Determine effective join mode, bot name, and credentials
|
||||||
# NOTE: Authentication is currently disabled. The bot always joins as an anonymous
|
|
||||||
# guest with the system bot's display name. See Teamsbot-Auth-Join-Learnings.md.
|
|
||||||
# Credentials are NOT sent to the browser bot.
|
|
||||||
joinMode = body.joinMode or TeamsbotJoinMode.ANONYMOUS
|
joinMode = body.joinMode or TeamsbotJoinMode.ANONYMOUS
|
||||||
effectiveBotName = body.botName
|
effectiveBotName = body.botName
|
||||||
|
botAccountEmail = None
|
||||||
|
botAccountPassword = None
|
||||||
|
|
||||||
# If a system bot exists, use its display name as the bot name (e.g. "Nyla Larsson")
|
# Load system bot from DB (try mandate-specific first, then any active bot)
|
||||||
systemBot = interface.getActiveSystemBot(mandateId)
|
systemBot = interface.getActiveSystemBot(mandateId)
|
||||||
|
if not systemBot:
|
||||||
|
from .datamodelTeamsbot import TeamsbotSystemBot
|
||||||
|
allBots = interface.db.getRecordset(TeamsbotSystemBot, recordFilter={"isActive": True})
|
||||||
|
if allBots:
|
||||||
|
systemBot = allBots[0]
|
||||||
|
logger.info(f"No mandate-specific system bot, using fallback: {systemBot.get('name')} ({systemBot.get('email')})")
|
||||||
|
|
||||||
if systemBot:
|
if systemBot:
|
||||||
if not effectiveBotName:
|
if not effectiveBotName:
|
||||||
effectiveBotName = systemBot.get("name") or effectiveConfig.botName
|
effectiveBotName = systemBot.get("name") or effectiveConfig.botName
|
||||||
logger.info(f"System bot found: {systemBot.get('name')} ({systemBot.get('email')}), using name: {effectiveBotName}")
|
logger.info(f"System bot found: {systemBot.get('name')} ({systemBot.get('email')})")
|
||||||
|
|
||||||
|
# Load and decrypt credentials for authenticated join
|
||||||
|
botAccountEmail = systemBot.get("email")
|
||||||
|
encryptedPwd = systemBot.get("encryptedPassword")
|
||||||
|
if botAccountEmail and encryptedPwd:
|
||||||
|
try:
|
||||||
|
from modules.shared.configuration import decryptValue
|
||||||
|
botAccountPassword = decryptValue(encryptedPwd, userId=str(context.user.id), keyName="systemBotPassword")
|
||||||
|
logger.info(f"System bot credentials loaded and decrypted for: {botAccountEmail}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Could not decrypt system bot password: {e} — falling back to anonymous join")
|
||||||
|
botAccountEmail = None
|
||||||
|
botAccountPassword = None
|
||||||
|
else:
|
||||||
|
logger.info("No system bot found in DB — using anonymous join")
|
||||||
|
|
||||||
if not effectiveBotName:
|
if not effectiveBotName:
|
||||||
effectiveBotName = effectiveConfig.botName
|
effectiveBotName = effectiveConfig.botName
|
||||||
|
|
||||||
|
|
@ -216,17 +237,15 @@ async def startSession(
|
||||||
if effectiveBotName != (body.botName or config.botName):
|
if effectiveBotName != (body.botName or config.botName):
|
||||||
interface.updateSession(sessionId, {"botName": effectiveBotName})
|
interface.updateSession(sessionId, {"botName": effectiveBotName})
|
||||||
|
|
||||||
# Build session config — no credentials sent (auth disabled)
|
# Build session config
|
||||||
sessionConfig = effectiveConfig.model_copy(update={
|
sessionConfig = effectiveConfig.model_copy(update={
|
||||||
"botAccountEmail": None,
|
|
||||||
"botAccountPassword": None,
|
|
||||||
"botName": effectiveBotName,
|
"botName": effectiveBotName,
|
||||||
})
|
})
|
||||||
|
|
||||||
# Start the bot in background (join meeting via bridge)
|
# Start the bot in background — pass credentials separately (not in config)
|
||||||
service = TeamsbotService(context.user, mandateId, instanceId, sessionConfig)
|
service = TeamsbotService(context.user, mandateId, instanceId, sessionConfig)
|
||||||
asyncio.create_task(
|
asyncio.create_task(
|
||||||
service.joinMeeting(sessionId, cleanMeetingUrl, body.connectionId, gatewayBaseUrl)
|
service.joinMeeting(sessionId, cleanMeetingUrl, body.connectionId, gatewayBaseUrl, botAccountEmail, botAccountPassword)
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"Teamsbot session {sessionId} created for instance {instanceId}")
|
logger.info(f"Teamsbot session {sessionId} created for instance {instanceId}")
|
||||||
|
|
|
||||||
|
|
@ -90,14 +90,16 @@ class TeamsbotService:
|
||||||
meetingLink: str,
|
meetingLink: str,
|
||||||
connectionId: Optional[str] = None,
|
connectionId: Optional[str] = None,
|
||||||
gatewayBaseUrl: str = "",
|
gatewayBaseUrl: str = "",
|
||||||
|
botAccountEmail: Optional[str] = None,
|
||||||
|
botAccountPassword: Optional[str] = None,
|
||||||
):
|
):
|
||||||
"""Send join command to the Browser Bot service.
|
"""Send join command to the Browser Bot service.
|
||||||
|
|
||||||
The browser bot will:
|
The browser bot will:
|
||||||
1. Launch a headless browser
|
1. Launch browser (headful if credentials provided, headless otherwise)
|
||||||
2. Navigate to Teams web app
|
2. Navigate to Teams web app
|
||||||
3. Join the meeting as anonymous guest
|
3. Authenticate if credentials provided, otherwise join as anonymous guest
|
||||||
4. Enable captions and start scraping
|
4. Enable captions/audio capture and start scraping
|
||||||
5. Connect back via WebSocket to send transcripts
|
5. Connect back via WebSocket to send transcripts
|
||||||
"""
|
"""
|
||||||
from . import interfaceFeatureTeamsbot as interfaceDb
|
from . import interfaceFeatureTeamsbot as interfaceDb
|
||||||
|
|
@ -123,6 +125,9 @@ class TeamsbotService:
|
||||||
gatewayHost = gatewayBaseUrl.replace("https://", "").replace("http://", "").rstrip("/")
|
gatewayHost = gatewayBaseUrl.replace("https://", "").replace("http://", "").rstrip("/")
|
||||||
fullGatewayWsUrl = f"{wsScheme}://{gatewayHost}/api/teamsbot/{self.instanceId}/bot/ws/{sessionId}"
|
fullGatewayWsUrl = f"{wsScheme}://{gatewayHost}/api/teamsbot/{self.instanceId}/bot/ws/{sessionId}"
|
||||||
|
|
||||||
|
hasAuth = bool(botAccountEmail and botAccountPassword)
|
||||||
|
logger.info(f"Joining meeting for session {sessionId}: auth={hasAuth}, email={botAccountEmail or 'N/A'}, transferMode={self.config.transferMode}")
|
||||||
|
|
||||||
result = await self.browserBotConnector.joinMeeting(
|
result = await self.browserBotConnector.joinMeeting(
|
||||||
sessionId=sessionId,
|
sessionId=sessionId,
|
||||||
meetingUrl=meetingLink,
|
meetingUrl=meetingLink,
|
||||||
|
|
@ -130,8 +135,8 @@ class TeamsbotService:
|
||||||
instanceId=self.instanceId,
|
instanceId=self.instanceId,
|
||||||
gatewayWsUrl=fullGatewayWsUrl,
|
gatewayWsUrl=fullGatewayWsUrl,
|
||||||
language=self.config.language,
|
language=self.config.language,
|
||||||
botAccountEmail=self.config.botAccountEmail if hasattr(self.config, 'botAccountEmail') else None,
|
botAccountEmail=botAccountEmail,
|
||||||
botAccountPassword=self.config.botAccountPassword if hasattr(self.config, 'botAccountPassword') else None,
|
botAccountPassword=botAccountPassword,
|
||||||
transferMode=self.config.transferMode if hasattr(self.config, 'transferMode') else "auto",
|
transferMode=self.config.transferMode if hasattr(self.config, 'transferMode') else "auto",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue