92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from fastapi import FastAPI, HTTPException, Query
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
from case_rules import is_self_test_case
|
|
from case_schema import CaseCommentCreate, CaseCreate, CaseHayabusaLink, CaseUpdate
|
|
from case_storage import CaseStorage, ForensicsHostMismatchError
|
|
|
|
DB = Path(os.environ.get("AW_DLP_CASE_DB_PATH", "/opt/activitywatch/dlp-case-management/cases.db"))
|
|
APP = FastAPI(title="AWatch DLP Case Management")
|
|
APP.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=["http://127.0.0.1:5600", "http://localhost:5600", "http://10.10.10.13:5600", "*"],
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
STORE = CaseStorage(DB)
|
|
|
|
|
|
@APP.get("/health")
|
|
def health() -> dict[str, Any]:
|
|
return {"ok": True, "db": str(DB)}
|
|
|
|
|
|
@APP.post("/api/0/dlp/cases")
|
|
def create_case(payload: CaseCreate) -> dict[str, Any]:
|
|
if is_self_test_case(payload.incident_id, payload.title):
|
|
raise HTTPException(status_code=422, detail="self_test cases are not allowed")
|
|
return STORE.create_case(payload.model_dump(exclude_none=True), actor="api")
|
|
|
|
|
|
@APP.get("/api/0/dlp/cases")
|
|
def list_cases(
|
|
status: str | None = Query(default=None),
|
|
host: str | None = Query(default=None),
|
|
limit: int = Query(default=200, ge=1, le=2000),
|
|
) -> list[dict[str, Any]]:
|
|
return STORE.list_cases(status=status, host=host, limit=limit)
|
|
|
|
|
|
@APP.get("/api/0/dlp/cases/{case_id}")
|
|
def get_case(case_id: int) -> dict[str, Any]:
|
|
try:
|
|
case = STORE.get_case(case_id)
|
|
except KeyError:
|
|
raise HTTPException(status_code=404, detail="case not found")
|
|
case["comments"] = STORE.list_comments(case_id, limit=200)
|
|
case["audit"] = STORE.list_audit(case_id, limit=200)
|
|
return case
|
|
|
|
|
|
@APP.patch("/api/0/dlp/cases/{case_id}")
|
|
def update_case(case_id: int, payload: CaseUpdate) -> dict[str, Any]:
|
|
patch = payload.model_dump(exclude_none=True)
|
|
if not patch:
|
|
return STORE.get_case(case_id)
|
|
try:
|
|
return STORE.update_case(case_id, patch=patch, actor="api")
|
|
except KeyError:
|
|
raise HTTPException(status_code=404, detail="case not found")
|
|
|
|
|
|
@APP.post("/api/0/dlp/cases/{case_id}/comments")
|
|
def add_comment(case_id: int, payload: CaseCommentCreate) -> dict[str, Any]:
|
|
try:
|
|
STORE.get_case(case_id)
|
|
except KeyError:
|
|
raise HTTPException(status_code=404, detail="case not found")
|
|
return STORE.add_comment(case_id=case_id, comment=payload.comment, author=payload.author)
|
|
|
|
|
|
@APP.get("/api/0/dlp/cases/{case_id}/comments")
|
|
def list_comments(case_id: int, limit: int = Query(default=200, ge=1, le=2000)) -> list[dict[str, Any]]:
|
|
return STORE.list_comments(case_id=case_id, limit=limit)
|
|
|
|
|
|
@APP.post("/api/0/dlp/cases/{case_id}/forensics/hayabusa")
|
|
def link_hayabusa(case_id: int, payload: CaseHayabusaLink) -> dict[str, Any]:
|
|
try:
|
|
return STORE.link_hayabusa(case_id=case_id, payload=payload.model_dump(exclude_none=True), actor="api")
|
|
except ForensicsHostMismatchError as exc:
|
|
raise HTTPException(status_code=409, detail=str(exc))
|
|
except KeyError:
|
|
raise HTTPException(status_code=404, detail="case not found")
|