feat(import): restrict WS subscriptions to initiating user or admins
This commit is contained in:
@@ -2464,6 +2464,43 @@ async def batch_import_csv_files(
|
|||||||
@router.websocket("/batch-progress/ws/{audit_id}")
|
@router.websocket("/batch-progress/ws/{audit_id}")
|
||||||
async def ws_import_batch_progress(websocket: WebSocket, audit_id: int):
|
async def ws_import_batch_progress(websocket: WebSocket, audit_id: int):
|
||||||
"""WebSocket: subscribe to real-time updates for an import audit using the pool."""
|
"""WebSocket: subscribe to real-time updates for an import audit using the pool."""
|
||||||
|
# Authenticate first (without accepting) to enforce authorization before subscribing
|
||||||
|
user = await websocket_manager.authenticate_websocket(websocket)
|
||||||
|
if not user:
|
||||||
|
# Authentication failure handled here to avoid accepting unauthorized connections
|
||||||
|
try:
|
||||||
|
await websocket.close(code=4401, reason="Authentication failed")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
# Authorization: only initiating user or admins may subscribe to this audit stream
|
||||||
|
db = SessionLocal()
|
||||||
|
try:
|
||||||
|
audit = db.query(ImportAudit).filter(ImportAudit.id == audit_id).first()
|
||||||
|
if not audit:
|
||||||
|
try:
|
||||||
|
await websocket.close(code=4404, reason="Batch not found")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
|
||||||
|
is_admin = bool(getattr(user, "is_admin", False))
|
||||||
|
if not is_admin and getattr(user, "id", None) != getattr(audit, "initiated_by_user_id", None):
|
||||||
|
import_logger.warning(
|
||||||
|
"Unauthorized WS subscription attempt for import batch",
|
||||||
|
audit_id=audit_id,
|
||||||
|
user_id=getattr(user, "id", None),
|
||||||
|
username=getattr(user, "username", None),
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
await websocket.close(code=4403, reason="Not authorized to subscribe to this batch")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
topic = f"import_batch_progress_{audit_id}"
|
topic = f"import_batch_progress_{audit_id}"
|
||||||
|
|
||||||
async def handle_ws_message(connection_id: str, message: WebSocketMessage):
|
async def handle_ws_message(connection_id: str, message: WebSocketMessage):
|
||||||
|
|||||||
Reference in New Issue
Block a user