3cd0f6fd99
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
Both backend (PATCH /users/{id}) and frontend (Edit User modal) now block
an admin from changing their own primary role away from admin or removing
admin from their own extra_roles, closing off a self-lockout scenario.
133 lines
4.4 KiB
Python
133 lines
4.4 KiB
Python
"""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_cannot_remove_own_admin_role(api, auth_headers, admin_user):
|
|
resp = api(
|
|
"patch", f"/api/v1/users/{admin_user.id}", auth_headers,
|
|
json={"role": "viewer"},
|
|
)
|
|
assert resp.status_code == 400
|
|
assert "admin" in resp.text.lower()
|
|
|
|
|
|
def test_admin_cannot_drop_admin_via_extra_roles_either(api, auth_headers, admin_user):
|
|
# Give themselves a second role first, then try to swap the primary
|
|
# role away from admin without admin anywhere in extra_roles.
|
|
resp = api(
|
|
"patch", f"/api/v1/users/{admin_user.id}", auth_headers,
|
|
json={"role": "red_lead", "extra_roles": ["viewer"]},
|
|
)
|
|
assert resp.status_code == 400
|
|
|
|
|
|
def test_admin_can_switch_primary_role_if_admin_kept_in_extra_roles(api, auth_headers, admin_user):
|
|
resp = api(
|
|
"patch", f"/api/v1/users/{admin_user.id}", auth_headers,
|
|
json={"role": "red_lead", "extra_roles": ["admin"]},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
|
|
|
|
def test_admin_can_edit_own_other_fields_without_touching_role(api, auth_headers, admin_user):
|
|
resp = api(
|
|
"patch", f"/api/v1/users/{admin_user.id}", auth_headers,
|
|
json={"full_name": "Renamed Admin"},
|
|
)
|
|
assert resp.status_code == 200, resp.text
|
|
assert resp.json()["role"] == "admin"
|
|
|
|
|
|
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
|