diff --git a/app/main/views/user_profile.py b/app/main/views/user_profile.py
index c52b41f86..d69398102 100644
--- a/app/main/views/user_profile.py
+++ b/app/main/views/user_profile.py
@@ -1,6 +1,6 @@
import json
-from flask import current_app, redirect, render_template, session, url_for
+from flask import current_app, redirect, render_template, session, url_for, abort
from flask_login import current_user, login_required
from notifications_utils.url_safe_token import check_token
@@ -12,6 +12,7 @@ from app.main.forms import (
ChangeNameForm,
ChangePasswordForm,
ConfirmPasswordForm,
+ ServiceOnOffSettingForm,
TwoFactorForm,
)
from app.models.user import User
@@ -192,3 +193,26 @@ def user_profile_password():
'views/user-profile/change-password.html',
form=form
)
+
+
+@main.route("/user-profile/suppress-platform-admin", methods=['GET', 'POST'])
+@login_required
+def user_profile_suppress_platform_admin():
+ if not current_user.platform_admin and not session.get('suppress_platform_admin'):
+ abort(403)
+
+ form = ServiceOnOffSettingForm(
+ name="This setting will be cleared if you sign out and sign in again",
+ enabled=session.get('suppress_platform_admin', False),
+ truthy='Yes (view as regular user)',
+ falsey='No (view as platform admin)',
+ )
+
+ if form.validate_on_submit():
+ session['suppress_platform_admin'] = form.enabled.data
+ return redirect(url_for('.user_profile'))
+
+ return render_template(
+ 'views/user-profile/suppress-platform-admin.html',
+ form=form
+ )
diff --git a/app/models/user.py b/app/models/user.py
index 087157795..10921dee4 100644
--- a/app/models/user.py
+++ b/app/models/user.py
@@ -49,6 +49,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):
@@ -173,6 +174,10 @@ class User(JSONModel, UserMixin):
super(User, self).is_authenticated
)
+ @property
+ def platform_admin(self):
+ return self._platform_admin and not session.get('suppress_platform_admin', False)
+
def has_permissions(self, *permissions, restrict_admin_usage=False):
unknown_permissions = set(permissions) - all_permissions
if unknown_permissions:
diff --git a/app/templates/views/user-profile.html b/app/templates/views/user-profile.html
index 24f3cdc02..b259a2d93 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('suppress_platform_admin') %}
+ {% call row() %}
+ {{ text_field('Suppress Platform Admin') }}
+ {{ text_field('On' if session.get('suppress_platform_admin') else 'Off') }}
+ {{ edit_field('Change', url_for('.user_profile_suppress_platform_admin')) }}
+ {% endcall %}
+ {% endif %}
+
{% endcall %}
diff --git a/app/templates/views/user-profile/suppress-platform-admin.html b/app/templates/views/user-profile/suppress-platform-admin.html
new file mode 100644
index 000000000..a579f001c
--- /dev/null
+++ b/app/templates/views/user-profile/suppress-platform-admin.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 %}
+ Suppress platform admin view
+{% endblock %}
+
+{% block maincolumn_content %}
+
+
+
+ {{ page_header(
+ 'Suppress platform admin',
+ 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..f77756e7c 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 'Suppress Platform Admin' not in page
+
+
+def test_overview_page_shows_suppress_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'
+ suppress_platform_admin_row = page.select('tr')[-1]
+ assert ' '.join(suppress_platform_admin_row.text.split()) == 'Suppress Platform Admin Off 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_suppress_platform_admin(client_request):
+ client_request.get('main.user_profile_suppress_platform_admin', _expected_status=403)
+
+
+def test_platform_admin_can_see_suppress_platform_admin_page(client_request, platform_admin_user):
+ client_request.login(platform_admin_user)
+ page = client_request.get('main.user_profile_suppress_platform_admin')
+
+ assert page.select_one('h1').text.strip() == 'Suppress platform admin'
+ assert page.select_one('input[checked]')['value'] == 'False'
+
+
+def test_can_suppress_platform_admin(client_request, platform_admin_user):
+ client_request.login(platform_admin_user)
+
+ with client_request.session_transaction() as session:
+ assert 'suppress_platform_admin' not in session
+
+ client_request.post(
+ 'main.user_profile_suppress_platform_admin',
+ _data={'enabled': True},
+ _expected_status=302,
+ _expected_redirect=url_for('main.user_profile', _external=True),
+ )
+
+ with client_request.session_transaction() as session:
+ assert session['suppress_platform_admin'] is True
+
+
+def test_can_turn_off_suppress_platform_admin(client_request, platform_admin_user):
+ client_request.login(platform_admin_user)
+
+ with client_request.session_transaction() as session:
+ session['suppress_platform_admin'] = True
+
+ client_request.post(
+ 'main.user_profile_suppress_platform_admin',
+ _data={'enabled': False},
+ _expected_status=302,
+ _expected_redirect=url_for('main.user_profile', _external=True),
+ )
+
+ with client_request.session_transaction() as session:
+ assert session['suppress_platform_admin'] is False
diff --git a/tests/app/models/test_user.py b/tests/app/models/test_user.py
index 92fabc0af..bae3189bd 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_checks_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['suppress_platform_admin'] = 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..33efce64a 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,
@@ -1443,7 +1445,7 @@ 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,
@@ -2165,7 +2167,7 @@ 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',