import copy import uuid import pytest from flask import url_for import app from app.utils.user import is_gov_user from tests.conftest import ( ORGANISATION_ID, ORGANISATION_TWO_ID, SERVICE_ONE_ID, USER_ONE_ID, create_active_user_empty_permissions, create_active_user_manage_template_permissions, create_active_user_view_permissions, create_active_user_with_permissions, create_platform_admin_user, normalize_spaces, sample_uuid, ) @pytest.mark.parametrize( ("user", "expected_self_text", "add_details"), [ ( create_active_user_with_permissions(), ( "Test User(you) " "Permissions " "Can See dashboard " "Can Send messages " "Can Add and edit templates " "Can Manage settings, team and usage " "Can Manage API integration" ), True, ), ( create_active_user_empty_permissions(), ("Test User With Empty Permissions(you) " "Permissions"), False, ), ( create_active_user_view_permissions(), ("Test User With Permissions(you) " "Permissions " "Can See dashboard"), False, ), ( create_active_user_manage_template_permissions(), ( "Test User With Permissions(you) " "Permissions " "Can See dashboard " "Can Add and edit templates" ), False, ), ], ) def test_should_show_overview_page( client_request, mocker, mock_get_invites_for_service, mock_get_template_folders, mock_has_no_jobs, service_one, user, expected_self_text, active_user_view_permissions, add_details, ): current_user = user other_user = copy.deepcopy(active_user_view_permissions) other_user["email_address"] = "zzzzzzz@example.gsa.gov" other_user["name"] = "ZZZZZZZZ" other_user["id"] = "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz" client_request.login(current_user) mock_get_users = mocker.patch( "app.models.user.Users.client_method", return_value=[ current_user, other_user, ], ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) assert normalize_spaces(page.select_one("h1").text) == "Team members" assert ( normalize_spaces(page.select(".user-list-item")[0].text) == expected_self_text ) expected = "ZZZZZZZZ zzzzzzz@example.gsa.gov " "Permissions " "Can See dashboard" if add_details is True: expected = f"{expected} Change details for ZZZZZZZZ zzzzzzz@example.gsa.gov" assert normalize_spaces(page.select(".user-list-item")[6].text) == expected mock_get_users.assert_called_once_with(SERVICE_ONE_ID) @pytest.mark.parametrize( "state", [ "active", "pending", ], ) def test_should_show_change_details_link( client_request, mocker, mock_get_invites_for_service, mock_get_template_folders, service_one, active_user_with_permissions, active_caseworking_user, state, ): current_user = active_user_with_permissions other_user = active_caseworking_user other_user["id"] = uuid.uuid4() other_user["email_address"] = "zzzzzzz@example.gsa.gov" other_user["state"] = state mocker.patch("app.user_api_client.get_user", return_value=current_user) mocker.patch( "app.models.user.Users.client_method", return_value=[ current_user, other_user, ], ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) link = page.select(".user-list-item")[-1].select_one("a") assert normalize_spaces(link.text) == ( "Change details for Test User zzzzzzz@example.gsa.gov" ) assert link["href"] == url_for( ".edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=other_user["id"], ) @pytest.mark.parametrize( "number_of_users", [ pytest.param(7), pytest.param(8), ], ) def test_should_show_live_search_if_more_than_7_users( client_request, mocker, mock_get_invites_for_service, mock_get_template_folders, mock_has_no_jobs, active_user_with_permissions, active_user_view_permissions, number_of_users, ): mocker.patch( "app.user_api_client.get_user", return_value=active_user_with_permissions ) mocker.patch("app.models.user.InvitedUsers.client_method", return_value=[]) mocker.patch( "app.models.user.Users.client_method", return_value=[active_user_with_permissions] * number_of_users, ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) if number_of_users == 7: with pytest.raises(expected_exception=TypeError): assert page.select_one("div[data-module=live-search]")["data-targets"] == ( ".user-list-item" ) return assert page.select_one("div[data-module=live-search]")["data-targets"] == ( ".user-list-item" ) assert len(page.select(".user-list-item")) == number_of_users textbox = page.select_one("[data-module=autofocus] .usa-input") assert "value" not in textbox assert textbox["name"] == "search" # data-module=autofocus is set on a containing element so it # shouldn’t also be set on the textbox itself assert "data-module" not in textbox assert not page.select_one("[data-force-focus]") assert textbox["class"] == [ "usa-input", ] assert ( normalize_spaces(page.select_one("label[for=search]").text) == "Search by name or email address" ) def test_should_show_caseworker_on_overview_page( client_request, mocker, mock_get_invites_for_service, mock_get_template_folders, service_one, active_user_view_permissions, active_caseworking_user, ): service_one["permissions"].append("caseworking") current_user = active_user_view_permissions other_user = active_caseworking_user other_user["id"] = uuid.uuid4() other_user["email_address"] = "zzzzzzz@example.gsa.gov" client_request.login(current_user) mocker.patch( "app.models.user.Users.client_method", return_value=[ current_user, other_user, ], ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) assert normalize_spaces(page.select_one("h1").text) == "Team members" assert normalize_spaces(page.select(".user-list-item")[0].text) == ( "Test User With Permissions(you) " "Permissions " "Can See dashboard" ) # [1:5] are invited users assert normalize_spaces(page.select(".user-list-item")[6].text) == ( "Test User zzzzzzz@example.gsa.gov " "Permissions " "Can Send messages" ) @pytest.mark.parametrize( ("endpoint", "extra_args", "service_has_email_auth", "auth_options_hidden"), [ ("main.edit_user_permissions", {"user_id": sample_uuid()}, True, False), ("main.edit_user_permissions", {"user_id": sample_uuid()}, False, True), ("main.invite_user", {}, True, False), ("main.invite_user", {}, False, True), ], ) def test_service_with_no_email_auth_hides_auth_type_options( client_request, endpoint, extra_args, service_has_email_auth, auth_options_hidden, service_one, mock_get_users_by_service, mock_get_template_folders, platform_admin_user, ): if service_has_email_auth: service_one["permissions"].append("email_auth") client_request.login(platform_admin_user) page = client_request.get(endpoint, service_id=service_one["id"], **extra_args) assert ( page.find("input", attrs={"name": "login_authentication"}) is None ) == auth_options_hidden @pytest.mark.parametrize("service_has_caseworking", [True, False]) @pytest.mark.parametrize( ("endpoint", "extra_args"), [ ( "main.edit_user_permissions", {"user_id": sample_uuid()}, ), ( "main.invite_user", {}, ), ], ) def test_service_without_caseworking_doesnt_show_admin_vs_caseworker( client_request, mock_get_users_by_service, mock_get_template_folders, endpoint, service_has_caseworking, extra_args, platform_admin_user, ): client_request.login(platform_admin_user) page = client_request.get(endpoint, service_id=SERVICE_ONE_ID, **extra_args) permission_checkboxes = page.select("input[type=checkbox]") for idx in range(len(permission_checkboxes)): assert permission_checkboxes[idx]["name"] == "permissions_field" assert permission_checkboxes[0]["value"] == "view_activity" assert permission_checkboxes[1]["value"] == "send_messages" assert permission_checkboxes[2]["value"] == "manage_templates" assert permission_checkboxes[3]["value"] == "manage_service" assert permission_checkboxes[4]["value"] == "manage_api_keys" @pytest.mark.parametrize( ("service_has_email_auth", "displays_auth_type"), [(True, True), (False, False)] ) def test_manage_users_page_shows_member_auth_type_if_service_has_email_auth_activated( client_request, service_has_email_auth, service_one, mock_get_users_by_service, mock_get_invites_for_service, mock_get_template_folders, displays_auth_type, ): if service_has_email_auth: service_one["permissions"].append("email_auth") page = client_request.get("main.manage_users", service_id=service_one["id"]) assert bool(page.select_one(".tick-cross-list-hint")) == displays_auth_type @pytest.mark.parametrize( ("sms_option_disabled", "mobile_number", "expected_label"), [ ( True, None, """ Text message code Not available because this team member has not added a phone number to their profile """, ), ( False, "202-867-5303", """ Text message code """, ), ], ) def test_user_with_no_mobile_number_cant_be_set_to_sms_auth( client_request, mock_get_users_by_service, mock_get_template_folders, sms_option_disabled, mobile_number, expected_label, service_one, mocker, active_user_with_permissions, ): active_user_with_permissions["mobile_number"] = mobile_number service_one["permissions"].append("email_auth") mocker.patch( "app.user_api_client.get_user", return_value=active_user_with_permissions ) page = client_request.get( "main.edit_user_permissions", service_id=service_one["id"], user_id=sample_uuid(), ) sms_auth_radio_button = page.select_one('input[value="sms_auth"]') assert sms_auth_radio_button.has_attr("disabled") == sms_option_disabled assert normalize_spaces( page.select_one("label[for=login_authentication-0]").text ) == normalize_spaces(expected_label) @pytest.mark.parametrize( ("endpoint", "extra_args", "expected_checkboxes"), [ ( "main.edit_user_permissions", {"user_id": sample_uuid()}, [ ("view_activity", True), ("send_messages", True), ("manage_templates", True), ("manage_service", True), ("manage_api_keys", True), ], ), ( "main.invite_user", {}, [ ("view_activity", False), ("send_messages", False), ("manage_templates", False), ("manage_service", False), ("manage_api_keys", False), ], ), ], ) def test_should_show_page_for_one_user( client_request, mock_get_users_by_service, mock_get_template_folders, endpoint, extra_args, expected_checkboxes, platform_admin_user, ): client_request.login(platform_admin_user) page = client_request.get(endpoint, service_id=SERVICE_ONE_ID, **extra_args) checkboxes = page.select("input[type=checkbox]") assert len(checkboxes) == 5 for index, expected in enumerate(expected_checkboxes): expected_input_value, expected_checked = expected assert checkboxes[index]["name"] == "permissions_field" assert checkboxes[index]["value"] == expected_input_value assert checkboxes[index].has_attr("checked") == expected_checked def test_invite_user_allows_to_choose_auth( client_request, mock_get_users_by_service, mock_get_template_folders, service_one, platform_admin_user, ): service_one["permissions"].append("email_auth") client_request.login(platform_admin_user) page = client_request.get("main.invite_user", service_id=SERVICE_ONE_ID) radio_buttons = page.select("input[name=login_authentication]") values = {button["value"] for button in radio_buttons} assert values == {"sms_auth", "email_auth"} assert not any(button.has_attr("disabled") for button in radio_buttons) def test_invite_user_has_correct_email_field( client_request, mock_get_users_by_service, mock_get_template_folders, platform_admin_user, ): client_request.login(platform_admin_user) email_field = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID ).select_one("#email_address") assert email_field["spellcheck"] == "false" assert "autocomplete" not in email_field def test_should_not_show_page_for_non_team_member( client_request, mock_get_users_by_service, ): client_request.get( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _expected_status=404, ) @pytest.mark.parametrize( ("submitted_permissions", "permissions_sent_to_api"), [ ( { "permissions_field": [ "view_activity", "send_messages", "manage_templates", "manage_service", "manage_api_keys", ] }, { "view_activity", "send_messages", "manage_service", "manage_templates", "manage_api_keys", }, ), ( { "permissions_field": [ "view_activity", "send_messages", "manage_templates", ] }, { "view_activity", "send_messages", "manage_templates", }, ), ( {}, set(), ), ], ) def test_edit_user_permissions( client_request, mocker, mock_get_users_by_service, mock_get_invites_for_service, mock_set_user_permissions, mock_get_template_folders, fake_uuid, submitted_permissions, permissions_sent_to_api, ): client_request.post( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=fake_uuid, _data=dict(email_address="test@example.com", **submitted_permissions), _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_set_user_permissions.assert_called_with( fake_uuid, SERVICE_ONE_ID, permissions=permissions_sent_to_api, folder_permissions=[], ) def test_edit_user_folder_permissions( client_request, mocker, service_one, mock_get_users_by_service, mock_get_invites_for_service, mock_set_user_permissions, mock_get_template_folders, fake_uuid, ): mock_get_template_folders.return_value = [ { "id": "folder-id-1", "name": "folder_one", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-2", "name": "folder_one", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-3", "name": "folder_one", "parent_id": "folder-id-1", "users_with_permission": [], }, ] page = client_request.get( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=fake_uuid, ) assert [ item["value"] for item in page.select("input[name=folder_permissions]") ] == ["folder-id-1", "folder-id-3", "folder-id-2"] client_request.post( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=fake_uuid, _data=dict(folder_permissions=["folder-id-1", "folder-id-3"]), _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_set_user_permissions.assert_called_with( fake_uuid, SERVICE_ONE_ID, permissions=set(), folder_permissions=["folder-id-1", "folder-id-3"], ) def test_cant_edit_user_folder_permissions_for_platform_admin_users( client_request, mocker, service_one, mock_get_users_by_service, mock_get_invites_for_service, mock_set_user_permissions, mock_get_template_folders, platform_admin_user, ): service_one["permissions"] = ["edit_folder_permissions"] mocker.patch("app.user_api_client.get_user", return_value=platform_admin_user) mock_get_template_folders.return_value = [ { "id": "folder-id-1", "name": "folder_one", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-2", "name": "folder_one", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-3", "name": "folder_one", "parent_id": "folder-id-1", "users_with_permission": [], }, ] page = client_request.get( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=platform_admin_user["id"], ) assert ( normalize_spaces(page.select("main .usa-body")[0].text) == "platform@admin.gsa.gov Change email address" ) assert normalize_spaces(page.select("main .usa-body")[1].text) == ( "Platform admin users can access all template folders." ) assert page.select("input[name=folder_permissions]") == [] client_request.post( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=platform_admin_user["id"], _data={}, _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_set_user_permissions.assert_called_with( platform_admin_user["id"], SERVICE_ONE_ID, permissions={ "manage_api_keys", "manage_service", "manage_templates", "send_messages", "view_activity", }, folder_permissions=None, ) def test_cant_edit_non_member_user_permissions( client_request, mocker, mock_get_users_by_service, mock_set_user_permissions, ): client_request.post( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _data={ "email_address": "test@example.com", "manage_service": "y", }, _expected_status=404, ) assert mock_set_user_permissions.called is False def test_edit_user_permissions_including_authentication_with_email_auth_service( client_request, service_one, active_user_with_permissions, mock_get_users_by_service, mock_get_invites_for_service, mock_set_user_permissions, mock_update_user_attribute, mock_get_template_folders, ): active_user_with_permissions["auth_type"] = "email_auth" service_one["permissions"].append("email_auth") client_request.post( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={ "email_address": active_user_with_permissions["email_address"], "permissions_field": [ "send_messages", "manage_templates", "manage_service", "manage_api_keys", ], "login_authentication": "sms_auth", }, _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_set_user_permissions.assert_called_with( str(active_user_with_permissions["id"]), SERVICE_ONE_ID, permissions={ "send_messages", "manage_templates", "manage_service", "manage_api_keys", }, folder_permissions=[], ) mock_update_user_attribute.assert_called_with( str(active_user_with_permissions["id"]), auth_type="sms_auth" ) def test_edit_user_permissions_shows_authentication_for_email_auth_service( client_request, service_one, mock_get_users_by_service, mock_get_template_folders, active_user_with_permissions, ): service_one["permissions"].append("email_auth") page = client_request.get( "main.edit_user_permissions", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ) radio_buttons = page.select("input[name=login_authentication]") values = {button["value"] for button in radio_buttons} assert values == {"sms_auth", "email_auth"} assert not any(button.has_attr("disabled") for button in radio_buttons) def test_should_show_page_for_inviting_user( client_request, mock_get_template_folders, active_user_with_permissions, ): client_request.login(active_user_with_permissions) page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, ) assert "Invite a team member" in page.find("h1").text.strip() assert not page.find("div", class_="checkboxes-nested") def test_should_not_show_page_for_inviting_user_without_permissions( client_request, mock_get_template_folders, active_user_empty_permissions ): client_request.login(active_user_empty_permissions) page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, _expected_status=403 ) assert "not allowed to see this page" in page.h1.string.strip() def test_should_show_page_for_inviting_user_with_email_prefilled( client_request, mocker, service_one, mock_get_template_folders, fake_uuid, active_user_with_permissions, active_user_with_permission_to_other_service, mock_get_organization_by_domain, mock_get_invites_for_service, ): client_request.login(active_user_with_permissions) service_one["organization"] = ORGANISATION_ID mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_user_with_permission_to_other_service, ], ) page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, # We have the user’s name in the H1 but don’t want it duplicated # in the page title _test_page_title=False, ) assert normalize_spaces(page.select_one("title").text).startswith( "Invite a team member" ) assert normalize_spaces(page.select_one("h1").text) == ("Invite Service Two User") assert not page.select("input#email_address") or page.select("input[type=email]") def test_should_show_page_if_prefilled_user_is_already_a_team_member( mocker, client_request, mock_get_template_folders, fake_uuid, active_user_with_permissions, active_caseworking_user, ): client_request.login(active_user_with_permissions) mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_caseworking_user, ], ) page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, ) assert normalize_spaces(page.select_one("title").text).startswith( "This person is already a team member" ) assert normalize_spaces(page.select_one("h1").text) == ( "This person is already a team member" ) assert normalize_spaces(page.select_one("main p").text) == ( "Test User is already member of ‘service one’." ) assert not page.select("form") def test_should_show_page_if_prefilled_user_is_already_invited( mocker, client_request, mock_get_template_folders, fake_uuid, active_user_with_permission_to_other_service, mock_get_invites_for_service, platform_admin_user, ): active_user_with_permission_to_other_service["email_address"] = ( "user_1@testnotify.gsa.gov" ) client_request.login(platform_admin_user) mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_user_with_permission_to_other_service, ], ) page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, ) assert normalize_spaces(page.select_one("title").text).startswith( "This person has already received an invite" ) assert normalize_spaces(page.select_one("h1").text) == ( "This person has already received an invite" ) assert normalize_spaces(page.select_one("main .usa-body").text) == ( "Service Two User has not accepted their invitation to " "‘service one’ yet. You do not need to do anything." ) assert not page.select("form") def test_should_403_if_trying_to_prefill_email_address_for_user_with_no_organization( mocker, client_request, service_one, mock_get_template_folders, fake_uuid, active_user_with_permissions, active_user_with_permission_to_other_service, mock_get_invites_for_service, mock_get_no_organization_by_domain, ): service_one["organization"] = ORGANISATION_ID client_request.login(active_user_with_permissions) mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_user_with_permission_to_other_service, ], ) client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, _expected_status=403, ) def test_should_403_if_trying_to_prefill_email_address_for_user_from_other_organization( mocker, client_request, service_one, mock_get_template_folders, fake_uuid, active_user_with_permissions, active_user_with_permission_to_other_service, mock_get_invites_for_service, mock_get_organization_by_domain, ): service_one["organization"] = ORGANISATION_TWO_ID client_request.login(active_user_with_permissions) mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_user_with_permission_to_other_service, ], ) client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, _expected_status=403, ) def test_should_show_folder_permission_form_if_service_has_folder_permissions_enabled( client_request, mocker, mock_get_template_folders, service_one, platform_admin_user ): client_request.login(platform_admin_user) mock_get_template_folders.return_value = [ { "id": "folder-id-1", "name": "folder_one", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-2", "name": "folder_two", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-3", "name": "folder_three", "parent_id": "folder-id-1", "users_with_permission": [], }, ] page = client_request.get( "main.invite_user", service_id=SERVICE_ONE_ID, ) assert "Invite a team member" in page.find("h1").text.strip() folder_checkboxes = page.find("div", class_="selection-wrapper").find_all("li") assert len(folder_checkboxes) == 3 @pytest.mark.parametrize( ("email_address", "gov_user"), [("test@example.gsa.gov", True), ("test@example.com", False)], ) def test_invite_user( client_request, platform_admin_user, mocker, sample_invite, email_address, gov_user, mock_get_template_folders, mock_get_organizations, ): sample_invite["email_address"] = email_address assert is_gov_user(email_address) == gov_user mocker.patch( "app.models.user.InvitedUsers.client_method", return_value=[sample_invite] ) mocker.patch( "app.models.user.Users.client_method", return_value=[platform_admin_user], ) mocker.patch("app.invite_api_client.create_invite", return_value=sample_invite) client_request.login(platform_admin_user) page = client_request.post( "main.invite_user", service_id=SERVICE_ONE_ID, _data={ "email_address": email_address, "permissions_field": [ "view_activity", "send_messages", "manage_templates", "manage_service", "manage_api_keys", ], }, _follow_redirects=True, ) assert page.h1.string.strip() == "Team members" flash_banner = page.find("div", class_="banner-default-with-tick").string.strip() assert flash_banner == f"Invite sent to {email_address}" expected_permissions = { "manage_api_keys", "manage_service", "manage_templates", "send_messages", "view_activity", } app.invite_api_client.create_invite.assert_called_once_with( sample_invite["from_user"], sample_invite["service"], email_address, expected_permissions, "sms_auth", [], ) def test_invite_user_when_email_address_is_prefilled( client_request, service_one, platform_admin_user, active_user_with_permission_to_other_service, fake_uuid, mocker, sample_invite, mock_get_template_folders, mock_get_invites_for_service, mock_get_organization_by_domain, ): service_one["organization"] = ORGANISATION_ID client_request.login(platform_admin_user) mocker.patch( "app.models.user.user_api_client.get_user", side_effect=[ active_user_with_permission_to_other_service, ], ) mocker.patch("app.invite_api_client.create_invite", return_value=sample_invite) client_request.post( "main.invite_user", service_id=SERVICE_ONE_ID, user_id=fake_uuid, _data={ # No posted email address "permissions_field": [ "send_messages", ], }, ) app.invite_api_client.create_invite.assert_called_once_with( platform_admin_user["id"], SERVICE_ONE_ID, active_user_with_permission_to_other_service["email_address"], {"send_messages"}, "sms_auth", [], ) @pytest.mark.parametrize("auth_type", [("sms_auth"), ("email_auth")]) @pytest.mark.parametrize( ("email_address", "gov_user"), [("test@example.gsa.gov", True), ("test@example.com", False)], ) def test_invite_user_with_email_auth_service( client_request, service_one, platform_admin_user, sample_invite, email_address, gov_user, mocker, auth_type, mock_get_organizations, mock_get_template_folders, ): service_one["permissions"].append("email_auth") sample_invite["email_address"] = "test@example.gsa.gov" assert is_gov_user(email_address) is gov_user mocker.patch( "app.models.user.InvitedUsers.client_method", return_value=[sample_invite] ) mocker.patch( "app.models.user.Users.client_method", return_value=[platform_admin_user], ) mocker.patch("app.invite_api_client.create_invite", return_value=sample_invite) client_request.login(platform_admin_user) page = client_request.post( "main.invite_user", service_id=SERVICE_ONE_ID, _data={ "email_address": email_address, "permissions_field": [ "view_activity", "send_messages", "manage_templates", "manage_service", "manage_api_keys", ], "login_authentication": auth_type, }, _follow_redirects=True, _expected_status=200, ) assert page.h1.string.strip() == "Team members" flash_banner = page.find("div", class_="banner-default-with-tick").string.strip() assert flash_banner == "Invite sent to test@example.gsa.gov" expected_permissions = { "manage_api_keys", "manage_service", "manage_templates", "send_messages", "view_activity", } app.invite_api_client.create_invite.assert_called_once_with( sample_invite["from_user"], sample_invite["service"], email_address, expected_permissions, auth_type, [], ) def test_resend_expired_invitation( client_request, mock_get_invites_for_service, expired_invite, active_user_with_permissions, mock_get_users_by_service, mock_get_template_folders, mocker, ): mock_resend = mocker.patch("app.invite_api_client.resend_invite") mocker.patch( "app.invite_api_client.get_invited_user_for_service", return_value=expired_invite, ) page = client_request.get( "main.resend_invite", service_id=SERVICE_ONE_ID, invited_user_id=expired_invite["id"], _follow_redirects=True, ) assert normalize_spaces(page.h1.text) == "Team members" assert mock_resend.called called_args = set(mock_resend.call_args.args) | set( mock_resend.call_args.kwargs.values() ) assert SERVICE_ONE_ID in called_args assert expired_invite["id"] in called_args def test_cancel_invited_user_cancels_user_invitations( client_request, mock_get_invites_for_service, sample_invite, active_user_with_permissions, mock_get_users_by_service, mock_get_template_folders, mocker, ): mock_cancel = mocker.patch("app.invite_api_client.cancel_invited_user") mocker.patch( "app.invite_api_client.get_invited_user_for_service", return_value=sample_invite, ) page = client_request.get( "main.cancel_invited_user", service_id=SERVICE_ONE_ID, invited_user_id=sample_invite["id"], _follow_redirects=True, ) assert normalize_spaces(page.h1.text) == "Team members" flash_banner = normalize_spaces( page.find("div", class_="banner-default-with-tick").text ) assert flash_banner == f"Invitation cancelled for {sample_invite['email_address']}" mock_cancel.assert_called_once_with( service_id=SERVICE_ONE_ID, invited_user_id=sample_invite["id"], ) def test_cancel_invited_user_doesnt_work_if_user_not_invited_to_this_service( client_request, mock_get_invites_for_service, mocker, ): mock_cancel = mocker.patch("app.invite_api_client.cancel_invited_user") client_request.get( "main.cancel_invited_user", service_id=SERVICE_ONE_ID, invited_user_id=sample_uuid(), _expected_status=404, ) assert mock_cancel.called is False @pytest.mark.parametrize( ("invite_status", "expected_text"), [ ( "pending", ( "invited_user@test.gsa.gov(invited) " "Permissions " "Can See dashboard " "Can Send messages " "Can Manage settings, team and usage " "Can Manage API integration " "Cancel invitation for invited_user@test.gsa.gov" ), ), # Test case removed due to the removal of canceled users from the dashboard # ( # "cancelled", # ( # "invited_user@test.gsa.gov(cancelled invite) " # "Permissions" # # all permissions are greyed out # ), # ), ], ) def test_manage_users_shows_invited_user( client_request, mocker, active_user_with_permissions, mock_get_template_folders, sample_invite, invite_status, expected_text, ): sample_invite["status"] = invite_status mocker.patch( "app.models.user.InvitedUsers.client_method", return_value=[sample_invite] ) mocker.patch( "app.models.user.Users.client_method", return_value=[active_user_with_permissions], ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) assert page.h1.string.strip() == "Team members" assert normalize_spaces(page.select(".user-list-item")[0].text) == expected_text def test_manage_users_does_not_show_accepted_invite( client_request, mocker, active_user_with_permissions, sample_invite, mock_get_template_folders, ): invited_user_id = uuid.uuid4() sample_invite["id"] = invited_user_id sample_invite["status"] = "accepted" mocker.patch( "app.models.user.InvitedUsers.client_method", return_value=[sample_invite] ) mocker.patch( "app.models.user.Users.client_method", return_value=[active_user_with_permissions], ) page = client_request.get("main.manage_users", service_id=SERVICE_ONE_ID) assert page.h1.string.strip() == "Team members" user_lists = page.find_all("div", {"class": "user-list"}) assert len(user_lists) == 1 assert not page.find(text="invited_user@test.gsa.gov") def test_user_cant_invite_themselves( client_request, mocker, active_user_with_permissions, mock_create_invite, mock_get_template_folders, ): page = client_request.post( "main.invite_user", service_id=SERVICE_ONE_ID, _data={ "email_address": active_user_with_permissions["email_address"], "permissions_field": ["send_messages", "manage_service", "manage_api_keys"], }, _follow_redirects=True, _expected_status=200, ) assert page.h1.string.strip() == "Invite a team member" form_error = page.find("span", class_="usa-error-message").text.strip() assert form_error == "Error: You cannot send an invitation to yourself" assert not mock_create_invite.called def test_user_cant_invite_themselves_platform_admin( client_request, mocker, mock_create_invite, mock_get_template_folders, ): platform_admin = create_platform_admin_user() client_request.login(platform_admin) page = client_request.post( "main.invite_user", service_id=SERVICE_ONE_ID, _follow_redirects=True, _expected_status=200, ) assert "Invite a team member" in page.h1.string.strip() def test_no_permission_manage_users_page( client_request, service_one, mock_get_users_by_service, mock_get_invites_for_service, mock_get_template_folders, api_user_active, mocker, ): resp_text = client_request.get("main.manage_users", service_id=service_one["id"]) assert url_for(".invite_user", service_id=service_one["id"]) not in resp_text assert "Edit permission" not in resp_text assert "Team members" not in resp_text @pytest.mark.parametrize( ("folders_user_can_see", "expected_message"), [ (3, "Can see all folders"), (2, "Can see 2 folders"), (1, "Can see 1 folder"), (0, "Cannot see any folders"), ], ) def test_manage_user_page_shows_how_many_folders_user_can_view( client_request, service_one, mock_get_template_folders, mock_get_users_by_service, mock_get_invites_for_service, api_user_active, folders_user_can_see, expected_message, ): service_one["permissions"] = ["edit_folder_permissions"] mock_get_template_folders.return_value = [ { "id": "folder-id-1", "name": "f1", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-2", "name": "f2", "parent_id": None, "users_with_permission": [], }, { "id": "folder-id-3", "name": "f3", "parent_id": None, "users_with_permission": [], }, ] for i in range(folders_user_can_see): mock_get_template_folders.return_value[i]["users_with_permission"].append( api_user_active["id"] ) page = client_request.get("main.manage_users", service_id=service_one["id"]) user_div = page.select_one( "h2[title='notify@digital.cabinet-office.gov.uk']" ).parent assert user_div.select_one(".tick-cross-list-hint").text.strip() == expected_message def test_manage_user_page_doesnt_show_folder_hint_if_service_has_no_folders( client_request, service_one, mock_get_template_folders, mock_get_users_by_service, mock_get_invites_for_service, api_user_active, ): service_one["permissions"] = ["edit_folder_permissions"] mock_get_template_folders.return_value = [] page = client_request.get("main.manage_users", service_id=service_one["id"]) user_div = page.select_one( "h2[title='notify@digital.cabinet-office.gov.uk']" ).parent assert user_div.find(".tick-cross-list-hint:last-child") is None def test_manage_user_page_doesnt_show_folder_hint_if_service_cant_edit_folder_permissions( client_request, service_one, mock_get_template_folders, mock_get_users_by_service, mock_get_invites_for_service, api_user_active, ): service_one["permissions"] = [] mock_get_template_folders.return_value = [ { "id": "folder-id-1", "name": "f1", "parent_id": None, "users_with_permission": [api_user_active["id"]], }, ] page = client_request.get("main.manage_users", service_id=service_one["id"]) user_div = page.select_one( "h2[title='notify@digital.cabinet-office.gov.uk']" ).parent assert user_div.find(".tick-cross-list-hint:last-child") is None def test_remove_user_from_service( client_request, active_user_with_permissions, api_user_active, service_one, mock_remove_user_from_service, mocker, ): mock_event_handler = mocker.patch( "app.main.views.manage_users.create_remove_user_from_service_event" ) client_request.post( "main.remove_user_from_service", service_id=service_one["id"], user_id=active_user_with_permissions["id"], _expected_redirect=url_for("main.manage_users", service_id=service_one["id"]), ) mock_remove_user_from_service.assert_called_once_with( service_one["id"], str(active_user_with_permissions["id"]) ) mock_event_handler.assert_called_once_with( user_id=active_user_with_permissions["id"], removed_by_id=api_user_active["id"], service_id=service_one["id"], ) def test_can_invite_user_as_platform_admin( client_request, service_one, platform_admin_user, active_user_with_permissions, mock_get_invites_for_service, mock_get_template_folders, mocker, ): mocker.patch( "app.models.user.Users.client_method", return_value=[active_user_with_permissions], ) page = client_request.get( "main.manage_users", service_id=SERVICE_ONE_ID, ) assert url_for(".invite_user", service_id=service_one["id"]) in str(page) def test_edit_user_email_page( client_request, active_user_with_permissions, service_one, mock_get_users_by_service, mocker, ): user = active_user_with_permissions mocker.patch("app.user_api_client.get_user", return_value=user) page = client_request.get( "main.edit_user_email", service_id=service_one["id"], user_id=sample_uuid() ) assert page.find("h1").text == "Change team member’s email address" assert page.select("p[id=user_name]")[ 0 ].text == "This will change the email address for {}.".format(user["name"]) assert page.select("input[type=email]")[0].attrs["value"] == user["email_address"] assert normalize_spaces(page.select("main button[type=submit]")[0].text) == "Save" def test_edit_user_email_page_404_for_non_team_member( client_request, mock_get_users_by_service, ): client_request.get( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _expected_status=404, ) def test_edit_user_email_redirects_to_confirmation( client_request, active_user_with_permissions, mock_get_users_by_service, mock_get_user_by_email_not_found, ): client_request.post( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _expected_status=302, _expected_redirect=url_for( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ), ) with client_request.session_transaction() as session: assert ( session[ "team_member_email_change-{}".format(active_user_with_permissions["id"]) ] == "test@user.gsa.gov" ) def test_edit_user_email_without_changing_goes_back_to_team_members( client_request, active_user_with_permissions, mock_get_user_by_email, mock_get_users_by_service, mock_update_user_attribute, ): client_request.post( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"email_address": active_user_with_permissions["email_address"]}, _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) assert mock_update_user_attribute.called is False @pytest.mark.parametrize("original_email_address", ["test@gsa.gov", "test@example.com"]) def test_edit_user_email_can_change_any_email_address_to_a_gov_email_address( client_request, active_user_with_permissions, mock_get_user_by_email_not_found, mock_get_users_by_service, mock_update_user_attribute, mock_get_organizations, original_email_address, ): active_user_with_permissions["email_address"] = original_email_address client_request.post( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"email_address": "new-email-address@gsa.gov"}, _expected_status=302, _expected_redirect=url_for( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ), ) def test_edit_user_email_can_change_a_non_gov_email_address_to_another_non_gov_email_address( client_request, active_user_with_permissions, mock_get_user_by_email_not_found, mock_get_users_by_service, mock_update_user_attribute, mock_get_organizations, ): active_user_with_permissions["email_address"] = "old@example.com" client_request.post( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"email_address": "new@example.com"}, _expected_status=302, _expected_redirect=url_for( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ), ) def test_edit_user_email_cannot_change_a_gov_email_address_to_a_non_gov_email_address( client_request, active_user_with_permissions, mock_get_user_by_email_not_found, mock_get_users_by_service, mock_update_user_attribute, mock_get_organizations, ): page = client_request.post( "main.edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"email_address": "new_email@example.com"}, _expected_status=200, ) assert ( "Enter a public sector email address" in page.select_one(".usa-error-message").text ) with client_request.session_transaction() as session: assert ( "team_member_email_change-{}".format(active_user_with_permissions["id"]) not in session ) def test_confirm_edit_user_email_page( client_request, active_user_with_permissions, mock_get_users_by_service, mock_get_user, ): new_email = "new_email@gsa.gov" with client_request.session_transaction() as session: session[ "team_member_email_change-{}".format(active_user_with_permissions["id"]) ] = new_email page = client_request.get( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ) assert "Confirm change of email address" in page.text for text in [ "New email address:", new_email, "We will send {} an email to tell them about the change.".format( active_user_with_permissions["name"] ), ]: assert text in page.text assert "Confirm" in page.text def test_confirm_edit_user_email_page_redirects_if_session_empty( client_request, mock_get_users_by_service, active_user_with_permissions, ): page = client_request.get( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _follow_redirects=True, ) assert "Confirm change of email address" not in page.text def test_confirm_edit_user_email_page_404s_for_non_team_member( client_request, mock_get_users_by_service, ): client_request.get( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _expected_status=404, ) def test_confirm_edit_user_email_changes_user_email( client_request, active_user_with_permissions, api_user_active, service_one, mocker, mock_update_user_attribute, ): # We want active_user_with_permissions (the current user) to update the email address for api_user_active # By default both users would have the same id, so we change the id of api_user_active api_user_active["id"] = str(uuid.uuid4()) mocker.patch( "app.models.user.Users.client_method", return_value=[api_user_active, active_user_with_permissions], ) # get_user gets called twice - first to check if current user can see the page, then to see if the team member # whose email address we're changing belongs to the service mocker.patch( "app.user_api_client.get_user", side_effect=[active_user_with_permissions, api_user_active], ) mock_event_handler = mocker.patch( "app.main.views.manage_users.create_email_change_event" ) new_email = "new_email@gsa.gov" with client_request.session_transaction() as session: session["team_member_email_change-{}".format(api_user_active["id"])] = new_email client_request.post( "main.confirm_edit_user_email", service_id=service_one["id"], user_id=api_user_active["id"], _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_update_user_attribute.assert_called_once_with( api_user_active["id"], email_address=new_email, updated_by=active_user_with_permissions["id"], ) mock_event_handler.assert_called_once_with( user_id=api_user_active["id"], updated_by_id=active_user_with_permissions["id"], original_email_address=api_user_active["email_address"], new_email_address=new_email, ) def test_confirm_edit_user_email_doesnt_change_user_email_for_non_team_member( client_request, mock_get_users_by_service, ): with client_request.session_transaction() as session: session["team_member_email_change"] = "new_email@gsa.gov" client_request.post( "main.confirm_edit_user_email", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _expected_status=404, ) def test_edit_user_permissions_page_displays_redacted_mobile_number_and_change_link( client_request, active_user_with_permissions, mock_get_users_by_service, mock_get_template_folders, service_one, mocker, ): page = client_request.get( "main.edit_user_permissions", service_id=service_one["id"], user_id=active_user_with_permissions["id"], ) assert active_user_with_permissions["name"] in page.find("h1").text mobile_number_paragraph = page.select("p[id=user_mobile_number]")[0] assert "202-8 • • • • 303" in mobile_number_paragraph.text change_link = mobile_number_paragraph.findChild() assert change_link.attrs[ "href" ] == "/services/{}/users/{}/edit-mobile-number".format( service_one["id"], active_user_with_permissions["id"] ) def test_edit_user_permissions_with_delete_query_shows_banner( client_request, active_user_with_permissions, mock_get_users_by_service, mock_get_template_folders, service_one, ): page = client_request.get( "main.edit_user_permissions", service_id=service_one["id"], user_id=active_user_with_permissions["id"], delete=1, ) banner = page.find("div", class_="banner-dangerous") assert banner.contents[0].strip() == "Are you sure you want to remove Test User?" assert banner.form.attrs["action"] == url_for( "main.remove_user_from_service", service_id=service_one["id"], user_id=active_user_with_permissions["id"], ) def test_edit_user_mobile_number_page( client_request, active_user_with_permissions, mock_get_users_by_service, service_one, mocker, ): page = client_request.get( "main.edit_user_mobile_number", service_id=service_one["id"], user_id=active_user_with_permissions["id"], ) assert page.find("h1").text == "Change team member’s mobile number" assert page.select("p[id=user_name]")[0].text == ( "This will change the mobile number for {}." ).format(active_user_with_permissions["name"]) assert page.select("input[name=mobile_number]")[0].attrs["value"] == "202-8••••303" assert normalize_spaces(page.select("main button[type=submit]")[0].text) == "Save" def test_edit_user_mobile_number_redirects_to_confirmation( client_request, active_user_with_permissions, mock_get_users_by_service, ): client_request.post( "main.edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"mobile_number": "2028675309"}, _expected_status=302, _expected_redirect=url_for( "main.confirm_edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ), ) def test_edit_user_mobile_number_redirects_to_manage_users_if_number_not_changed( client_request, active_user_with_permissions, mock_get_users_by_service, service_one, mocker, mock_get_user, ): client_request.post( "main.edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _data={"mobile_number": "202-8••••303"}, _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) def test_confirm_edit_user_mobile_number_page( client_request, active_user_with_permissions, mock_get_users_by_service, service_one, mocker, mock_get_user, ): new_number = "2028675309" with client_request.session_transaction() as session: session["team_member_mobile_change"] = new_number page = client_request.get( "main.confirm_edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], ) assert "Confirm change of mobile number" in page.text for text in [ "New mobile number:", new_number, "We will send {} a text message to tell them about the change.".format( active_user_with_permissions["name"] ), ]: assert text in page.text assert "Confirm" in page.text def test_confirm_edit_user_mobile_number_page_redirects_if_session_empty( client_request, active_user_with_permissions, mock_get_users_by_service, service_one, mocker, mock_get_user, ): page = client_request.get( "main.confirm_edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=active_user_with_permissions["id"], _expected_status=302, ) assert "Confirm change of mobile number" not in page.text def test_confirm_edit_user_mobile_number_changes_user_mobile_number( client_request, active_user_with_permissions, api_user_active, service_one, mocker, mock_update_user_attribute, ): # We want active_user_with_permissions (the current user) to update the mobile number for api_user_active # By default both users would have the same id, so we change the id of api_user_active api_user_active["id"] = str(uuid.uuid4()) mocker.patch( "app.models.user.Users.client_method", return_value=[api_user_active, active_user_with_permissions], ) # get_user gets called twice - first to check if current user can see the page, then to see if the team member # whose mobile number we're changing belongs to the service mocker.patch( "app.user_api_client.get_user", side_effect=[active_user_with_permissions, api_user_active], ) mock_event_handler = mocker.patch( "app.main.views.manage_users.create_mobile_number_change_event" ) new_number = "2028675309" with client_request.session_transaction() as session: session["team_member_mobile_change"] = new_number client_request.post( "main.confirm_edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=api_user_active["id"], _expected_status=302, _expected_redirect=url_for( "main.manage_users", service_id=SERVICE_ONE_ID, ), ) mock_update_user_attribute.assert_called_once_with( api_user_active["id"], mobile_number=new_number, updated_by=active_user_with_permissions["id"], ) mock_event_handler.assert_called_once_with( user_id=api_user_active["id"], updated_by_id=active_user_with_permissions["id"], original_mobile_number=api_user_active["mobile_number"], new_mobile_number=new_number, ) def test_confirm_edit_user_mobile_number_doesnt_change_user_mobile_for_non_team_member( client_request, mock_get_users_by_service, ): with client_request.session_transaction() as session: session["team_member_mobile_change"] = "2028675309" client_request.post( "main.confirm_edit_user_mobile_number", service_id=SERVICE_ONE_ID, user_id=USER_ONE_ID, _expected_status=404, )