- {{ tick_cross(
- user.has_permission_for_service(current_service.id, 'send_messages'),
- 'Send messages'
- ) }}
- {{ tick_cross(
- user.has_permission_for_service(current_service.id, 'manage_templates'),
- 'Add and edit templates'
- ) }}
- {{ tick_cross(
- user.has_permission_for_service(current_service.id, 'manage_service'),
- 'Manage service'
- ) }}
- {{ tick_cross(
- user.has_permission_for_service(current_service.id, 'manage_api_keys'),
- 'Access API keys'
- ) }}
+ {% if user.has_permission_for_service(current_service.id, 'view_activity') %}
+ {% if 'caseworking' in current_service.permissions %}
+ {{ tick_cross(
+ user.status != 'cancelled',
+ 'Admin'
+ ) }}
+ {% endif %}
+ {{ tick_cross(
+ user.has_permission_for_service(current_service.id, 'send_messages'),
+ 'Send messages'
+ ) }}
+ {{ tick_cross(
+ user.has_permission_for_service(current_service.id, 'manage_templates'),
+ 'Add and edit templates'
+ ) }}
+ {{ tick_cross(
+ user.has_permission_for_service(current_service.id, 'manage_service'),
+ 'Manage service'
+ ) }}
+ {{ tick_cross(
+ user.has_permission_for_service(current_service.id, 'manage_api_keys'),
+ 'Access API keys'
+ ) }}
+ {% else %}
+ {{ tick_cross(
+ True,
+ 'Caseworker'
+ ) }}
+ {% endif %}
{% if 'email_auth' in current_service['permissions'] %}
{% if user.auth_type == 'sms_auth' %}
diff --git a/app/templates/views/manage-users/permissions.html b/app/templates/views/manage-users/permissions.html
index 5d98cd0f2..17b1a2de5 100644
--- a/app/templates/views/manage-users/permissions.html
+++ b/app/templates/views/manage-users/permissions.html
@@ -1,26 +1,58 @@
{% from "components/checkbox.html" import checkbox %}
-{% from "components/radios.html" import radios %}
+{% from "components/radios.html" import radio, radios, radios_wrapper, conditional_radio_panel %}
-
-
-
-
- All team members can see
-
-
- - templates
- - history of sent messages
- - who the other team members are
-
-
+{% if 'caseworking' in current_service.permissions %}
+
+ {% call radios_wrapper(form.user_type, hide_legend=True) %}
+ {% for option in form.user_type %}
+ {{ radio(option) }}
+ {% if option.data == 'admin' %}
+ {% call conditional_radio_panel(option.data) %}
+
+
+ All admin users can see
+
+
+ - templates
+ - history of sent messages
+ - who the other team members are
+
+
+
+ {% endcall %}
+ {% endif %}
+ {% endfor %}
+ {% endcall %}
+
+{% else %}
+
+
+
+ All team members can see
+
+
+ - templates
+ - history of sent messages
+ - who the other team members are
+
+
+{% endif %}
{% if service_has_email_auth %}
{% if user_has_no_mobile_number %}
diff --git a/gulpfile.babel.js b/gulpfile.babel.js
index 11ecd5f27..b1a6ab92e 100644
--- a/gulpfile.babel.js
+++ b/gulpfile.babel.js
@@ -73,6 +73,7 @@ gulp.task('javascripts', () => gulp
paths.src + 'javascripts/errorTracking.js',
paths.src + 'javascripts/preventDuplicateFormSubmissions.js',
paths.src + 'javascripts/fullscreenTable.js',
+ paths.src + 'javascripts/conditionalRadios.js',
paths.src + 'javascripts/main.js'
])
.pipe(plugins.prettyerror())
diff --git a/tests/app/main/views/test_accept_invite.py b/tests/app/main/views/test_accept_invite.py
index 24e486f46..718b00d05 100644
--- a/tests/app/main/views/test_accept_invite.py
+++ b/tests/app/main/views/test_accept_invite.py
@@ -24,7 +24,7 @@ def test_existing_user_accept_invite_calls_api_and_redirects_to_dashboard(
mocker,
):
expected_service = service_one['id']
- expected_permissions = {'send_messages', 'manage_service', 'manage_api_keys'}
+ expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
response = client.get(url_for('main.accept_invite', token='thisisnotarealtoken'))
@@ -123,7 +123,7 @@ def test_existing_signed_out_user_accept_invite_redirects_to_sign_in(
mocker,
):
expected_service = service_one['id']
- expected_permissions = {'send_messages', 'manage_service', 'manage_api_keys'}
+ expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
response = client.get(url_for('main.accept_invite', token='thisisnotarealtoken'), follow_redirects=True)
@@ -401,7 +401,7 @@ def test_new_invited_user_verifies_and_added_to_service(
# when they post codes back to admin user should be added to
# service and sent on to dash board
- expected_permissions = {'send_messages', 'manage_service', 'manage_api_keys'}
+ expected_permissions = {'view_activity', 'send_messages', 'manage_service', 'manage_api_keys'}
with client.session_transaction() as session:
new_user_id = session['user_id']
diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py
index 3b4f29708..fb9c7ceb1 100644
--- a/tests/app/main/views/test_manage_users.py
+++ b/tests/app/main/views/test_manage_users.py
@@ -9,6 +9,7 @@ from app.notify_client.models import InvitedUser
from app.utils import is_gov_user
from tests.conftest import (
SERVICE_ONE_ID,
+ active_caseworking_user,
active_user_manage_template_permission,
active_user_no_mobile,
active_user_view_permissions,
@@ -53,12 +54,24 @@ from tests.conftest import service_one as create_sample_service
'Can’t Send messages Can’t Add and edit templates Can’t Manage service Can’t Access API keys'
)
),
+ (
+ active_user_manage_template_permission,
+ (
+ 'Test User With Permissions (you) '
+ 'Can’t Send messages Can Add and edit templates Can’t Manage service Can’t Access API keys'
+ ),
+ (
+ 'ZZZZZZZZ zzzzzzz@example.gov.uk '
+ 'Can’t Send messages Can’t Add and edit templates Can’t Manage service Can’t Access API keys'
+ )
+ ),
])
def test_should_show_overview_page(
client_request,
mocker,
mock_get_invites_for_service,
fake_uuid,
+ service_one,
user,
expected_self_text,
expected_coworker_text,
@@ -85,6 +98,42 @@ def test_should_show_overview_page(
app.user_api_client.get_users_for_service.assert_called_once_with(service_id=SERVICE_ONE_ID)
+def test_should_show_caseworker_on_overview_page(
+ client_request,
+ mocker,
+ mock_get_invites_for_service,
+ fake_uuid,
+ service_one,
+):
+ service_one['permissions'].append('caseworking')
+ current_user = active_user_view_permissions(active_user_view_permissions)
+ other_user = active_caseworking_user(fake_uuid)
+ other_user.email_address = 'zzzzzzz@example.gov.uk'
+
+ mocker.patch('app.user_api_client.get_user', return_value=current_user)
+ mocker.patch('app.user_api_client.get_users_for_service', return_value=[
+ current_user,
+ other_user,
+ ])
+
+ page = client_request.get('main.manage_users', service_id=SERVICE_ONE_ID)
+
+ assert normalize_spaces(page.select_one('h1').text) == 'Team members'
+ assert normalize_spaces(page.select('.user-list-item')[0].text) == (
+ 'Test User With Permissions (you) '
+ 'Can Admin '
+ 'Can’t Send messages '
+ 'Can’t Add and edit templates '
+ 'Can’t Manage service '
+ 'Can’t Access API keys'
+ )
+ # [1:5] are invited users
+ assert normalize_spaces(page.select('.user-list-item')[6].text) == (
+ 'Test User zzzzzzz@example.gov.uk '
+ 'Can Caseworker'
+ )
+
+
@pytest.mark.parametrize('endpoint, extra_args, service_has_email_auth, auth_options_hidden', [
(
'main.edit_user_permissions',
@@ -125,6 +174,59 @@ def test_service_with_no_email_auth_hides_auth_type_options(
assert (page.find('input', attrs={"name": "login_authentication"}) is None) == auth_options_hidden
+@pytest.mark.parametrize('endpoint, extra_args, service_has_caseworking, radio_buttons_on_page', [
+ (
+ 'main.edit_user_permissions',
+ {'user_id': 0},
+ True,
+ True,
+ ),
+ (
+ 'main.edit_user_permissions',
+ {'user_id': 0},
+ False,
+ False,
+ ),
+ (
+ 'main.invite_user',
+ {},
+ True,
+ True,
+ ),
+ (
+ 'main.invite_user',
+ {},
+ False,
+ False,
+ )
+])
+def test_service_without_caseworking_doesnt_show_admin_vs_caseworker(
+ client_request,
+ endpoint,
+ extra_args,
+ service_has_caseworking,
+ radio_buttons_on_page,
+ service_one
+):
+ if service_has_caseworking:
+ service_one['permissions'].append('caseworking')
+ page = client_request.get(endpoint, service_id=service_one['id'], **extra_args)
+ radio_buttons = page.select('input[name=user_type]')
+ admin_permissions_panel = page.select_one('#panel-admin')
+ if radio_buttons_on_page:
+ assert radio_buttons[0]['type'] == 'radio'
+ assert radio_buttons[0]['value'] == 'caseworker'
+ assert radio_buttons[1]['type'] == 'radio'
+ assert radio_buttons[1]['value'] == 'admin'
+ assert admin_permissions_panel.select('input')[0]['name'] == 'send_messages'
+ assert admin_permissions_panel.select('input')[1]['name'] == 'manage_templates'
+ assert admin_permissions_panel.select('input')[2]['name'] == 'manage_service'
+ assert admin_permissions_panel.select('input')[3]['name'] == 'manage_api_keys'
+ else:
+ assert not radio_buttons
+ assert not admin_permissions_panel
+
+
@pytest.mark.parametrize('service_has_email_auth, displays_auth_type', [
(True, True),
(False, False)
@@ -343,6 +445,85 @@ def test_edit_user_permissions_including_authentication_with_email_auth_service(
)
+def test_edit_user_to_be_admin(
+ client_request,
+ active_user_with_permissions,
+ mocker,
+ mock_get_invites_for_service,
+ mock_set_user_permissions,
+ service_one,
+):
+ service_one['permissions'].append('caseworking')
+ client_request.post(
+ 'main.edit_user_permissions',
+ service_id=SERVICE_ONE_ID,
+ user_id=active_user_with_permissions.id,
+ _data={
+ 'email_address': active_user_with_permissions.email_address,
+ 'user_type': 'admin',
+ 'send_messages': 'y',
+ 'manage_templates': 'y',
+ 'manage_service': 'y',
+ 'manage_api_keys': 'y',
+ },
+ _expected_redirect=url_for(
+ 'main.manage_users', service_id=SERVICE_ONE_ID, _external=True
+ ),
+ )
+ mock_set_user_permissions.assert_called_with(
+ str(active_user_with_permissions.id),
+ SERVICE_ONE_ID,
+ permissions={
+ 'send_messages',
+ 'manage_service',
+ 'manage_templates',
+ 'manage_api_keys',
+ 'view_activity'
+ }
+ )
+
+
+@pytest.mark.parametrize('extra_args', (
+ # The user shouldn’t be able to forge a request which makes a
+ # caseworker without the ‘send’ permission…
+ ({'send_messages': 'n'}),
+ # …or with any additional permissions
+ ({'manage_templates': 'y'}),
+ ({'manage_service': 'y'}),
+ ({'manage_api_keys': 'y'}),
+))
+def test_edit_user_to_be_caseworker(
+ client_request,
+ active_user_with_permissions,
+ mocker,
+ mock_get_invites_for_service,
+ mock_set_user_permissions,
+ service_one,
+ extra_args,
+):
+ service_one['permissions'].append('caseworking')
+ client_request.post(
+ 'main.edit_user_permissions',
+ service_id=SERVICE_ONE_ID,
+ user_id=active_user_with_permissions.id,
+ _data=dict(
+ email_address=active_user_with_permissions.email_address,
+ user_type='caseworker',
+ **extra_args
+ ),
+ _expected_redirect=url_for(
+ 'main.manage_users', service_id=SERVICE_ONE_ID, _external=True
+ ),
+ )
+ mock_set_user_permissions.assert_called_with(
+ str(active_user_with_permissions.id),
+ SERVICE_ONE_ID,
+ permissions={
+ 'send_messages',
+ }
+ )
+
+
def test_should_show_page_for_inviting_user(
logged_in_client,
active_user_with_permissions,
@@ -452,6 +633,41 @@ def test_invite_user_with_email_auth_service(
auth_type)
+@pytest.mark.parametrize('extra_args', (
+ {},
+ {
+ 'send_messages': 'y',
+ 'manage_templates': 'y',
+ 'manage_service': 'y',
+ 'manage_api_keys': 'y',
+ },
+))
+def test_invite_user_must_choose_caseworker_or_admin(
+ client_request,
+ mock_set_user_permissions,
+ service_one,
+ fake_uuid,
+ extra_args,
+):
+ service_one['permissions'].append('caseworking')
+ page = client_request.post(
+ 'main.invite_user',
+ service_id=service_one['id'],
+ user_id=fake_uuid,
+ _data={
+ 'email_address': 'test@example.com',
+ **extra_args
+ },
+ _expected_status=200,
+ )
+ assert page.select_one('.error-message').text.strip() == (
+ 'Not a valid choice'
+ )
+ assert mock_set_user_permissions.called is False
+ for form_input in page.select('form input'):
+ assert 'checked' not in form_input
+
+
def test_cancel_invited_user_cancels_user_invitations(
logged_in_client,
active_user_with_permissions,
@@ -496,7 +712,6 @@ def test_manage_users_shows_invited_user(
):
sample_invite['status'] = invite_status
data = [InvitedUser(**sample_invite)]
-
mocker.patch('app.invite_api_client.get_invites_for_service', return_value=data)
mocker.patch('app.user_api_client.get_users_for_service', return_value=[active_user_with_permissions])
diff --git a/tests/conftest.py b/tests/conftest.py
index 2c457151d..d199499f7 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1161,6 +1161,32 @@ def active_user_with_permissions(fake_uuid):
return user
+@pytest.fixture(scope='function')
+def active_caseworking_user(fake_uuid):
+ from app.notify_client.user_api_client import User
+
+ user_data = {
+ 'id': fake_uuid,
+ 'name': 'Test User',
+ 'password': 'somepassword',
+ 'password_changed_at': str(datetime.utcnow()),
+ 'email_address': 'caseworker@example.gov.uk',
+ 'mobile_number': '07700 900762',
+ 'state': 'active',
+ 'failed_login_count': 0,
+ 'permissions': {SERVICE_ONE_ID: [
+ 'send_texts',
+ 'send_emails',
+ 'send_letters',
+ ]},
+ 'platform_admin': False,
+ 'auth_type': 'sms_auth',
+ 'organisations': [],
+ }
+ user = User(user_data)
+ return user
+
+
@pytest.fixture(scope='function')
def active_user_no_mobile(fake_uuid):
from app.notify_client.user_api_client import User
@@ -2046,7 +2072,7 @@ def sample_invite(mocker, service_one, status='pending'):
from_user = service_one['users'][0]
email_address = 'invited_user@test.gov.uk'
service_id = service_one['id']
- permissions = 'send_messages,manage_service,manage_api_keys'
+ permissions = 'view_activity,send_messages,manage_service,manage_api_keys'
created_at = str(datetime.utcnow())
auth_type = 'sms_auth'