Automated Action 2adbcd0535 Complete multi-tenant SaaS platform with external integrations
- Implemented comprehensive multi-tenant data isolation using database-level security
- Built JWT authentication system with role-based access control (Super Admin, Org Admin, User, Viewer)
- Created RESTful API endpoints for user and organization operations
- Added complete audit logging for all data modifications with IP tracking
- Implemented API rate limiting and input validation with security middleware
- Built webhook processing engine with async event handling and retry logic
- Created external API call handlers with circuit breaker pattern and error handling
- Implemented data synchronization between external services and internal data
- Added integration health monitoring and status tracking
- Created three mock external services (User Management, Payment, Communication)
- Implemented idempotency for webhook processing to handle duplicates gracefully
- Added comprehensive security headers and XSS/CSRF protection
- Set up Alembic database migrations with proper SQLite configuration
- Included extensive documentation and API examples

Architecture features:
- Multi-tenant isolation at database level
- Circuit breaker pattern for external API resilience
- Async background task processing
- Complete audit trail with user context
- Role-based permission system
- Webhook signature verification
- Request validation and sanitization
- Health monitoring endpoints

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-27 21:14:30 +00:00

166 lines
5.4 KiB
Python

from fastapi import HTTPException, status, Request
from sqlalchemy.orm import Session
from typing import Dict, Any
import logging
from app.services.webhook import WebhookService
from app.schemas.webhook import WebhookEventCreate
from app.models.integration import IntegrationType
from app.core.config import settings
logger = logging.getLogger(__name__)
class WebhookHandler:
def __init__(self, db: Session):
self.db = db
self.webhook_service = WebhookService(db)
async def handle_user_webhook(
self,
request: Request,
organization_id: int,
payload: Dict[str, Any]
):
"""Handle webhooks from user management service"""
# Verify webhook signature
signature = request.headers.get("x-webhook-signature")
if not signature:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing webhook signature"
)
body = await request.body()
if not self.webhook_service.verify_webhook_signature(
body, signature, settings.WEBHOOK_SECRET
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid webhook signature"
)
# Extract event data
event_id = payload.get("event_id")
event_type = payload.get("event_type")
if not event_id or not event_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing required fields: event_id, event_type"
)
# Create webhook event
webhook_data = WebhookEventCreate(
external_id=event_id,
event_type=event_type,
payload=payload,
integration_type=IntegrationType.USER_MANAGEMENT,
organization_id=organization_id
)
webhook_event = self.webhook_service.create_webhook_event(webhook_data)
logger.info(f"User webhook received: {event_type} - {event_id}")
return {"status": "accepted", "webhook_id": webhook_event.id}
async def handle_payment_webhook(
self,
request: Request,
organization_id: int,
payload: Dict[str, Any]
):
"""Handle webhooks from payment service"""
# Verify webhook signature
signature = request.headers.get("x-webhook-signature")
if not signature:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing webhook signature"
)
body = await request.body()
if not self.webhook_service.verify_webhook_signature(
body, signature, settings.WEBHOOK_SECRET
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid webhook signature"
)
# Extract event data
event_id = payload.get("event_id")
event_type = payload.get("event_type")
if not event_id or not event_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing required fields: event_id, event_type"
)
# Create webhook event
webhook_data = WebhookEventCreate(
external_id=event_id,
event_type=event_type,
payload=payload,
integration_type=IntegrationType.PAYMENT,
organization_id=organization_id
)
webhook_event = self.webhook_service.create_webhook_event(webhook_data)
logger.info(f"Payment webhook received: {event_type} - {event_id}")
return {"status": "accepted", "webhook_id": webhook_event.id}
async def handle_communication_webhook(
self,
request: Request,
organization_id: int,
payload: Dict[str, Any]
):
"""Handle webhooks from communication service"""
# Verify webhook signature
signature = request.headers.get("x-webhook-signature")
if not signature:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing webhook signature"
)
body = await request.body()
if not self.webhook_service.verify_webhook_signature(
body, signature, settings.WEBHOOK_SECRET
):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid webhook signature"
)
# Extract event data
event_id = payload.get("event_id")
event_type = payload.get("event_type")
if not event_id or not event_type:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Missing required fields: event_id, event_type"
)
# Create webhook event
webhook_data = WebhookEventCreate(
external_id=event_id,
event_type=event_type,
payload=payload,
integration_type=IntegrationType.COMMUNICATION,
organization_id=organization_id
)
webhook_event = self.webhook_service.create_webhook_event(webhook_data)
logger.info(f"Communication webhook received: {event_type} - {event_id}")
return {"status": "accepted", "webhook_id": webhook_event.id}