import uuid import pytest from flask import url_for from app.main.views.sign_in import _reformat_keystring from app.models.user import User from tests.conftest import SERVICE_ONE_ID, normalize_spaces def test_render_sign_in_template_for_new_user(client_request): client_request.logout() page = client_request.get("main.sign_in") assert normalize_spaces(page.select_one("h1").text) == "Sign in" assert ( page.select("main p")[0].text == "Access your Notify.gov account by signing in with Login.gov:" ) # TODO: Fix this test to be less brittle! If the Login.gov link is enabled, # then these indices need to be 1 instead of 0. # Currently it's not enabled for the test or production environments. assert page.select("main a")[0].text == "Sign in with Login.gov" assert page.select("main a")[1].text == "Create Login.gov account" # TODO: We'll have to adjust this depending on whether Login.gov is # enabled or not; fix this in the future. assert "Sign in again" not in normalize_spaces(page.text) def test_reformat_keystring(): orig = "-----BEGIN PRIVATE KEY----- blah blah blah -----END PRIVATE KEY-----" expected = """-----BEGIN PRIVATE KEY----- blah blah blah -----END PRIVATE KEY----- """ reformatted = _reformat_keystring(orig) assert reformatted == expected def test_sign_in_explains_session_timeout(client_request): client_request.logout() page = client_request.get("main.sign_in", next="/foo") assert ( "We signed you out because you have not used Notify for a while." in page.text ) def test_sign_in_explains_other_browser(client_request, api_user_active, mocker): api_user_active["current_session_id"] = str(uuid.UUID(int=1)) mocker.patch("app.user_api_client.get_user", return_value=api_user_active) with client_request.session_transaction() as session: session["current_session_id"] = str(uuid.UUID(int=2)) page = client_request.get("main.sign_in", next="/foo") assert ( "We signed you out because you logged in to Notify on another device" in page.text ) def test_doesnt_redirect_to_sign_in_if_no_session_info( client_request, api_user_active, mock_get_organization_by_domain, ): api_user_active["current_session_id"] = str(uuid.UUID(int=1)) with client_request.session_transaction() as session: session["current_session_id"] = None with client_request.session_transaction() as session: session["current_session_id"] = None client_request.get("main.add_service") @pytest.mark.parametrize( ("db_sess_id", "cookie_sess_id"), [ (None, None), (None, uuid.UUID(int=1)), # BAD - cookie doesn't match db ( uuid.UUID(int=1), None, ), # BAD - has used other browsers before but this is a brand new browser with no cookie ( uuid.UUID(int=1), uuid.UUID(int=2), ), # BAD - this person has just signed in on a different browser ], ) def test_redirect_to_sign_in_if_logged_in_from_other_browser( client_request, api_user_active, mocker, db_sess_id, cookie_sess_id ): api_user_active["current_session_id"] = db_sess_id mocker.patch("app.user_api_client.get_user", return_value=api_user_active) with client_request.session_transaction() as session: session["current_session_id"] = str(cookie_sess_id) client_request.get( "main.choose_account", _expected_status=302, _expected_redirect=url_for("main.sign_in", next="/accounts"), ) def test_logged_in_user_redirects_to_account(client_request): client_request.get( "main.sign_in", _expected_status=302, _expected_redirect=url_for("main.show_accounts_or_dashboard"), ) def test_logged_in_user_redirects_to_next_url(client_request): client_request.get( "main.sign_in", next="/user-profile", _expected_status=302, _expected_redirect=url_for("main.user_profile"), ) def test_logged_in_user_doesnt_do_evil_redirect(client_request): client_request.get( "main.sign_in", next="http://www.evil.com", _expected_status=302, _expected_redirect=url_for("main.show_accounts_or_dashboard"), ) @pytest.mark.parametrize( "redirect_url", [ None, f"/services/{SERVICE_ONE_ID}/templates", ], ) @pytest.mark.parametrize( ("email_address", "password"), [ ("valid@example.gsa.gov", "val1dPassw0rd!"), (" valid@example.gsa.gov ", " val1dPassw0rd! "), ], ) def test_process_sms_auth_sign_in_return_2fa_template( client_request, api_user_active, mock_send_verify_code, mock_get_user, mock_get_user_by_email, mock_verify_password, email_address, password, redirect_url, ): client_request.logout() client_request.post( "main.sign_in", next=redirect_url, _data={ "email_address": email_address, "password": password, }, _expected_redirect=url_for(".two_factor_sms", next=redirect_url), ) mock_verify_password.assert_called_with(api_user_active["id"], password) mock_get_user_by_email.assert_called_with("valid@example.gsa.gov") @pytest.mark.parametrize( "redirect_url", [ None, f"/services/{SERVICE_ONE_ID}/templates", ], ) def test_process_email_auth_sign_in_return_2fa_template( client_request, api_user_active_email_auth, mock_send_verify_code, mock_verify_password, mocker, redirect_url, ): client_request.logout() mocker.patch( "app.user_api_client.get_user", return_value=api_user_active_email_auth ) mocker.patch( "app.user_api_client.get_user_by_email", return_value=api_user_active_email_auth ) client_request.post( "main.sign_in", next=redirect_url, _data={ "email_address": "valid@example.gsa.gov", "password": "val1dPassw0rd!", }, _expected_redirect=url_for(".two_factor_email_sent", next=redirect_url), ) mock_send_verify_code.assert_called_with( api_user_active_email_auth["id"], "email", None, redirect_url ) mock_verify_password.assert_called_with( api_user_active_email_auth["id"], "val1dPassw0rd!" ) def test_should_return_locked_out_true_when_user_is_locked( client_request, mock_get_user_by_email_locked, ): client_request.logout() page = client_request.post( "main.sign_in", _data={ "email_address": "valid@example.gsa.gov", "password": "whatIsMyPassword!", }, _expected_status=200, ) assert "The email address or password you entered is incorrect" in page.text def test_should_return_200_when_user_does_not_exist( client_request, mock_get_user_by_email_not_found, ): client_request.logout() page = client_request.post( "main.sign_in", _data={"email_address": "notfound@gsa.gov", "password": "doesNotExist!"}, _expected_status=200, ) assert "The email address or password you entered is incorrect" in page.text def test_should_return_redirect_when_user_is_pending( client_request, mock_get_user_by_email_pending, api_user_pending, mock_verify_password, ): client_request.logout() client_request.post( "main.sign_in", _data={ "email_address": "pending_user@example.gsa.gov", "password": "val1dPassw0rd!", }, _expected_redirect=url_for("main.resend_email_verification"), ) with client_request.session_transaction() as s: assert s["user_details"] == { "email": api_user_pending["email_address"], "id": api_user_pending["id"], } @pytest.mark.parametrize( "redirect_url", [ None, f"/services/{SERVICE_ONE_ID}/templates", ], ) def test_should_attempt_redirect_when_user_is_pending( client_request, mock_get_user_by_email_pending, mock_verify_password, redirect_url ): client_request.logout() client_request.post( "main.sign_in", next=redirect_url, _data={ "email_address": "pending_user@example.gsa.gov", "password": "val1dPassw0rd!", }, _expected_redirect=url_for("main.resend_email_verification", next=redirect_url), ) def test_email_address_is_treated_case_insensitively_when_signing_in_as_invited_user( client_request, mocker, mock_verify_password, api_user_active, sample_invite, mock_accept_invite, mock_send_verify_code, mock_get_invited_user_by_id, ): client_request.logout() sample_invite["email_address"] = "TEST@user.gsa.gov" mocker.patch( "app.models.user.User.from_email_address_and_password_or_none", return_value=User(api_user_active), ) with client_request.session_transaction() as session: session["invited_user_id"] = sample_invite["id"] client_request.post( "main.sign_in", _data={"email_address": "test@user.gsa.gov", "password": "val1dPassw0rd!"}, ) assert mock_accept_invite.called assert mock_send_verify_code.called mock_get_invited_user_by_id.assert_called_once_with(sample_invite["id"]) def test_when_signing_in_as_invited_user_you_cannot_accept_an_invite_for_another_email_address( client_request, mocker, mock_verify_password, api_user_active, sample_invite, mock_accept_invite, mock_send_verify_code, mock_get_invited_user_by_id, ): sample_invite["email_address"] = "some_other_user@user.gsa.gov" mocker.patch( "app.models.user.User.from_email_address_and_password_or_none", return_value=User(api_user_active), ) client_request.logout() with client_request.session_transaction() as session: session["invited_user_id"] = sample_invite["id"] page = client_request.post( "main.sign_in", _data={"email_address": "test@user.gsa.gov", "password": "val1dPassw0rd!"}, _expected_status=403, ) assert mock_accept_invite.called is False assert mock_send_verify_code.called is False assert ( page.select_one(".banner-dangerous").text.strip() == "You cannot accept an invite for another person." )