feat(campaigns): timeline endpoint, admin-only manual activation, remove lead-set dates
This commit is contained in:
@@ -56,6 +56,10 @@ from app.services.campaign_crud_service import (
|
||||
get_campaign_history as crud_get_history,
|
||||
)
|
||||
|
||||
from app.services.campaign_crud_service import (
|
||||
get_campaign_timeline as crud_get_timeline,
|
||||
)
|
||||
|
||||
# Import from app.services.campaign_crud_service
|
||||
from app.services.campaign_crud_service import (
|
||||
get_campaign_progress_data as crud_get_progress,
|
||||
@@ -149,7 +153,6 @@ class CampaignCreate(BaseModel):
|
||||
tags: Optional[list[str]] = Field(default_factory=list)
|
||||
# Assign scheduled_at = None
|
||||
scheduled_at: Optional[str] = None
|
||||
start_date: Optional[str] = None # ISO date — campaign won't activate before this
|
||||
|
||||
|
||||
# Define class CampaignUpdate
|
||||
@@ -168,7 +171,6 @@ class CampaignUpdate(BaseModel):
|
||||
tags: Optional[list[str]] = None
|
||||
# Assign scheduled_at = None
|
||||
scheduled_at: Optional[str] = None
|
||||
start_date: Optional[str] = None # ISO date — can be updated while still in draft
|
||||
|
||||
|
||||
# Define class AddTestPayload
|
||||
@@ -327,7 +329,6 @@ def create_campaign(
|
||||
tags=payload.tags,
|
||||
# Keyword argument: scheduled_at
|
||||
scheduled_at=payload.scheduled_at,
|
||||
start_date=payload.start_date,
|
||||
)
|
||||
campaign_id = result["id"]
|
||||
log_action(
|
||||
@@ -786,8 +787,8 @@ def activate_campaign(
|
||||
campaign_id: str,
|
||||
force: bool = Query(False, description="Activate even if start_date is in the future"),
|
||||
db: Session = Depends(get_db),
|
||||
# Entry: current_user
|
||||
current_user: User = Depends(require_any_role("red_lead", "blue_lead")),
|
||||
# admin passes automatically via require_any_role's built-in bypass — do not add "admin" here
|
||||
current_user: User = Depends(require_any_role("admin")),
|
||||
):
|
||||
"""Activate a campaign, moving it from draft to active.
|
||||
|
||||
@@ -796,7 +797,7 @@ def activate_campaign(
|
||||
activates immediately regardless of start_date.
|
||||
"""
|
||||
from fastapi import HTTPException
|
||||
campaign_obj = db.query(Campaign).filter(Campaign.id == campaign_id).first()
|
||||
campaign_obj = db.query(Campaign).filter(Campaign.id == uuid.UUID(campaign_id)).first()
|
||||
if campaign_obj and campaign_obj.start_date and not force:
|
||||
now = datetime.utcnow()
|
||||
if campaign_obj.start_date > now:
|
||||
@@ -967,7 +968,7 @@ def get_campaign_progress_endpoint(
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class GenerateFromActorPayload(BaseModel):
|
||||
start_date: Optional[str] = None # ISO date YYYY-MM-DD
|
||||
pass
|
||||
|
||||
|
||||
@router.post("/from-threat-actor/{actor_id}", status_code=201)
|
||||
@@ -993,14 +994,10 @@ def generate_campaign_from_actor(
|
||||
Returns:
|
||||
dict: Serialised representation of the newly generated campaign.
|
||||
"""
|
||||
start_date_parsed = (
|
||||
datetime.fromisoformat(payload.start_date) if payload.start_date else None
|
||||
)
|
||||
campaign = generate_campaign_from_threat_actor(
|
||||
db,
|
||||
uuid.UUID(actor_id),
|
||||
current_user,
|
||||
start_date=start_date_parsed,
|
||||
)
|
||||
|
||||
# Open context manager
|
||||
@@ -1130,6 +1127,20 @@ def get_campaign_history(
|
||||
return crud_get_history(db, campaign_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /campaigns/{id}/timeline — Audit-log history for this campaign
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.get("/{campaign_id}/timeline")
|
||||
def get_campaign_timeline_endpoint(
|
||||
campaign_id: str,
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
) -> list:
|
||||
"""Return the chronological audit-log history for a campaign."""
|
||||
return crud_get_timeline(db, campaign_id)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /campaigns/{id}/timing-summary — Aggregated timing across campaign tests
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user