| 1 | from typing import Optional |
| 2 | |
| 3 | from fastapi import APIRouter |
| 4 | from fastapi import Depends |
| 5 | from fastapi import HTTPException |
| 6 | from fastapi import Query |
| 7 | from fastapi import Security |
| 8 | from loguru import logger |
| 9 | from sqlalchemy.ext.asyncio import AsyncSession |
| 10 | |
| 11 | from app.auth.models.users import User |
| 12 | from app.auth.utils import AuthHandler |
| 13 | from app.db.db_session import get_db |
| 14 | from app.middleware.customer_access import customer_access_handler |
| 15 | from app.siem.schema.events import EventsQueryParams |
| 16 | from app.siem.schema.events import EventsQueryResponse |
| 17 | from app.siem.schema.events import FieldMappingsResponse |
| 18 | from app.siem.services.events import get_field_mappings |
| 19 | from app.siem.services.events import query_events |
| 20 | |
| 21 | siem_events_router = APIRouter() |
| 22 | |
| 23 | |
| 24 | @siem_events_router.get( |
| 25 | "/{customer_code}/{source_name}", |
| 26 | response_model=EventsQueryResponse, |
| 27 | description="Query events from a customer's event source with scroll-based pagination and optional Lucene query", |
| 28 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst", "customer_user"))], |
| 29 | ) |
| 30 | async def query_events_endpoint( |
| 31 | customer_code: str, |
| 32 | source_name: str, |
| 33 | timerange: str = Query("24h", description="Time range (e.g. '1h', '24h', '7d', '1w')"), |
| 34 | page_size: int = Query(50, ge=1, le=1000, description="Number of results per page"), |
| 35 | scroll_id: Optional[str] = Query(None, description="Scroll ID for fetching the next page"), |
| 36 | query: Optional[str] = Query(None, description="Lucene query string (e.g. 'agent_name:piHole AND agent_id:088')"), |
| 37 | time_from: Optional[str] = Query(None, description="Absolute start time in ISO format. Overrides timerange."), |
| 38 | time_to: Optional[str] = Query(None, description="Absolute end time in ISO format. Overrides timerange."), |
| 39 | current_user: User = Depends(AuthHandler().get_current_user), |
| 40 | db: AsyncSession = Depends(get_db), |
| 41 | ) -> EventsQueryResponse: |
| 42 | logger.info(f"Querying events for customer {customer_code}, source {source_name}") |
| 43 | |
| 44 | if not await customer_access_handler.check_customer_access(current_user, customer_code, db): |
| 45 | raise HTTPException(status_code=403, detail=f"Access denied to customer {customer_code}") |
| 46 | |
| 47 | params = EventsQueryParams( |
| 48 | timerange=timerange, |
| 49 | page_size=page_size, |
| 50 | scroll_id=scroll_id, |
| 51 | query=query, |
| 52 | time_from=time_from, |
| 53 | time_to=time_to, |
| 54 | ) |
| 55 | return await query_events(customer_code, source_name, params, db) |
| 56 | |
| 57 | |
| 58 | @siem_events_router.get( |
| 59 | "/{customer_code}/{source_name}/fields", |
| 60 | response_model=FieldMappingsResponse, |
| 61 | description="Get index field name mappings for a customer's event source to assist with building queries", |
| 62 | dependencies=[Security(AuthHandler().require_any_scope("admin", "analyst", "customer_user"))], |
| 63 | ) |
| 64 | async def get_field_mappings_endpoint( |
| 65 | customer_code: str, |
| 66 | source_name: str, |
| 67 | current_user: User = Depends(AuthHandler().get_current_user), |
| 68 | db: AsyncSession = Depends(get_db), |
| 69 | ) -> FieldMappingsResponse: |
| 70 | logger.info(f"Getting field mappings for customer {customer_code}, source {source_name}") |
| 71 | |
| 72 | if not await customer_access_handler.check_customer_access(current_user, customer_code, db): |
| 73 | raise HTTPException(status_code=403, detail=f"Access denied to customer {customer_code}") |
| 74 | |
| 75 | return await get_field_mappings(customer_code, source_name, db) |