- TestTemplate/TemplateSuggestion creation and updates now reject any
mitre_technique_id that doesn't match a real, already-synced MITRE
ATT&CK technique. Frontend swaps the free-text ID input for a
technique picker.
- Test Catalog now shows pending template suggestions before the
catalog grid, with full field detail (platform, severity, tool,
atomic ID, source URL, remediation) instead of just name/procedure.
- A manager can edit and directly re-approve a campaign they previously
rejected, instead of needing the original lead to resubmit it.
A manager organizes and validates work, so their own campaigns skip the
draft -> submit -> pending_approval queue and go straight to active with
the start_date they provide (they're the same role that would otherwise
approve it). Manager can also now create tests from the catalog, same as
red_lead/blue_lead.
- Approve endpoint now only creates Jira tickets immediately when
start_date is now/past; a new periodic job (every 15 min) catches
campaigns whose scheduled start_date has since arrived.
- Recurring campaign clones now go to pending_approval instead of
active, routing through the same manager-approval gate as any other
campaign; managers are notified instead of red_tech.
- Fix UTC conversion for the campaign approval start_date input and
extract shared isoToDatetimeLocal/datetimeLocalToIso helpers.
- /auth/refresh now allows a short grace window past expiry and checks
the blacklist, so an active session's silent refresh no longer fails
the instant its own token expires
- normal manager /approve flow now creates Jira tickets for the campaign
and its already-linked tests, matching the admin-only /activate path
- GenerateFromActorPayload now accepts start_date and threads it through
to the new campaign instead of silently discarding it
Now that only the manager sets start_date, and only at the moment of
approval (which immediately activates the campaign), a draft campaign
can never have a start_date. This made three things permanently
unreachable: the hourly _run_scheduled_campaign_activation cron job,
the activate_campaign 409/force future-date guard, and a query filter
hiding tests from scheduled-but-inactive draft campaigns. Removes all
three rather than leaving dead code behind. Also adds the missing
manager-cannot-directly-activate router test.
Moves the local _post cookie-clearing helper into a shared 'api' fixture
in conftest.py so later router tests in this plan (Tasks 10/11) don't
have to reinvent or forget the TestClient cookie-vs-Authorization-header
gotcha. Also adds a one-line comment at both require_any_role("manager")
call sites clarifying admin passthrough is automatic.