diff --git a/backend/app/routers/techniques.py b/backend/app/routers/techniques.py index 805eee7..bd53dab 100644 --- a/backend/app/routers/techniques.py +++ b/backend/app/routers/techniques.py @@ -254,7 +254,7 @@ def review_technique( # Entry: repo repo: SATechniqueRepository = Depends(get_technique_repository), # Entry: current_user - current_user: User = Depends(require_any_role("red_lead", "blue_lead")), + current_user: User = Depends(require_any_role("red_lead", "blue_lead", "manager")), ) -> TechniqueOut: """Mark a technique as reviewed. diff --git a/backend/tests/test_review_queue_manager_access.py b/backend/tests/test_review_queue_manager_access.py new file mode 100644 index 0000000..9e4d07a --- /dev/null +++ b/backend/tests/test_review_queue_manager_access.py @@ -0,0 +1,25 @@ +"""Manager must have the same Review Queue access as red_lead/blue_lead — +mark_reviewed was previously red_lead/blue_lead (and admin) only.""" + + +def test_manager_can_mark_technique_reviewed(client, db, api, auth_headers, manager_headers): + resp = api( + "post", "/api/v1/techniques", auth_headers, + json={"mitre_id": "T1059.400", "name": "Manager Review Access Technique"}, + ) + assert resp.status_code == 201, resp.text + + resp = api("patch", "/api/v1/techniques/T1059.400/review", manager_headers) + assert resp.status_code == 200, resp.text + assert resp.json()["review_required"] is False + + +def test_red_tech_cannot_mark_technique_reviewed(client, db, api, auth_headers, red_tech_headers): + resp = api( + "post", "/api/v1/techniques", auth_headers, + json={"mitre_id": "T1059.401", "name": "Red Tech No Access Technique"}, + ) + assert resp.status_code == 201, resp.text + + resp = api("patch", "/api/v1/techniques/T1059.401/review", red_tech_headers) + assert resp.status_code == 403 diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5256c3a..6f5627c 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -59,7 +59,7 @@ export default function App() { + }> } diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 9169486..bd27f8a 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -43,7 +43,7 @@ const mainLinks: NavItem[] = [ icon: Grid3X3, children: [ { to: "/matrix", label: "Coverage Matrix", icon: Grid3X3 }, - { to: "/techniques/review-queue", label: "Review Queue", icon: ClipboardCheck, roles: ["admin", "red_lead", "blue_lead"] }, + { to: "/techniques/review-queue", label: "Review Queue", icon: ClipboardCheck, roles: ["admin", "red_lead", "blue_lead", "manager"] }, ], }, { @@ -159,7 +159,7 @@ export default function Sidebar() { const isAdmin = role === "admin"; const canSeeReviewQueue = - isAdmin || role === "red_lead" || role === "blue_lead"; + isAdmin || role === "red_lead" || role === "blue_lead" || role === "manager"; // Fetch review queue count for the badge (only for roles that can see it) const { data: reviewQueue } = useQuery({ diff --git a/frontend/src/pages/ReviewQueuePage.tsx b/frontend/src/pages/ReviewQueuePage.tsx index d70e18f..c0fd8d8 100644 --- a/frontend/src/pages/ReviewQueuePage.tsx +++ b/frontend/src/pages/ReviewQueuePage.tsx @@ -336,7 +336,8 @@ export default function ReviewQueuePage() { const canReview = user?.role === "admin" || user?.role === "red_lead" || - user?.role === "blue_lead"; + user?.role === "blue_lead" || + user?.role === "manager"; const [expandedId, setExpandedId] = useState(null); diff --git a/frontend/src/pages/SettingsPage.tsx b/frontend/src/pages/SettingsPage.tsx index f381d3b..c3c6ce5 100644 --- a/frontend/src/pages/SettingsPage.tsx +++ b/frontend/src/pages/SettingsPage.tsx @@ -1693,7 +1693,6 @@ export default function SettingsPage() { const { user } = useAuth(); const role = user?.role ?? "viewer"; const isAdmin = role === "admin"; - const isLead = ["admin", "red_lead", "blue_lead"].includes(role); const [activeTab, setActiveTab] = useState("profile"); @@ -1705,7 +1704,7 @@ export default function SettingsPage() { id: "webhooks", label: "Webhooks", icon: Webhook, - show: isLead, + show: isAdmin, }, { id: "email", label: "Email / SMTP", icon: Mail, show: isAdmin }, { id: "jira", label: "Jira", icon: Link2, show: isAdmin }, @@ -1760,7 +1759,7 @@ export default function SettingsPage() { )} - {activeTab === "webhooks" && isLead && ( + {activeTab === "webhooks" && isAdmin && (