import json import uuid import pytest from flask import url_for from notifications_utils.url_safe_token import generate_token from tests.conftest import ( create_api_user_active, create_user, normalize_spaces, url_for_endpoint_with_token, ) def test_should_show_overview_page( client_request, ): page = client_request.get("main.user_profile") assert page.select_one("h1").text.strip() == "User profile" assert "Use platform admin view" not in page assert "Security keys" not in page def test_overview_page_shows_disable_for_platform_admin( client_request, platform_admin_user ): client_request.login(platform_admin_user) page = client_request.get("main.user_profile") assert page.select_one("h1").text.strip() == "User profile" disable_platform_admin_row = page.select_one("#disable-platform-admin") assert ( " ".join(disable_platform_admin_row.text.split()) == "Use platform admin view Yes Change whether to use platform admin view" ) def test_should_show_name_page(client_request): page = client_request.get(("main.user_profile_name")) assert page.select_one("h1").text.strip() == "Change your name" def test_should_redirect_after_name_change( client_request, mock_update_user_attribute, ): client_request.post( "main.user_profile_name", _data={"new_name": "New Name"}, _expected_status=302, _expected_redirect=url_for("main.user_profile"), ) assert mock_update_user_attribute.called is True def test_should_show_email_page( client_request, ): page = client_request.get("main.user_profile_email") assert page.select_one("h1").text.strip() == "Change your email address" # template is shared with "Change your mobile number" but we don't want to show Delete mobile number link assert "Delete your number" not in page.text def test_should_redirect_after_email_change( client_request, mock_login, mock_email_is_not_already_in_use, ): client_request.post( "main.user_profile_email", _data={"email_address": "new_notify@notify.gsa.gov"}, _expected_status=302, _expected_redirect=url_for( "main.user_profile_email_authenticate", ), ) assert mock_email_is_not_already_in_use.called @pytest.mark.parametrize( ("email_address", "error_message"), [ ( "me@example.com", "Enter a public sector email address.", ), ( "not_valid", "Enter a valid email address", ), # 2 errors with email address, only first error shown ], ) def test_should_show_errors_if_new_email_address_does_not_validate( client_request, mock_email_is_not_already_in_use, mock_get_organizations, email_address, error_message, ): page = client_request.post( "main.user_profile_email", _data={"email_address": email_address}, _expected_status=200, ) assert ( normalize_spaces(page.find("span", class_="usa-error-message").text) == f"Error: {error_message}" ) # We only call API to check if the email address is already in use if there are no other errors assert not mock_email_is_not_already_in_use.called def test_should_show_authenticate_after_email_change( client_request, ): with client_request.session_transaction() as session: session["new-email"] = "new_notify@notify.gsa.gov" page = client_request.get("main.user_profile_email_authenticate") assert "Change your email address" in page.text assert "Confirm" in page.text def test_should_render_change_email_continue_after_authenticate_email( client_request, mock_verify_password, mock_send_change_email_verification, ): with client_request.session_transaction() as session: session["new-email"] = "new_notify@notify.gsa.gov" page = client_request.post( "main.user_profile_email_authenticate", _data={"password": "12345"}, _expected_status=200, ) assert ( "Click the link in the email to confirm the change to your email address." in page.text ) def test_should_redirect_to_user_profile_when_user_confirms_email_link( notify_admin, client_request, api_user_active, mock_update_user_attribute, ): token = generate_token( payload=json.dumps( {"user_id": api_user_active["id"], "email": "new_email@gsa.gov"} ), secret=notify_admin.config["SECRET_KEY"], salt=notify_admin.config["DANGEROUS_SALT"], ) client_request.get_url( url_for_endpoint_with_token( "main.user_profile_email_confirm", token=token, ), _expected_redirect=url_for("main.user_profile"), ) def test_should_show_mobile_number_page( client_request, ): page = client_request.get(("main.user_profile_mobile_number")) assert "Change your mobile number" in page.text assert "Delete your number" not in page.text def test_change_your_mobile_number_page_shows_delete_link_if_user_on_email_auth( client_request, api_user_active_email_auth, mocker ): client_request.login(api_user_active_email_auth) page = client_request.get(("main.user_profile_mobile_number")) assert "Change your mobile number" in page.text assert "Delete your number" in page.text def test_change_your_mobile_number_page_doesnt_show_delete_link_if_user_has_no_mobile_number( client_request, fake_uuid, mocker ): user = create_user(id=fake_uuid, auth_type="email_auth", mobile_number=None) client_request.login(user) page = client_request.get(("main.user_profile_mobile_number")) assert "Change your mobile number" in page.text assert "Delete your number" not in page.text def test_confirm_delete_mobile_number( client_request, api_user_active_email_auth, mocker ): client_request.login(api_user_active_email_auth) page = client_request.get( ".user_profile_confirm_delete_mobile_number", _test_page_title=False, ) assert normalize_spaces(page.select_one(".banner-dangerous").text) == ( "Are you sure you want to delete your mobile number from Notify? " "Yes, delete" ) assert "action" not in page.select_one(".banner-dangerous form") assert page.select_one(".banner-dangerous form")["method"] == "post" def test_delete_mobile_number(client_request, api_user_active_email_auth, mocker): client_request.login(api_user_active_email_auth) mock_delete = mocker.patch("app.user_api_client.update_user_attribute") client_request.post( ".user_profile_mobile_number_delete", _expected_redirect=url_for( ".user_profile", ), ) mock_delete.assert_called_once_with( api_user_active_email_auth["id"], mobile_number=None ) @pytest.mark.parametrize( "phone_number_to_register_with", [ "+12024900460", "+1800-555-5555", ], ) def test_should_redirect_after_mobile_number_change( client_request, phone_number_to_register_with, ): client_request.post( "main.user_profile_mobile_number", _data={"mobile_number": phone_number_to_register_with}, _expected_status=302, _expected_redirect=url_for( "main.user_profile_mobile_number_authenticate", ), ) with client_request.session_transaction() as session: assert session["new-mob"] == phone_number_to_register_with def test_should_redirect_after_mobile_number_authenticate( client_request, mock_verify_password, mock_send_verify_code, ): with client_request.session_transaction() as session: session["new-mob"] = "+12021234123" client_request.post( "main.user_profile_mobile_number_authenticate", _data={"password": "12345667"}, _expected_status=302, _expected_redirect=url_for( "main.user_profile_mobile_number_confirm", ), ) def test_should_show_confirm_after_mobile_number_change( client_request, ): with client_request.session_transaction() as session: session["new-mob-password-confirmed"] = True page = client_request.get("main.user_profile_mobile_number_confirm") assert "Change your mobile number" in page.text assert "Confirm" in page.text @pytest.mark.parametrize( "phone_number_to_register_with", [ "+12020900460", "+1800-555-555", ], ) def test_should_redirect_after_mobile_number_confirm( client_request, mocker, mock_update_user_attribute, mock_check_verify_code, phone_number_to_register_with, ): user_before = create_api_user_active(with_unique_id=True) user_after = create_api_user_active(with_unique_id=True) user_before["current_session_id"] = str(uuid.UUID(int=1)) user_after["current_session_id"] = str(uuid.UUID(int=2)) client_request.login(user_before) mocker.patch("app.user_api_client.get_user", side_effect=[user_after]) with client_request.session_transaction() as session: session["new-mob-password-confirmed"] = True session["new-mob"] = phone_number_to_register_with session["current_session_id"] = user_before["current_session_id"] client_request.post( "main.user_profile_mobile_number_confirm", _data={"sms_code": "123456"}, _expected_status=302, _expected_redirect=url_for( "main.user_profile", ), ) # make sure the current_session_id has changed to what the API returned with client_request.session_transaction() as session: assert session["current_session_id"] == user_after["current_session_id"] def test_should_show_password_page( client_request, ): page = client_request.get(("main.user_profile_password")) assert page.select_one("h1").text.strip() == "Change your password" def test_should_redirect_after_password_change( client_request, mock_update_user_password, mock_verify_password, ): client_request.post( "main.user_profile_password", _data={ "new_password": "the new password", "old_password": "the old password", }, _expected_status=302, _expected_redirect=url_for( "main.user_profile", ), ) def test_non_gov_user_cannot_see_change_email_link( client_request, api_nongov_user_active, mock_get_organizations, ): client_request.login(api_nongov_user_active) page = client_request.get("main.user_profile") assert not page.find("a", {"href": url_for("main.user_profile_email")}) assert page.select_one("h1").text.strip() == "User profile" def test_non_gov_user_cannot_access_change_email_page( client_request, api_nongov_user_active, mock_get_organizations, ): client_request.login(api_nongov_user_active) client_request.get("main.user_profile_email", _expected_status=403) def test_normal_user_doesnt_see_disable_platform_admin(client_request): client_request.get( "main.user_profile_disable_platform_admin_view", _expected_status=403 ) def test_platform_admin_can_see_disable_platform_admin_page( client_request, platform_admin_user ): client_request.login(platform_admin_user) page = client_request.get("main.user_profile_disable_platform_admin_view") assert page.select_one("h1").text.strip() == "Use platform admin view" assert page.select_one("input[checked]")["value"] == "True" def test_can_disable_platform_admin(client_request, platform_admin_user): client_request.login(platform_admin_user) with client_request.session_transaction() as session: assert "disable_platform_admin_view" not in session client_request.post( "main.user_profile_disable_platform_admin_view", _data={"enabled": False}, _expected_status=302, _expected_redirect=url_for("main.user_profile"), ) with client_request.session_transaction() as session: assert session["disable_platform_admin_view"] is True def test_can_reenable_platform_admin(client_request, platform_admin_user): client_request.login(platform_admin_user) with client_request.session_transaction() as session: session["disable_platform_admin_view"] = True client_request.post( "main.user_profile_disable_platform_admin_view", _data={"enabled": True}, _expected_status=302, _expected_redirect=url_for("main.user_profile"), ) with client_request.session_transaction() as session: assert session["disable_platform_admin_view"] is False