diff --git a/app/main/views/manage_users.py b/app/main/views/manage_users.py index cbbff87c8..e2a8cf785 100644 --- a/app/main/views/manage_users.py +++ b/app/main/views/manage_users.py @@ -1,4 +1,12 @@ -from flask import abort, flash, redirect, render_template, request, url_for +from flask import ( + abort, + flash, + redirect, + render_template, + request, + session, + url_for, +) from flask_login import current_user, login_required from notifications_python_client.errors import HTTPError @@ -9,7 +17,12 @@ from app import ( user_api_client, ) from app.main import main -from app.main.forms import InviteUserForm, PermissionsForm, SearchUsersForm +from app.main.forms import ( + ChangeEmailForm, + InviteUserForm, + PermissionsForm, + SearchUsersForm, +) from app.models.user import permissions from app.utils import user_has_permissions @@ -122,6 +135,69 @@ def remove_user_from_service(service_id, user_id): ) +@main.route("/services//users//edit-email", methods=['GET', 'POST']) +@login_required +@user_has_permissions('manage_service') +def edit_user_email(service_id, user_id): + user = user_api_client.get_user(user_id) + user_email = user.email_address + + def _is_email_already_in_use(email): + return user_api_client.is_email_already_in_use(email) + + form = ChangeEmailForm(_is_email_already_in_use, email_address=user_email) + if form.validate_on_submit(): + session['team_member_email_change'] = form.email_address.data + + return redirect(url_for('.confirm_edit_user_email', user_id=user.id, service_id=service_id)) + + return render_template( + 'views/manage-users/edit-user-email.html', + user=user, + form=form, + service_id=service_id + ) + + +@main.route("/services//users//edit-email/confirm", methods=['GET', 'POST']) +@login_required +@user_has_permissions('manage_service') +def confirm_edit_user_email(service_id, user_id): + user = user_api_client.get_user(user_id) + if 'team_member_email_change' in session: + new_email = session['team_member_email_change'] + else: + return redirect(url_for( + '.edit_user_email', + service_id=service_id, + user_id=user_id + )) + if request.method == 'POST': + try: + user_api_client.update_user_attribute(user_id, email_address=new_email) + except HTTPError as e: + if e.status_code == 403: + flash("You don't have permission to edit users emails for this service", 'info') + return redirect(url_for( + '.manage_users', + service_id=service_id)) + else: + abort(500, e) + finally: + session.pop("team_member_email_change", None) + + return redirect(url_for( + '.manage_users', + service_id=service_id + )) + return render_template( + 'views/manage-users/confirm-edit-user-email.html', + user=user, + service_id=service_id, + new_email=new_email + ) + + @main.route("/services//cancel-invited-user/", methods=['GET']) @user_has_permissions('manage_service') def cancel_invited_user(service_id, invited_user_id): diff --git a/app/navigation.py b/app/navigation.py index 6bcaf66ae..125c81bc3 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -135,6 +135,7 @@ class HeaderNavigation(Navigation): 'choose_template', 'choose_template_to_copy', 'confirm_edit_organisation_name', + 'confirm_edit_user_email', 'confirm_redact_template', 'conversation', 'conversation_reply', @@ -157,6 +158,7 @@ class HeaderNavigation(Navigation): 'edit_service_template', 'edit_template_postage', 'edit_user_org_permissions', + 'edit_user_email', 'edit_user_permissions', 'email_not_received', 'email_template', @@ -324,6 +326,8 @@ class MainNavigation(Navigation): 'view_template_versions', }, 'team-members': { + 'confirm_edit_user_email', + 'edit_user_email', 'edit_user_permissions', 'invite_user', 'manage_users', @@ -579,6 +583,7 @@ class CaseworkNavigation(Navigation): 'choose_template_to_copy', 'clear_cache', 'confirm_edit_organisation_name', + 'confirm_edit_user_email', 'confirm_redact_template', 'conversation', 'conversation_reply', @@ -603,6 +608,7 @@ class CaseworkNavigation(Navigation): 'edit_provider', 'edit_service_template', 'edit_template_postage', + 'edit_user_email', 'edit_user_org_permissions', 'edit_user_permissions', 'email_branding', @@ -816,6 +822,7 @@ class OrgNavigation(Navigation): 'choose_template', 'choose_template_to_copy', 'clear_cache', + 'confirm_edit_user_email', 'confirm_redact_template', 'conversation', 'conversation_reply', @@ -839,6 +846,7 @@ class OrgNavigation(Navigation): 'edit_provider', 'edit_service_template', 'edit_template_postage', + 'edit_user_email', 'edit_user_permissions', 'email_branding', 'email_not_received', diff --git a/app/templates/views/edit-user-permissions.html b/app/templates/views/edit-user-permissions.html index 4a59b6363..4bd585d96 100644 --- a/app/templates/views/edit-user-permissions.html +++ b/app/templates/views/edit-user-permissions.html @@ -14,7 +14,7 @@

- {{ user.email_address }} + {{ user.email_address }} Change

diff --git a/app/templates/views/manage-users.html b/app/templates/views/manage-users.html index da3157c5a..645f67f1d 100644 --- a/app/templates/views/manage-users.html +++ b/app/templates/views/manage-users.html @@ -63,7 +63,7 @@ {% if user.status == 'pending' %} Cancel invitation {% elif user.state == 'active' and current_user.id != user.id %} - Edit permissions + Edit team member {% endif %} {% endif %} diff --git a/app/templates/views/manage-users/confirm-edit-user-email.html b/app/templates/views/manage-users/confirm-edit-user-email.html new file mode 100644 index 000000000..322d470fe --- /dev/null +++ b/app/templates/views/manage-users/confirm-edit-user-email.html @@ -0,0 +1,30 @@ +{% extends "withnav_template.html" %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/form.html" import form_wrapper %} + +{% block per_page_title %} + Confirm change of email address +{% endblock %} + +{% block maincolumn_content %} + +

Confirm change of email address

+ +
+
+ {% call form_wrapper() %} +

New email address:

+
+

{{ new_email }}

+
+

We will send {{ user.name }} an email to tell them about the change.

+ {{ page_footer( + 'Confirm', + destructive=destructive, + back_link=url_for('.edit_user_email', service_id=service_id, user_id=user.id) + ) }} + {% endcall %} +
+
+ +{% endblock %} diff --git a/app/templates/views/manage-users/edit-user-email.html b/app/templates/views/manage-users/edit-user-email.html new file mode 100644 index 000000000..d1bdfeb91 --- /dev/null +++ b/app/templates/views/manage-users/edit-user-email.html @@ -0,0 +1,27 @@ +{% extends "withnav_template.html" %} +{% from "components/textbox.html" import textbox %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/form.html" import form_wrapper %} + +{% block per_page_title %} + Change team member’s email address +{% endblock %} + +{% block maincolumn_content %} + +

Change team member’s email address

+

This will change the email address for {{ user.name }}.

+
+
+ {% call form_wrapper() %} + {{ textbox(form.email_address) }} + {{ page_footer( + 'Save', + back_link=url_for('.edit_user_permissions', service_id=service_id, user_id=user.id), + back_link_text="Back" + ) }} + {% endcall %} +
+
+ +{% endblock %} diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py index 5cf45990d..162f81b1f 100644 --- a/tests/app/main/views/test_manage_users.py +++ b/tests/app/main/views/test_manage_users.py @@ -39,7 +39,7 @@ from tests.conftest import service_one as create_sample_service 'Can’t Add and edit templates ' 'Can’t Manage settings, team and usage ' 'Can’t Manage API integration ' - 'Edit permissions' + 'Edit team member' ) ), ( @@ -769,3 +769,110 @@ def test_can_invite_user_as_platform_admin( response = logged_in_client.get(url_for('main.manage_users', service_id=service_one['id'])) resp_text = response.get_data(as_text=True) assert url_for('.invite_user', service_id=service_one['id']) in resp_text + + +def test_edit_user_email_page( + client_request, + active_user_with_permissions, + service_one, + mocker +): + user = active_user_with_permissions + test_user = 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=test_user.id + ) + + 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 page.select('button[type=submit]')[0].text == "Save" + + +def test_edit_user_email_redirects_to_confirmation( + logged_in_client, + active_user_with_permissions, + service_one, + mocker, + mock_get_user, +): + response = logged_in_client.post( + url_for( + 'main.edit_user_email', + service_id=service_one['id'], + user_id=active_user_with_permissions.id)) + assert response.status_code == 302 + assert response.location == url_for( + 'main.confirm_edit_user_email', + service_id=service_one['id'], + user_id=active_user_with_permissions.id, + _external=True + ) + + +def test_confirm_edit_user_email_page( + logged_in_client, + active_user_with_permissions, + service_one, + mocker, + mock_get_user, +): + new_email = 'new_email@gov.uk' + with logged_in_client.session_transaction() as session: + session['team_member_email_change'] = new_email + response = logged_in_client.get(url_for( + 'main.confirm_edit_user_email', + service_id=service_one['id'], + user_id=active_user_with_permissions.id + )) + + assert 'Confirm change of email address' in response.get_data(as_text=True) + 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 response.get_data(as_text=True) + assert 'Confirm' in response.get_data(as_text=True) + assert response.status_code == 200 + + +def test_confirm_edit_user_email_page_redirects_if_session_empty( + logged_in_client, + active_user_with_permissions, + service_one, + mocker, + mock_get_user, +): + response = logged_in_client.get(url_for( + 'main.confirm_edit_user_email', + service_id=service_one['id'], + user_id=active_user_with_permissions.id + )) + assert response.status_code == 302 + assert 'Confirm change of email address' not in response.get_data(as_text=True) + + +def test_confirm_edit_user_email_changes_user_email( + logged_in_client, + active_user_with_permissions, + service_one, + mocker, + mock_get_user, + mock_update_user_attribute +): + new_email = 'new_email@gov.uk' + with logged_in_client.session_transaction() as session: + session['team_member_email_change'] = new_email + response = logged_in_client.post( + url_for( + 'main.confirm_edit_user_email', + service_id=service_one['id'], + user_id=active_user_with_permissions.id)) + assert response.status_code == 302 + assert response.location == url_for( + 'main.manage_users', service_id=service_one['id'], _external=True) + mock_update_user_attribute.assert_called_once_with(active_user_with_permissions.id, email_address=new_email)