""" Security Incidents API router for ClaudeTools. This module defines all REST API endpoints for managing security incidents. """ from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Path, Query, status from sqlalchemy.orm import Session from api.database import get_db from api.middleware.auth import get_current_user from api.schemas.security_incident import ( SecurityIncidentCreate, SecurityIncidentResponse, SecurityIncidentUpdate, ) from api.services import security_incident_service # Create router with prefix and tags router = APIRouter() @router.get( "", response_model=dict, summary="List all security incidents", description="Retrieve a paginated list of all security incidents", status_code=status.HTTP_200_OK, ) def list_security_incidents( skip: int = Query( default=0, ge=0, description="Number of records to skip for pagination" ), limit: int = Query( default=100, ge=1, le=1000, description="Maximum number of records to return (max 1000)" ), db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ List all security incidents with pagination. - **skip**: Number of incidents to skip (default: 0) - **limit**: Maximum number of incidents to return (default: 100, max: 1000) Returns a list of security incidents with pagination metadata. Incidents are ordered by incident_date descending (most recent first). """ try: incidents, total = security_incident_service.get_security_incidents(db, skip, limit) return { "total": total, "skip": skip, "limit": limit, "incidents": [SecurityIncidentResponse.model_validate(incident) for incident in incidents] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve security incidents: {str(e)}" ) @router.get( "/{incident_id}", response_model=SecurityIncidentResponse, summary="Get security incident by ID", description="Retrieve a single security incident by its unique identifier", status_code=status.HTTP_200_OK, ) def get_security_incident( incident_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get a specific security incident by ID. - **incident_id**: UUID of the security incident to retrieve Returns the complete security incident details including investigation findings, remediation steps, and current status. """ incident = security_incident_service.get_security_incident_by_id(db, incident_id) return SecurityIncidentResponse.model_validate(incident) @router.post( "", response_model=SecurityIncidentResponse, summary="Create new security incident", description="Create a new security incident record", status_code=status.HTTP_201_CREATED, ) def create_security_incident( incident_data: SecurityIncidentCreate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Create a new security incident. Records a new security incident including the incident type, severity, affected resources, and initial description. Status defaults to 'investigating'. Requires a valid JWT token with appropriate permissions. """ incident = security_incident_service.create_security_incident(db, incident_data) return SecurityIncidentResponse.model_validate(incident) @router.put( "/{incident_id}", response_model=SecurityIncidentResponse, summary="Update security incident", description="Update an existing security incident's details", status_code=status.HTTP_200_OK, ) def update_security_incident( incident_id: UUID, incident_data: SecurityIncidentUpdate, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Update an existing security incident. - **incident_id**: UUID of the security incident to update Only provided fields will be updated. All fields are optional. Commonly updated fields include status, findings, remediation_steps, and resolved_at timestamp. """ incident = security_incident_service.update_security_incident(db, incident_id, incident_data) return SecurityIncidentResponse.model_validate(incident) @router.delete( "/{incident_id}", response_model=dict, summary="Delete security incident", description="Delete a security incident by its ID", status_code=status.HTTP_200_OK, ) def delete_security_incident( incident_id: UUID, db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Delete a security incident. - **incident_id**: UUID of the security incident to delete This is a permanent operation and cannot be undone. Consider setting status to 'resolved' instead of deleting for audit purposes. """ return security_incident_service.delete_security_incident(db, incident_id) @router.get( "/by-client/{client_id}", response_model=dict, summary="Get security incidents by client", description="Retrieve all security incidents for a specific client", status_code=status.HTTP_200_OK, ) def get_security_incidents_by_client( client_id: UUID, skip: int = Query(default=0, ge=0, description="Number of records to skip"), limit: int = Query(default=100, ge=1, le=1000, description="Maximum number of records to return"), db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get all security incidents for a specific client. - **client_id**: UUID of the client - **skip**: Number of incidents to skip (default: 0) - **limit**: Maximum number of incidents to return (default: 100, max: 1000) Returns incidents ordered by incident_date descending (most recent first). """ try: incidents, total = security_incident_service.get_security_incidents_by_client( db, client_id, skip, limit ) return { "total": total, "skip": skip, "limit": limit, "client_id": str(client_id), "incidents": [SecurityIncidentResponse.model_validate(incident) for incident in incidents] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve security incidents for client: {str(e)}" ) @router.get( "/by-status/{status_filter}", response_model=dict, summary="Get security incidents by status", description="Retrieve all security incidents with a specific status", status_code=status.HTTP_200_OK, ) def get_security_incidents_by_status( status_filter: str = Path(..., description="Status: investigating, contained, resolved, monitoring"), skip: int = Query(default=0, ge=0, description="Number of records to skip"), limit: int = Query(default=100, ge=1, le=1000, description="Maximum number of records to return"), db: Session = Depends(get_db), current_user: dict = Depends(get_current_user), ): """ Get all security incidents with a specific status. - **status_filter**: Status to filter by (investigating, contained, resolved, monitoring) - **skip**: Number of incidents to skip (default: 0) - **limit**: Maximum number of incidents to return (default: 100, max: 1000) Returns incidents ordered by incident_date descending (most recent first). """ try: incidents, total = security_incident_service.get_security_incidents_by_status( db, status_filter, skip, limit ) return { "total": total, "skip": skip, "limit": limit, "status": status_filter, "incidents": [SecurityIncidentResponse.model_validate(incident) for incident in incidents] } except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to retrieve security incidents by status: {str(e)}" )