diff --git a/app/main/views/user_profile.py b/app/main/views/user_profile.py
index c52b41f86..f136c61b7 100644
--- a/app/main/views/user_profile.py
+++ b/app/main/views/user_profile.py
@@ -1,6 +1,13 @@
import json
-from flask import current_app, redirect, render_template, session, url_for
+from flask import (
+ abort,
+ current_app,
+ redirect,
+ render_template,
+ session,
+ url_for,
+)
from flask_login import current_user, login_required
from notifications_utils.url_safe_token import check_token
@@ -12,6 +19,7 @@ from app.main.forms import (
ChangeNameForm,
ChangePasswordForm,
ConfirmPasswordForm,
+ ServiceOnOffSettingForm,
TwoFactorForm,
)
from app.models.user import User
@@ -192,3 +200,26 @@ def user_profile_password():
'views/user-profile/change-password.html',
form=form
)
+
+
+@main.route("/user-profile/disable-platform-admin-view", methods=['GET', 'POST'])
+@login_required
+def user_profile_disable_platform_admin_view():
+ if not current_user.platform_admin and not session.get('disable_platform_admin_view'):
+ abort(403)
+
+ form = ServiceOnOffSettingForm(
+ name="Signing in again clears this setting",
+ enabled=not session.get('disable_platform_admin_view'),
+ truthy='Yes',
+ falsey='No',
+ )
+
+ if form.validate_on_submit():
+ session['disable_platform_admin_view'] = not form.enabled.data
+ return redirect(url_for('.user_profile'))
+
+ return render_template(
+ 'views/user-profile/disable-platform-admin-view.html',
+ form=form
+ )
diff --git a/app/models/user.py b/app/models/user.py
index 2fe6f01f1..210fdcb3e 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -48,6 +48,7 @@ class User(JSONModel, UserMixin):
super().__init__(_dict)
self.permissions = _dict.get('permissions', {})
self.max_failed_login_count = current_app.config['MAX_FAILED_LOGIN_COUNT']
+ self._platform_admin = _dict['platform_admin']
@classmethod
def from_id(cls, user_id):
@@ -172,6 +173,10 @@ class User(JSONModel, UserMixin):
super(User, self).is_authenticated
)
+ @property
+ def platform_admin(self):
+ return self._platform_admin and not session.get('disable_platform_admin_view', False)
+
def has_permissions(self, *permissions, restrict_admin_usage=False):
unknown_permissions = set(permissions) - all_permissions
if unknown_permissions:
diff --git a/app/navigation.py b/app/navigation.py
index 2a2366d63..63b6e2fa4 100644
--- a/app/navigation.py
+++ b/app/navigation.py
@@ -77,6 +77,7 @@ class HeaderNavigation(Navigation):
'user_profile_mobile_number_confirm',
'user_profile_name',
'user_profile_password',
+ 'user_profile_disable_platform_admin_view',
},
'platform-admin': {
'add_organisation',
@@ -571,6 +572,7 @@ class MainNavigation(Navigation):
'user_profile_mobile_number_confirm',
'user_profile_name',
'user_profile_password',
+ 'user_profile_disable_platform_admin_view',
'using_notify',
'verify',
'verify_email',
@@ -838,6 +840,7 @@ class CaseworkNavigation(Navigation):
'user_profile_mobile_number_confirm',
'user_profile_name',
'user_profile_password',
+ 'user_profile_disable_platform_admin_view',
'using_notify',
'verify',
'verify_email',
@@ -1106,6 +1109,7 @@ class OrgNavigation(Navigation):
'user_profile_mobile_number_confirm',
'user_profile_name',
'user_profile_password',
+ 'user_profile_disable_platform_admin_view',
'using_notify',
'verify',
'verify_email',
diff --git a/app/templates/views/user-profile.html b/app/templates/views/user-profile.html
index 24f3cdc02..a39853c32 100644
--- a/app/templates/views/user-profile.html
+++ b/app/templates/views/user-profile.html
@@ -45,6 +45,14 @@
{{ edit_field('Change', url_for('.user_profile_password')) }}
{% endcall %}
+ {% if current_user.platform_admin or session.get('disable_platform_admin_view') %}
+ {% call row() %}
+ {{ text_field('Use platform admin view') }}
+ {{ text_field('Yes' if not session.get('disable_platform_admin_view') else 'No') }}
+ {{ edit_field('Change', url_for('.user_profile_disable_platform_admin_view')) }}
+ {% endcall %}
+ {% endif %}
+
{% endcall %}
diff --git a/app/templates/views/user-profile/disable-platform-admin-view.html b/app/templates/views/user-profile/disable-platform-admin-view.html
new file mode 100644
index 000000000..334de0b13
--- /dev/null
+++ b/app/templates/views/user-profile/disable-platform-admin-view.html
@@ -0,0 +1,27 @@
+{% extends "withoutnav_template.html" %}
+{% from "components/form.html" import form_wrapper %}
+{% from "components/page-footer.html" import page_footer %}
+{% from "components/page-header.html" import page_header %}
+{% from "components/radios.html" import radios %}
+
+{% block per_page_title %}
+ Use platform admin view
+{% endblock %}
+
+{% block maincolumn_content %}
+
+
+
+ {{ page_header(
+ 'Use platform admin view',
+ back_link=url_for('.user_profile')
+ ) }}
+
+ {% call form_wrapper() %}
+ {{ radios(form.enabled) }}
+ {{ page_footer('Save') }}
+ {% endcall %}
+
+
+
+{% endblock %}
diff --git a/tests/app/main/views/test_user_profile.py b/tests/app/main/views/test_user_profile.py
index 519eba426..afd0f9a79 100644
--- a/tests/app/main/views/test_user_profile.py
+++ b/tests/app/main/views/test_user_profile.py
@@ -14,6 +14,18 @@ def test_should_show_overview_page(
):
page = client_request.get(('main.user_profile'))
assert page.select_one('h1').text.strip() == 'Your profile'
+ assert 'Use platform admin view' 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() == 'Your profile'
+ disable_platform_admin_row = page.select('tr')[-1]
+ assert ' '.join(disable_platform_admin_row.text.split()) == 'Use platform admin view Yes Change'
def test_should_show_name_page(
@@ -264,3 +276,49 @@ def test_non_gov_user_cannot_access_change_email_page(
):
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', _external=True),
+ )
+
+ 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', _external=True),
+ )
+
+ with client_request.session_transaction() as session:
+ assert session['disable_platform_admin_view'] is False
diff --git a/tests/app/models/test_user.py b/tests/app/models/test_user.py
index 92fabc0af..edc69b51e 100644
--- a/tests/app/models/test_user.py
+++ b/tests/app/models/test_user.py
@@ -9,7 +9,8 @@ def test_user(app_):
'email_address': 'test@user.gov.uk',
'mobile_number': '+4412341234',
'state': 'pending',
- 'failed_login_count': 0
+ 'failed_login_count': 0,
+ 'platform_admin': False,
}
user = User(user_data)
@@ -40,3 +41,21 @@ def test_activate_user(app_, api_user_pending, mock_activate_user):
def test_activate_user_already_active(app_, api_user_active, mock_activate_user):
assert User(api_user_active).activate() == User(api_user_active)
assert mock_activate_user.called is False
+
+
+@pytest.mark.parametrize('is_platform_admin, value_in_session, expected_result', [
+ (True, True, False),
+ (True, False, True),
+ (True, None, True),
+ (False, True, False),
+ (False, False, False),
+ (False, None, False),
+])
+def test_platform_admin_flag_set_in_session(client, mocker, is_platform_admin, value_in_session, expected_result):
+ session_dict = {}
+ if value_in_session is not None:
+ session_dict['disable_platform_admin_view'] = value_in_session
+
+ mocker.patch.dict('app.models.user.session', values=session_dict, clear=True)
+
+ assert User({'platform_admin': is_platform_admin}).platform_admin == expected_result
diff --git a/tests/conftest.py b/tests/conftest.py
index aa65d3cf4..1e8b8fa68 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1091,6 +1091,7 @@ def api_user_pending(fake_uuid):
'permissions': {},
'organisations': [],
'current_session_id': None,
+ 'password_changed_at': str(datetime.utcnow()),
}
return user_data
@@ -1114,6 +1115,7 @@ def platform_admin_user(fake_uuid):
'view_activity']},
'platform_admin': True,
'auth_type': 'sms_auth',
+ 'password_changed_at': str(datetime.utcnow()),
'services': [],
'organisations': [],
'current_session_id': None,
@@ -1429,6 +1431,7 @@ def api_user_locked(fake_uuid):
'auth_type': 'sms_auth',
'organisations': [],
'current_session_id': None,
+ 'platform_admin': False,
}
return user_data
@@ -1443,10 +1446,11 @@ def api_user_request_password_reset(fake_uuid):
'state': 'active',
'failed_login_count': 5,
'permissions': {},
- 'password_changed_at': None,
+ 'password_changed_at': str(datetime.utcnow()),
'auth_type': 'sms_auth',
'organisations': [],
'current_session_id': None,
+ 'platform_admin': False,
}
return user_data
@@ -1465,6 +1469,7 @@ def api_user_changed_password(fake_uuid):
'password_changed_at': str(datetime.utcnow() + timedelta(minutes=1)),
'organisations': [],
'current_session_id': None,
+ 'platform_admin': False,
}
return user_data
@@ -2165,12 +2170,14 @@ def mock_get_users_by_service(mocker):
'manage_settings',
'manage_api_keys']},
'state': 'active',
- 'password_changed_at': None,
+ 'password_changed_at': str(datetime.utcnow()),
'name': 'Test User',
'email_address': 'notify@digital.cabinet-office.gov.uk',
'auth_type': 'sms_auth',
'failed_login_count': 0,
- 'organisations': []}]
+ 'organisations': [],
+ 'platform_admin': False,
+ }]
return [data[0]]
# You shouldn’t be calling the user API client directly, so it’s the