"""Data sources management endpoints (admin only). Provides a centralized panel for managing all external data sources (Atomic Red Team, Sigma, LOLBAS, GTFOBins, CALDERA, Elastic, etc.) including sync triggers, enable/disable toggles, and statistics. """ from fastapi import APIRouter, Depends from pydantic import BaseModel from sqlalchemy.orm import Session from typing import Optional from app.database import get_db from app.dependencies.auth import require_role from app.domain.unit_of_work import UnitOfWork from app.models.user import User from app.services.audit_service import log_action from app.services.data_source_service import ( get_source_stats, list_sources, sync_all_sources, sync_source, update_source, ) # --------------------------------------------------------------------------- # Pydantic schemas for request validation # --------------------------------------------------------------------------- class DataSourceUpdate(BaseModel): """Payload for updating a data source — only allowed fields.""" is_enabled: Optional[bool] = None sync_frequency: Optional[str] = None config: Optional[dict] = None router = APIRouter(prefix="/data-sources", tags=["data-sources"]) # --------------------------------------------------------------------------- # Endpoints # --------------------------------------------------------------------------- @router.get("") def list_data_sources( db: Session = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """List all registered data sources. **Requires** the ``admin`` role. """ return list_sources(db) @router.patch("/{source_id}") def update_data_source( source_id: str, body: DataSourceUpdate, db: Session = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """Update a data source (enable/disable, change config). **Requires** the ``admin`` role. """ update_data = body.model_dump(exclude_unset=True) with UnitOfWork(db) as uow: update_source(db, source_id, **update_data) log_action( db, user_id=current_user.id, action="update_data_source", entity_type="data_source", entity_id=source_id, details={"updates": update_data}, ) uow.commit() return {"message": "Data source updated", "id": source_id} @router.post("/{source_id}/sync") def sync_data_source( source_id: str, db: Session = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """Trigger sync/import for a specific data source. **Requires** the ``admin`` role. """ return sync_source(db, source_id) @router.post("/sync-all") def sync_all_data_sources( db: Session = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """Trigger sync for all enabled data sources (sequentially). **Requires** the ``admin`` role. """ results = sync_all_sources(db) with UnitOfWork(db) as uow: log_action( db, user_id=current_user.id, action="sync_all_data_sources", entity_type="data_source", entity_id=None, details={"results": results}, ) uow.commit() return {"message": "Sync all complete", "results": results} @router.get("/{source_id}/stats") def get_data_source_stats( source_id: str, db: Session = Depends(get_db), current_user: User = Depends(require_role("admin")), ): """Get detailed statistics for a specific data source. **Requires** the ``admin`` role. """ return get_source_stats(db, source_id)