mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-25 00:33:58 -04:00
Update email_access_validated_at on invite
Accepting an invite means that you’ve just clicked a link in your email inbox. This shows that you have access to your email. We can make a record of this, thereby extending the time before we ask you to revalidate your email address.
This commit is contained in:
@@ -52,6 +52,7 @@ def accept_invite(token):
|
|||||||
existing_user = User.from_email_address_or_none(invited_user.email_address)
|
existing_user = User.from_email_address_or_none(invited_user.email_address)
|
||||||
|
|
||||||
if existing_user:
|
if existing_user:
|
||||||
|
existing_user.update_email_access_validated_at()
|
||||||
invited_user.accept_invite()
|
invited_user.accept_invite()
|
||||||
if existing_user in Users(invited_user.service):
|
if existing_user in Users(invited_user.service):
|
||||||
return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
|
return redirect(url_for('main.service_dashboard', service_id=invited_user.service))
|
||||||
@@ -117,6 +118,7 @@ def accept_org_invite(token):
|
|||||||
organisation_users = OrganisationUsers(invited_org_user.organisation)
|
organisation_users = OrganisationUsers(invited_org_user.organisation)
|
||||||
|
|
||||||
if existing_user:
|
if existing_user:
|
||||||
|
existing_user.update_email_access_validated_at()
|
||||||
invited_org_user.accept_invite()
|
invited_org_user.accept_invite()
|
||||||
if existing_user not in organisation_users:
|
if existing_user not in organisation_users:
|
||||||
existing_user.add_to_organisation(organisation_id=invited_org_user.organisation)
|
existing_user.add_to_organisation(organisation_id=invited_org_user.organisation)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from unittest.mock import ANY
|
|||||||
import pytest
|
import pytest
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
from freezegun import freeze_time
|
||||||
|
|
||||||
from app.models.user import InvitedOrgUser
|
from app.models.user import InvitedOrgUser
|
||||||
from tests.conftest import ORGANISATION_ID, normalize_spaces
|
from tests.conftest import ORGANISATION_ID, normalize_spaces
|
||||||
@@ -138,14 +139,17 @@ def test_user_invite_already_accepted(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_user_invite_already_is_member_of_organisation(
|
def test_existing_user_invite_already_is_member_of_organisation(
|
||||||
client,
|
client,
|
||||||
mock_check_org_invite_token,
|
mock_check_org_invite_token,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
|
api_user_active,
|
||||||
mock_get_users_for_organisation,
|
mock_get_users_for_organisation,
|
||||||
mock_accept_org_invite,
|
mock_accept_org_invite,
|
||||||
mock_add_user_to_organisation,
|
mock_add_user_to_organisation,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
response = client.get(url_for('main.accept_org_invite', token='thisisnotarealtoken'))
|
response = client.get(url_for('main.accept_org_invite', token='thisisnotarealtoken'))
|
||||||
|
|
||||||
@@ -160,8 +164,13 @@ def test_existing_user_invite_already_is_member_of_organisation(
|
|||||||
mock_accept_org_invite.assert_called_once_with(ORGANISATION_ID, ANY)
|
mock_accept_org_invite.assert_called_once_with(ORGANISATION_ID, ANY)
|
||||||
mock_get_user_by_email.assert_called_once_with('invited_user@test.gov.uk')
|
mock_get_user_by_email.assert_called_once_with('invited_user@test.gov.uk')
|
||||||
mock_get_users_for_organisation.assert_called_once_with(ORGANISATION_ID)
|
mock_get_users_for_organisation.assert_called_once_with(ORGANISATION_ID)
|
||||||
|
mock_update_user_attribute.assert_called_once_with(
|
||||||
|
api_user_active['id'],
|
||||||
|
email_access_validated_at='2021-12-12T12:12:12',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_user_invite_not_a_member_of_organisation(
|
def test_existing_user_invite_not_a_member_of_organisation(
|
||||||
client,
|
client,
|
||||||
api_user_active,
|
api_user_active,
|
||||||
@@ -170,6 +179,7 @@ def test_existing_user_invite_not_a_member_of_organisation(
|
|||||||
mock_get_users_for_organisation,
|
mock_get_users_for_organisation,
|
||||||
mock_accept_org_invite,
|
mock_accept_org_invite,
|
||||||
mock_add_user_to_organisation,
|
mock_add_user_to_organisation,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
response = client.get(url_for('main.accept_org_invite', token='thisisnotarealtoken'))
|
response = client.get(url_for('main.accept_org_invite', token='thisisnotarealtoken'))
|
||||||
|
|
||||||
@@ -188,6 +198,10 @@ def test_existing_user_invite_not_a_member_of_organisation(
|
|||||||
ORGANISATION_ID,
|
ORGANISATION_ID,
|
||||||
api_user_active['id'],
|
api_user_active['id'],
|
||||||
)
|
)
|
||||||
|
mock_update_user_attribute.assert_called_once_with(
|
||||||
|
mock_get_user_by_email.side_effect(None)['id'],
|
||||||
|
email_access_validated_at='2021-12-12T12:12:12',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_user_accepts_invite(
|
def test_user_accepts_invite(
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
from unittest.mock import ANY, Mock
|
from unittest.mock import ANY, Mock, call
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from bs4 import BeautifulSoup
|
from bs4 import BeautifulSoup
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
from freezegun import freeze_time
|
||||||
from notifications_python_client.errors import HTTPError
|
from notifications_python_client.errors import HTTPError
|
||||||
|
|
||||||
import app
|
import app
|
||||||
@@ -30,6 +31,7 @@ def mock_check_invite_token(mocker, sample_invite):
|
|||||||
return mocker.patch('app.invite_api_client.check_token', return_value=sample_invite)
|
return mocker.patch('app.invite_api_client.check_token', return_value=sample_invite)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_user_accept_invite_calls_api_and_redirects_to_dashboard(
|
def test_existing_user_accept_invite_calls_api_and_redirects_to_dashboard(
|
||||||
client,
|
client,
|
||||||
service_one,
|
service_one,
|
||||||
@@ -43,6 +45,7 @@ def test_existing_user_accept_invite_calls_api_and_redirects_to_dashboard(
|
|||||||
mocker,
|
mocker,
|
||||||
mock_events,
|
mock_events,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
expected_service = service_one['id']
|
expected_service = service_one['id']
|
||||||
expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
|
expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
|
||||||
@@ -75,6 +78,7 @@ def test_broadcast_service_shows_tour(
|
|||||||
mock_no_users_for_service,
|
mock_no_users_for_service,
|
||||||
mock_accept_invite,
|
mock_accept_invite,
|
||||||
mock_add_user_to_service,
|
mock_add_user_to_service,
|
||||||
|
mock_update_user_attribute,
|
||||||
mocker,
|
mocker,
|
||||||
mock_events,
|
mock_events,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
@@ -114,6 +118,7 @@ def test_existing_user_with_no_permissions_or_folder_permissions_accept_invite(
|
|||||||
mock_get_service,
|
mock_get_service,
|
||||||
mock_events,
|
mock_events,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
expected_service = service_one['id']
|
expected_service = service_one['id']
|
||||||
sample_invite['permissions'] = ''
|
sample_invite['permissions'] = ''
|
||||||
@@ -136,6 +141,7 @@ def test_if_existing_user_accepts_twice_they_redirect_to_sign_in(
|
|||||||
sample_invite,
|
sample_invite,
|
||||||
mock_check_invite_token,
|
mock_check_invite_token,
|
||||||
mock_get_service,
|
mock_get_service,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
sample_invite['status'] = 'accepted'
|
sample_invite['status'] = 'accepted'
|
||||||
|
|
||||||
@@ -149,6 +155,9 @@ def test_if_existing_user_accepts_twice_they_redirect_to_sign_in(
|
|||||||
'You need to sign in again',
|
'You need to sign in again',
|
||||||
'We signed you out because you have not used Notify for a while.',
|
'We signed you out because you have not used Notify for a while.',
|
||||||
)
|
)
|
||||||
|
# We don’t let people update `email_access_validated_at` using an
|
||||||
|
# already-accepted invite
|
||||||
|
assert mock_update_user_attribute.called is False
|
||||||
|
|
||||||
|
|
||||||
def test_invite_goes_in_session(
|
def test_invite_goes_in_session(
|
||||||
@@ -226,6 +235,7 @@ def test_accepting_invite_removes_invite_from_session(
|
|||||||
assert 'invited_user_id' not in session
|
assert 'invited_user_id' not in session
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12T12:12:12')
|
||||||
def test_existing_user_of_service_get_redirected_to_signin(
|
def test_existing_user_of_service_get_redirected_to_signin(
|
||||||
client,
|
client,
|
||||||
mocker,
|
mocker,
|
||||||
@@ -235,6 +245,7 @@ def test_existing_user_of_service_get_redirected_to_signin(
|
|||||||
mock_get_user_by_email,
|
mock_get_user_by_email,
|
||||||
mock_check_invite_token,
|
mock_check_invite_token,
|
||||||
mock_accept_invite,
|
mock_accept_invite,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
sample_invite['email_address'] = api_user_active['email_address']
|
sample_invite['email_address'] = api_user_active['email_address']
|
||||||
mocker.patch('app.models.user.Users.client_method', return_value=[api_user_active])
|
mocker.patch('app.models.user.Users.client_method', return_value=[api_user_active])
|
||||||
@@ -263,6 +274,7 @@ def test_accept_invite_redirects_if_api_raises_an_error_that_they_are_already_pa
|
|||||||
mock_get_service,
|
mock_get_service,
|
||||||
mock_no_users_for_service,
|
mock_no_users_for_service,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
mocker.patch('app.user_api_client.add_user_to_service', side_effect=HTTPError(
|
mocker.patch('app.user_api_client.add_user_to_service', side_effect=HTTPError(
|
||||||
response=Mock(
|
response=Mock(
|
||||||
@@ -293,6 +305,7 @@ def test_existing_signed_out_user_accept_invite_redirects_to_sign_in(
|
|||||||
mocker,
|
mocker,
|
||||||
mock_events,
|
mock_events,
|
||||||
mock_get_user,
|
mock_get_user,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
expected_service = service_one['id']
|
expected_service = service_one['id']
|
||||||
expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
|
expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
|
||||||
@@ -386,6 +399,7 @@ def test_cancelled_invited_user_accepts_invited_redirect_to_cancelled_invitation
|
|||||||
mock_get_service,
|
mock_get_service,
|
||||||
sample_invite,
|
sample_invite,
|
||||||
mock_check_invite_token,
|
mock_check_invite_token,
|
||||||
|
mock_update_user_attribute,
|
||||||
):
|
):
|
||||||
sample_invite['status'] = 'cancelled'
|
sample_invite['status'] = 'cancelled'
|
||||||
response = client.get(url_for('main.accept_invite', token='thisisnotarealtoken'))
|
response = client.get(url_for('main.accept_invite', token='thisisnotarealtoken'))
|
||||||
@@ -394,6 +408,9 @@ def test_cancelled_invited_user_accepts_invited_redirect_to_cancelled_invitation
|
|||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
assert page.h1.string.strip() == 'The invitation you were sent has been cancelled'
|
assert page.h1.string.strip() == 'The invitation you were sent has been cancelled'
|
||||||
|
# We don’t let people update `email_access_validated_at` using an
|
||||||
|
# cancelled invite
|
||||||
|
assert mock_update_user_attribute.called is False
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('admin_endpoint, api_endpoint', [
|
@pytest.mark.parametrize('admin_endpoint, api_endpoint', [
|
||||||
@@ -653,6 +670,7 @@ def test_new_invited_user_is_redirected_to_correct_place(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_user_accepts_and_sets_email_auth(
|
def test_existing_user_accepts_and_sets_email_auth(
|
||||||
client_request,
|
client_request,
|
||||||
api_user_active,
|
api_user_active,
|
||||||
@@ -679,10 +697,14 @@ def test_existing_user_accepts_and_sets_email_auth(
|
|||||||
)
|
)
|
||||||
|
|
||||||
mock_get_existing_user_by_email.assert_called_once_with('test@user.gov.uk')
|
mock_get_existing_user_by_email.assert_called_once_with('test@user.gov.uk')
|
||||||
mock_update_user_attribute.assert_called_once_with(api_user_active['id'], auth_type='email_auth')
|
assert mock_update_user_attribute.call_args_list == [
|
||||||
|
call(api_user_active['id'], email_access_validated_at='2021-12-12T12:12:12'),
|
||||||
|
call(api_user_active['id'], auth_type='email_auth'),
|
||||||
|
]
|
||||||
mock_add_user_to_service.assert_called_once_with(ANY, api_user_active['id'], ANY, ANY)
|
mock_add_user_to_service.assert_called_once_with(ANY, api_user_active['id'], ANY, ANY)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_platform_admin_user_accepts_and_preserves_auth(
|
def test_platform_admin_user_accepts_and_preserves_auth(
|
||||||
client_request,
|
client_request,
|
||||||
platform_admin_user,
|
platform_admin_user,
|
||||||
@@ -691,7 +713,6 @@ def test_platform_admin_user_accepts_and_preserves_auth(
|
|||||||
mock_check_invite_token,
|
mock_check_invite_token,
|
||||||
mock_no_users_for_service,
|
mock_no_users_for_service,
|
||||||
mock_accept_invite,
|
mock_accept_invite,
|
||||||
mock_update_user_attribute,
|
|
||||||
mock_add_user_to_service,
|
mock_add_user_to_service,
|
||||||
mocker
|
mocker
|
||||||
):
|
):
|
||||||
@@ -700,6 +721,10 @@ def test_platform_admin_user_accepts_and_preserves_auth(
|
|||||||
service_one['permissions'].append('email_auth')
|
service_one['permissions'].append('email_auth')
|
||||||
|
|
||||||
mocker.patch('app.user_api_client.get_user_by_email', return_value=platform_admin_user)
|
mocker.patch('app.user_api_client.get_user_by_email', return_value=platform_admin_user)
|
||||||
|
mock_update_user_attribute = mocker.patch(
|
||||||
|
'app.user_api_client.update_user_attribute',
|
||||||
|
return_value=platform_admin_user,
|
||||||
|
)
|
||||||
|
|
||||||
client_request.login(platform_admin_user)
|
client_request.login(platform_admin_user)
|
||||||
|
|
||||||
@@ -710,10 +735,14 @@ def test_platform_admin_user_accepts_and_preserves_auth(
|
|||||||
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not mock_update_user_attribute.called
|
mock_update_user_attribute.assert_called_once_with(
|
||||||
|
platform_admin_user['id'],
|
||||||
|
email_access_validated_at='2021-12-12T12:12:12',
|
||||||
|
)
|
||||||
assert mock_add_user_to_service.called
|
assert mock_add_user_to_service.called
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_user_doesnt_get_auth_changed_by_service_without_permission(
|
def test_existing_user_doesnt_get_auth_changed_by_service_without_permission(
|
||||||
client_request,
|
client_request,
|
||||||
api_user_active,
|
api_user_active,
|
||||||
@@ -740,9 +769,13 @@ def test_existing_user_doesnt_get_auth_changed_by_service_without_permission(
|
|||||||
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not mock_update_user_attribute.called
|
mock_update_user_attribute.assert_called_once_with(
|
||||||
|
api_user_active['id'],
|
||||||
|
email_access_validated_at='2021-12-12T12:12:12',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_email_auth_user_without_phone_cannot_set_sms_auth(
|
def test_existing_email_auth_user_without_phone_cannot_set_sms_auth(
|
||||||
client_request,
|
client_request,
|
||||||
api_user_active,
|
api_user_active,
|
||||||
@@ -772,9 +805,13 @@ def test_existing_email_auth_user_without_phone_cannot_set_sms_auth(
|
|||||||
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
_expected_redirect=url_for('main.service_dashboard', service_id=service_one['id'], _external=True),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert not mock_update_user_attribute.called
|
mock_update_user_attribute.assert_called_once_with(
|
||||||
|
api_user_active['id'],
|
||||||
|
email_access_validated_at='2021-12-12T12:12:12',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@freeze_time('2021-12-12 12:12:12')
|
||||||
def test_existing_email_auth_user_with_phone_can_set_sms_auth(
|
def test_existing_email_auth_user_with_phone_can_set_sms_auth(
|
||||||
client_request,
|
client_request,
|
||||||
api_user_active,
|
api_user_active,
|
||||||
@@ -800,4 +837,7 @@ def test_existing_email_auth_user_with_phone_can_set_sms_auth(
|
|||||||
)
|
)
|
||||||
|
|
||||||
mock_get_existing_user_by_email.assert_called_once_with(sample_invite['email_address'])
|
mock_get_existing_user_by_email.assert_called_once_with(sample_invite['email_address'])
|
||||||
mock_update_user_attribute.assert_called_once_with(api_user_active['id'], auth_type='sms_auth')
|
assert mock_update_user_attribute.call_args_list == [
|
||||||
|
call(api_user_active['id'], email_access_validated_at='2021-12-12T12:12:12'),
|
||||||
|
call(api_user_active['id'], auth_type='sms_auth'),
|
||||||
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user