File size: 2,921 Bytes
a10e62e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | import logging
import json
from typing import Dict, Any, Optional
from fastapi import APIRouter, Depends, HTTPException, Header, Request, Body
from sqlalchemy.orm import Session
from core.database import get_db
from core.models import TenantIntegration
from core.integration_registry import IntegrationRegistry
from core.tenant_discovery import TenantDiscoveryService
from core.webhook_security import verify_slack_webhook
from api.routes.webhooks.base import get_webhook_registry
from api.routes.webhooks.webhook_bridge import webhook_bridge
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/slack", tags=["Slack Webhooks"])
@router.post("")
async def slack_webhook(
request: Request,
x_slack_signature: str = Header(None),
x_slack_request_timestamp: str = Header(None),
db: Session = Depends(get_db),
registry: IntegrationRegistry = Depends(get_webhook_registry)
):
"""
Unified Slack webhook callback.
Standardizes events and dispatches via the IntegrationRegistry.
"""
body = await request.body()
try:
data = json.loads(body)
except Exception:
raise HTTPException(status_code=400, detail="Invalid JSON")
# 1. Challenge Response (System requirement)
if data.get("type") == "url_verification":
return {"challenge": data.get("challenge")}
# 2. Workspace Resolution (Tenant Isolation)
team_id = data.get("team_id")
if not team_id:
# Check inside event if not top-level
team_id = data.get("event", {}).get("team")
if not team_id:
logger.warning("Slack webhook missing team_id")
raise HTTPException(status_code=400, detail="Missing team_id")
# Resolve tenant using Discovery Service
discoverer = TenantDiscoveryService(db)
tenant_id = await discoverer.get_tenant_id_by_external_id("slack", team_id)
if not tenant_id:
logger.warning(f"No tenant found for Slack team_id: {team_id}")
# Return 200/202 to avoid Slack retries, but log it
return {"status": "ignored", "reason": "tenant_not_found"}
# 3. Security Verification
integration = db.query(TenantIntegration).filter(
TenantIntegration.tenant_id == tenant_id,
TenantIntegration.connector_id == "slack"
).first()
if integration and integration.config:
signing_secret = integration.config.get("slack_signing_secret")
if signing_secret:
if not verify_slack_webhook(body, x_slack_signature, x_slack_request_timestamp, signing_secret):
logger.error(f"Unauthorized Slack webhook for tenant {tenant_id}")
raise HTTPException(status_code=401, detail="Invalid signature")
# 4. Dispatch via Webhook Bridge
result = await webhook_bridge.process_event(
"slack",
tenant_id,
data.get("event", {}),
registry,
db
)
return result
|