"""Admin operations integration tests. Covers user suspension, MFA removal, password reset, and hard deletion. All endpoints require admin/superadmin privileges. """ import pytest from tests.integration.client.base import ApiError from gatehouse_app.utils.constants import OrganizationRole def assert_success(response: dict, message_contains: str = "") -> dict: data = response.get("data", {}) assert response.get("success") is not False, ( f"Expected success but got error: {response.get('message')}" ) if message_contains: assert message_contains.lower() in response.get("message", "").lower() return data def assert_error(exc: ApiError, expected_status: int, expected_error_type: str | None = None): assert exc.status_code == expected_status, ( f"Expected status {expected_status} but got {exc.status_code}" ) if expected_error_type: assert exc.error_type == expected_error_type class TestAdminUserManagement: """Test admin-only user management endpoints.""" def test_list_users_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-01 — List all users as admin. WHAT: Create an admin user, login, then GET /admin/users. WHY: The user management page needs a paginated user list. EXPECTED: 200 OK with users array. """ admin = create_test_user(password="AdminPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.list_users() data = assert_success(result) assert "users" in data or "count" in data def test_list_users_non_admin_negative(self, integration_client, create_test_user): """TEST: ADMIN-02 — Reject listing users as non-admin. WHAT: Regular user attempts GET /admin/users. WHY: User lists contain sensitive data; must be admin-only. EXPECTED: 403 Forbidden. """ user = create_test_user(password="MyPassword123!") integration_client.auth.login(email=user["email"], password="MyPassword123!") with pytest.raises(ApiError) as exc_info: integration_client.admin.list_users() assert exc_info.value.status_code == 403 def test_suspend_user_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-03 — Suspend user account. WHAT: Admin suspends a user, then verify the user cannot login. WHY: Suspension is a critical security tool for compromised accounts. EXPECTED: 200 OK on suspend. Login returns 403. """ admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.suspend_user(victim["id"]) assert_success(result) # Verify victim cannot login integration_client.auth.logout() with pytest.raises(ApiError) as exc_info: integration_client.auth.login(email=victim["email"], password="VictimPass123!") assert exc_info.value.status_code == 403 def test_unsuspend_user_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-05 — Unsuspend user account. WHAT: Admin suspends then unsuspends a user, verify they can login again. WHY: False positives happen; admins must be able to restore access. EXPECTED: 200 OK on unsuspend. Login succeeds afterwards. """ admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") integration_client.admin.suspend_user(victim["id"]) result = integration_client.admin.unsuspend_user(victim["id"]) assert_success(result) integration_client.auth.logout() login_result = integration_client.auth.login(email=victim["email"], password="VictimPass123!") assert_success(login_result, "login successful") def test_admin_verify_email_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-07 — Admin verifies user email. WHAT: Create an unverified user, admin calls verify endpoint. WHY: Admins may need to bypass verification for support reasons. EXPECTED: 200 OK, user.email_verified becomes True. """ from gatehouse_app.models.user.user import User from gatehouse_app.extensions import db admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!", email_verified=False) org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.verify_user_email(victim["id"]) assert_success(result) with integration_app.app_context(): user = User.query.get(victim["id"]) assert user.email_verified is True def test_admin_set_password_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-08 — Admin sets user password. WHAT: Admin overrides a user's password, then verify the user can login with the new password. WHY: Account recovery when user has lost access to email/MFA. EXPECTED: 200 OK. Login with new password succeeds. """ admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.set_user_password(victim["id"], "NewAdminSet456!") assert_success(result) integration_client.auth.logout() login_result = integration_client.auth.login(email=victim["email"], password="NewAdminSet456!") assert_success(login_result, "login successful") def test_admin_remove_totp_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-10 — Admin removes user TOTP. WHAT: User enrolls TOTP, admin removes it. WHY: Account recovery when user lost their authenticator. EXPECTED: 200 OK. TOTP status returns disabled. """ import pyotp admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) # Victim enrolls TOTP integration_client.auth.login(email=victim["email"], password="VictimPass123!") enroll = integration_client.mfa.enroll_totp() secret = enroll["data"]["secret"] integration_client.mfa.verify_enrollment(pyotp.TOTP(secret).now()) integration_client.auth.logout() # Admin removes TOTP integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.remove_user_mfa(victim["id"], "totp") assert_success(result) # Verify victim's TOTP is disabled integration_client.auth.logout() integration_client.auth.login(email=victim["email"], password="VictimPass123!") status = integration_client.mfa.get_totp_status() assert status["data"].get("totp_enabled") is False def test_admin_hard_delete_user_positive(self, integration_app, integration_client, create_test_user, create_test_org, create_test_membership): """TEST: ADMIN-11 — Admin hard-deletes user. WHAT: Admin hard-deletes a user, verify they cannot login. WHY: GDPR compliance and removing malicious actors. EXPECTED: 200 OK. Login fails (user no longer exists). """ admin = create_test_user(password="AdminPass123!") victim = create_test_user(password="VictimPass123!") org = create_test_org() create_test_membership(admin["id"], org["id"], OrganizationRole.OWNER) create_test_membership(victim["id"], org["id"], OrganizationRole.MEMBER) integration_client.auth.login(email=admin["email"], password="AdminPass123!") result = integration_client.admin.hard_delete_user(victim["id"], confirm=True) assert_success(result) # Verify victim cannot login integration_client.auth.logout() with pytest.raises(ApiError) as exc_info: integration_client.auth.login(email=victim["email"], password="VictimPass123!") assert exc_info.value.status_code in (400, 401)