fix: on_behalf_of in WS auth for BFF proxy, debug logging in broadcast
Some checks failed
Deploy Tracker / deploy (push) Failing after 2s
Some checks failed
Deploy Tracker / deploy (push) Failing after 2s
This commit is contained in:
parent
6a6eaada3e
commit
f50182bc52
@ -36,6 +36,18 @@ async def init_db():
|
|||||||
)
|
)
|
||||||
session.add(admin)
|
session.add(admin)
|
||||||
|
|
||||||
|
# Create BFF service member with TRACKER_TOKEN
|
||||||
|
bff_service = Member(
|
||||||
|
name="BFF Service",
|
||||||
|
slug="bff",
|
||||||
|
type="bridge",
|
||||||
|
role="bridge",
|
||||||
|
auth_method="token",
|
||||||
|
token="tb-tracker-dev-token",
|
||||||
|
status="offline",
|
||||||
|
)
|
||||||
|
session.add(bff_service)
|
||||||
|
|
||||||
# Create lobby chat
|
# Create lobby chat
|
||||||
lobby = Chat(
|
lobby = Chat(
|
||||||
kind="lobby",
|
kind="lobby",
|
||||||
@ -44,7 +56,7 @@ async def init_db():
|
|||||||
session.add(lobby)
|
session.add(lobby)
|
||||||
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
logger.info("Seed data created: admin user + lobby chat (id=%s)", lobby.id)
|
logger.info("Seed data created: admin user, BFF service, lobby chat (id=%s)", lobby.id)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
@ -29,7 +29,8 @@ async def websocket_endpoint(ws: WebSocket):
|
|||||||
return
|
return
|
||||||
|
|
||||||
token = auth_msg.get("token", "")
|
token = auth_msg.get("token", "")
|
||||||
slug = await _authenticate(ws, token)
|
on_behalf_of = auth_msg.get("on_behalf_of")
|
||||||
|
slug = await _authenticate(ws, token, on_behalf_of=on_behalf_of)
|
||||||
if not slug:
|
if not slug:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -77,8 +78,12 @@ async def websocket_endpoint(ws: WebSocket):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _authenticate(ws: WebSocket, token: str) -> str | None:
|
async def _authenticate(ws: WebSocket, token: str, on_behalf_of: str | None = None) -> str | None:
|
||||||
"""Authenticate by token, return slug or None."""
|
"""Authenticate by token, return slug or None.
|
||||||
|
|
||||||
|
If on_behalf_of is set and the token belongs to a bridge member,
|
||||||
|
use on_behalf_of slug instead (BFF proxying for web users).
|
||||||
|
"""
|
||||||
async with async_session() as db:
|
async with async_session() as db:
|
||||||
result = await db.execute(
|
result = await db.execute(
|
||||||
select(Member).where(Member.token == token).options(selectinload(Member.agent_config))
|
select(Member).where(Member.token == token).options(selectinload(Member.agent_config))
|
||||||
@ -103,6 +108,26 @@ async def _authenticate(ws: WebSocket, token: str) -> str | None:
|
|||||||
await ws.close()
|
await ws.close()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
# BFF proxy: bridge member can act on behalf of a user
|
||||||
|
effective_slug = member.slug
|
||||||
|
effective_type = member.type
|
||||||
|
if on_behalf_of and member.type == "bridge":
|
||||||
|
# Look up the actual user
|
||||||
|
user_result = await db.execute(
|
||||||
|
select(Member).where(Member.slug == on_behalf_of)
|
||||||
|
.options(selectinload(Member.agent_config))
|
||||||
|
)
|
||||||
|
user_member = user_result.scalar_one_or_none()
|
||||||
|
if user_member:
|
||||||
|
effective_slug = user_member.slug
|
||||||
|
effective_type = user_member.type
|
||||||
|
member = user_member # use user's settings
|
||||||
|
logger.info("Bridge %s acting on behalf of %s", member.slug, effective_slug)
|
||||||
|
else:
|
||||||
|
# User not found, use a synthetic slug to avoid collisions
|
||||||
|
effective_slug = on_behalf_of
|
||||||
|
logger.info("Bridge acting on behalf of unknown user %s", effective_slug)
|
||||||
|
|
||||||
# Get listen modes
|
# Get listen modes
|
||||||
chat_listen = "all"
|
chat_listen = "all"
|
||||||
task_listen = "all"
|
task_listen = "all"
|
||||||
@ -113,8 +138,8 @@ async def _authenticate(ws: WebSocket, token: str) -> str | None:
|
|||||||
# Register connection
|
# Register connection
|
||||||
client = ConnectedClient(
|
client = ConnectedClient(
|
||||||
ws=ws,
|
ws=ws,
|
||||||
member_slug=member.slug,
|
member_slug=effective_slug,
|
||||||
member_type=member.type,
|
member_type=effective_type,
|
||||||
chat_listen=chat_listen,
|
chat_listen=chat_listen,
|
||||||
task_listen=task_listen,
|
task_listen=task_listen,
|
||||||
)
|
)
|
||||||
@ -148,7 +173,7 @@ async def _authenticate(ws: WebSocket, token: str) -> str | None:
|
|||||||
await ws.send_json({
|
await ws.send_json({
|
||||||
"type": "auth.ok",
|
"type": "auth.ok",
|
||||||
"data": {
|
"data": {
|
||||||
"slug": member.slug,
|
"slug": effective_slug,
|
||||||
"lobby_chat_id": str(lobby_chat.id) if lobby_chat else None,
|
"lobby_chat_id": str(lobby_chat.id) if lobby_chat else None,
|
||||||
"projects": project_list,
|
"projects": project_list,
|
||||||
"online": online,
|
"online": online,
|
||||||
|
|||||||
@ -44,6 +44,9 @@ class ConnectionManager:
|
|||||||
|
|
||||||
async def broadcast_message(self, project_id: str, message: dict, author_slug: str):
|
async def broadcast_message(self, project_id: str, message: dict, author_slug: str):
|
||||||
"""Broadcast message.new — filtered by chat_listen."""
|
"""Broadcast message.new — filtered by chat_listen."""
|
||||||
|
logger.info("broadcast_message: project=%s, author=%s, clients=%s", project_id, author_slug, list(self.clients.keys()))
|
||||||
|
for slug, client in list(self.clients.items()):
|
||||||
|
logger.info(" checking %s: subscribed=%s, chat_listen=%s", slug, client.subscribed_projects, client.chat_listen)
|
||||||
mentions = message.get("mentions", [])
|
mentions = message.get("mentions", [])
|
||||||
for slug, client in list(self.clients.items()):
|
for slug, client in list(self.clients.items()):
|
||||||
if slug == author_slug:
|
if slug == author_slug:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user