9d66c30127
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
red_lead/blue_lead never saw a plain "All Tests" list — they were always routed into their own review two-queue view or "My Tasks", with no way to see every test and who's actually working it. Adds an always-visible section at the end of the Tests page for leads showing every test with both Red and Blue assignee columns, so they can monitor the queues and spot a stuck ticket or an overloaded operator. The list endpoint (GET /tests) now joinedloads the assignee relationships and resolves usernames the same way the detail endpoint already does, so the new section doesn't depend on GET /users/operators (lead/manager-only).
85 lines
3.2 KiB
Python
85 lines
3.2 KiB
Python
"""GET /tests/{id} must resolve assignee IDs into usernames directly, since
|
|
GET /users/operators (the only other source of that mapping) is restricted
|
|
to leads/managers — a plain operator viewing their own assigned test has no
|
|
other way to find out who they're looking at. Regression: the detail page
|
|
badge showed "RT: Unassigned" for the assigned operator themselves."""
|
|
|
|
from app.models.test import Test
|
|
from app.models.technique import Technique
|
|
|
|
|
|
def _seed_technique(db) -> Technique:
|
|
technique = Technique(
|
|
mitre_id="T9996", name="Assignee Username Test Technique",
|
|
tactic="execution", platforms=["linux"],
|
|
)
|
|
db.add(technique)
|
|
db.commit()
|
|
db.refresh(technique)
|
|
return technique
|
|
|
|
|
|
def _seed_test(db, technique, created_by, **overrides) -> Test:
|
|
test = Test(technique_id=technique.id, name="Assignee username test", created_by=created_by, **overrides)
|
|
db.add(test)
|
|
db.commit()
|
|
db.refresh(test)
|
|
return test
|
|
|
|
|
|
def test_red_tech_assignee_username_resolved_for_the_assigned_operator(
|
|
client, db, red_tech_headers, red_tech_user, red_lead_user,
|
|
):
|
|
technique = _seed_technique(db)
|
|
test = _seed_test(db, technique, red_lead_user.id, red_tech_assignee=red_tech_user.id)
|
|
|
|
resp = client.get(f"/api/v1/tests/{test.id}", headers=red_tech_headers)
|
|
|
|
assert resp.status_code == 200, resp.text
|
|
body = resp.json()
|
|
assert body["red_tech_assignee"] == str(red_tech_user.id)
|
|
assert body["red_tech_assignee_username"] == red_tech_user.username
|
|
|
|
|
|
def test_blue_tech_assignee_username_resolved(client, db, red_lead_headers, red_lead_user, blue_tech_user):
|
|
technique = _seed_technique(db)
|
|
test = _seed_test(db, technique, red_lead_user.id, blue_tech_assignee=blue_tech_user.id)
|
|
|
|
resp = client.get(f"/api/v1/tests/{test.id}", headers=red_lead_headers)
|
|
|
|
assert resp.status_code == 200, resp.text
|
|
assert resp.json()["blue_tech_assignee_username"] == blue_tech_user.username
|
|
|
|
|
|
def test_assignee_username_is_none_when_unassigned(client, db, red_lead_headers, red_lead_user):
|
|
technique = _seed_technique(db)
|
|
test = _seed_test(db, technique, red_lead_user.id)
|
|
|
|
resp = client.get(f"/api/v1/tests/{test.id}", headers=red_lead_headers)
|
|
|
|
assert resp.status_code == 200, resp.text
|
|
body = resp.json()
|
|
assert body["red_tech_assignee"] is None
|
|
assert body["red_tech_assignee_username"] is None
|
|
|
|
|
|
def test_list_endpoint_resolves_assignee_usernames_for_team_queue_overview(
|
|
client, db, red_lead_headers, red_lead_user, red_tech_user, blue_tech_user,
|
|
):
|
|
"""The leads' 'All Tests — Team Queue Overview' section reads assignee
|
|
usernames off GET /tests (the list endpoint), not just the detail
|
|
endpoint — both must resolve them the same way."""
|
|
technique = _seed_technique(db)
|
|
_seed_test(
|
|
db, technique, red_lead_user.id,
|
|
red_tech_assignee=red_tech_user.id, blue_tech_assignee=blue_tech_user.id,
|
|
)
|
|
|
|
resp = client.get("/api/v1/tests", headers=red_lead_headers)
|
|
|
|
assert resp.status_code == 200, resp.text
|
|
body = resp.json()
|
|
match = next(t for t in body if t["red_tech_assignee"] == str(red_tech_user.id))
|
|
assert match["red_tech_assignee_username"] == red_tech_user.username
|
|
assert match["blue_tech_assignee_username"] == blue_tech_user.username
|