feat(tests,users): blocking procedure-suggestion review on test detail, multi-role switcher, Power Automate webhook payload
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

- A lead opening a test with a pending procedure suggestion awaiting
  their review now gets a blocking popup (approve/reject only) instead
  of discovering it later in a separate queue.
- Users can be granted more than one role via extra_roles; only one is
  ever active at a time (no permission mixing) and a top-bar switcher
  lets the user swap which one, taking effect immediately since role
  is read fresh from the DB on every request.
- The password-setup/reset webhook now posts the agreed Power Automate
  contract ({to, subject, body} with the platform's standard greeting
  and signature template) and supports an admin-configured API key
  sent as an x-api-key header.
This commit is contained in:
kitos
2026-07-20 09:29:36 +02:00
parent 4dea19cae9
commit bbe7f49c86
24 changed files with 708 additions and 49 deletions
+96
View File
@@ -0,0 +1,96 @@
"""A user can be granted more than one role but only ever acts under a
single active one at a time — switching swaps which role is active
instead of ever mixing permissions from more than one.
"""
def test_admin_can_grant_extra_roles(api, auth_headers):
created = api(
"post", "/api/v1/users", auth_headers,
json={"full_name": "Multi Role User", "email": "multirole@test.com", "role": "red_tech"},
)
assert created.status_code == 201, created.text
user_id = created.json()["id"]
resp = api(
"patch", f"/api/v1/users/{user_id}", auth_headers,
json={"extra_roles": ["blue_tech", "red_lead"]},
)
assert resp.status_code == 200, resp.text
assert resp.json()["role"] == "red_tech"
assert set(resp.json()["extra_roles"]) == {"blue_tech", "red_lead"}
def test_admin_cannot_grant_invalid_extra_role(api, auth_headers):
created = api(
"post", "/api/v1/users", auth_headers,
json={"full_name": "Bad Extra Role", "email": "badextrarole@test.com", "role": "viewer"},
)
user_id = created.json()["id"]
resp = api(
"patch", f"/api/v1/users/{user_id}", auth_headers,
json={"extra_roles": ["superuser"]},
)
assert resp.status_code == 400
def test_user_can_switch_to_a_granted_extra_role(api, db, auth_headers):
from app.auth import hash_password
from app.models.user import User
user = User(
username="switcher@test.com",
email="switcher@test.com",
full_name="Switcher",
hashed_password=hash_password("SwitcherPass123!@#"),
role="red_tech",
extra_roles=["blue_tech"],
must_change_password=False,
)
db.add(user)
db.commit()
login = api(
"post", "/api/v1/auth/login", {},
data={"username": "switcher@test.com", "password": "SwitcherPass123!@#"},
)
assert login.status_code == 200, login.text
token = login.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
resp = api("post", "/api/v1/users/me/switch-role", headers, json={"role": "blue_tech"})
assert resp.status_code == 200, resp.text
body = resp.json()
assert body["role"] == "blue_tech"
assert body["extra_roles"] == ["red_tech"]
me = api("get", "/api/v1/auth/me", headers)
assert me.json()["role"] == "blue_tech"
def test_user_cannot_switch_to_an_ungranted_role(api, db, auth_headers):
from app.auth import hash_password
from app.models.user import User
user = User(
username="switcher2@test.com",
email="switcher2@test.com",
full_name="Switcher Two",
hashed_password=hash_password("SwitcherPass123!@#"),
role="red_tech",
extra_roles=["blue_tech"],
must_change_password=False,
)
db.add(user)
db.commit()
login = api(
"post", "/api/v1/auth/login", {},
data={"username": "switcher2@test.com", "password": "SwitcherPass123!@#"},
)
token = login.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
resp = api("post", "/api/v1/users/me/switch-role", headers, json={"role": "admin"})
assert resp.status_code == 400