"""User management endpoint tests.""" from __future__ import annotations from flask.testing import FlaskClient from backend.app.auth import hash_password from backend.app.extensions import db from backend.app.models import User, UserRole from backend.tests.conftest import auth_headers as _h def test_list_users_admin_only( client: FlaskClient, admin_user: User, admin_token: str ) -> None: resp = client.get("/api/users", headers=_h(admin_token)) assert resp.status_code == 200 body = resp.get_json() assert isinstance(body, list) assert any(u["username"] == "admin1" for u in body) assert all("password_hash" not in u for u in body) def test_list_users_forbidden_for_redteam( client: FlaskClient, redteam_token: str ) -> None: resp = client.get("/api/users", headers=_h(redteam_token)) assert resp.status_code == 403 def test_list_users_forbidden_for_soc(client: FlaskClient, soc_token: str) -> None: resp = client.get("/api/users", headers=_h(soc_token)) assert resp.status_code == 403 def test_list_users_unauth(client: FlaskClient) -> None: resp = client.get("/api/users") assert resp.status_code == 401 def test_create_user_success(client: FlaskClient, admin_token: str) -> None: resp = client.post( "/api/users", headers=_h(admin_token), json={"username": "newbie", "password": "longenough1", "role": "redteam"}, ) assert resp.status_code == 201 body = resp.get_json() assert body["username"] == "newbie" assert body["role"] == "redteam" assert "password_hash" not in body def test_create_user_duplicate_username( client: FlaskClient, admin_user: User, admin_token: str ) -> None: resp = client.post( "/api/users", headers=_h(admin_token), json={"username": "admin1", "password": "longenough1", "role": "redteam"}, ) assert resp.status_code == 400 assert "exists" in resp.get_json()["error"] def test_create_user_short_password(client: FlaskClient, admin_token: str) -> None: resp = client.post( "/api/users", headers=_h(admin_token), json={"username": "short", "password": "abc", "role": "soc"}, ) assert resp.status_code == 400 assert "8 characters" in resp.get_json()["error"] def test_create_user_invalid_role(client: FlaskClient, admin_token: str) -> None: resp = client.post( "/api/users", headers=_h(admin_token), json={"username": "x", "password": "longenough1", "role": "godmode"}, ) assert resp.status_code == 400 def test_create_user_forbidden_for_non_admin( client: FlaskClient, redteam_token: str ) -> None: resp = client.post( "/api/users", headers=_h(redteam_token), json={"username": "x", "password": "longenough1", "role": "soc"}, ) assert resp.status_code == 403 def test_patch_user_change_role( client: FlaskClient, admin_token: str, soc_user: User ) -> None: resp = client.patch( f"/api/users/{soc_user.id}", headers=_h(admin_token), json={"role": "redteam"}, ) assert resp.status_code == 200 assert resp.get_json()["role"] == "redteam" def test_patch_user_change_password( client: FlaskClient, admin_token: str, soc_user: User ) -> None: resp = client.patch( f"/api/users/{soc_user.id}", headers=_h(admin_token), json={"password": "anotherone1"}, ) assert resp.status_code == 200 # New password should now allow login. login = client.post( "/api/auth/login", json={"username": "soc1", "password": "anotherone1"} ) assert login.status_code == 200 def test_patch_user_short_password( client: FlaskClient, admin_token: str, soc_user: User ) -> None: resp = client.patch( f"/api/users/{soc_user.id}", headers=_h(admin_token), json={"password": "no"}, ) assert resp.status_code == 400 def test_patch_user_404(client: FlaskClient, admin_token: str) -> None: resp = client.patch( "/api/users/9999", headers=_h(admin_token), json={"role": "soc"} ) assert resp.status_code == 404 def test_patch_user_forbidden_for_redteam( client: FlaskClient, redteam_token: str, soc_user: User ) -> None: resp = client.patch( f"/api/users/{soc_user.id}", headers=_h(redteam_token), json={"role": "admin"} ) assert resp.status_code == 403 def test_delete_user_success( client: FlaskClient, admin_token: str, soc_user: User ) -> None: resp = client.delete(f"/api/users/{soc_user.id}", headers=_h(admin_token)) assert resp.status_code == 204 def test_delete_last_admin_blocked( client: FlaskClient, admin_user: User, admin_token: str ) -> None: resp = client.delete(f"/api/users/{admin_user.id}", headers=_h(admin_token)) assert resp.status_code == 409 assert "last admin" in resp.get_json()["error"] def test_delete_admin_when_other_admin_exists( client: FlaskClient, admin_user: User, admin_token: str ) -> None: other = User( username="admin2", password_hash=hash_password("adminpass2"), role=UserRole.ADMIN, ) db.session.add(other) db.session.commit() other_id = other.id resp = client.delete(f"/api/users/{other_id}", headers=_h(admin_token)) assert resp.status_code == 204 def test_demote_last_admin_blocked( client: FlaskClient, admin_user: User, admin_token: str ) -> None: resp = client.patch( f"/api/users/{admin_user.id}", headers=_h(admin_token), json={"role": "redteam"}, ) assert resp.status_code == 409 def test_delete_user_404(client: FlaskClient, admin_token: str) -> None: resp = client.delete("/api/users/9999", headers=_h(admin_token)) assert resp.status_code == 404 def test_delete_user_forbidden_for_soc( client: FlaskClient, soc_token: str, redteam_user: User ) -> None: resp = client.delete(f"/api/users/{redteam_user.id}", headers=_h(soc_token)) assert resp.status_code == 403