mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-24 00:07:02 -04:00
Stop users from editing/adding templates without the correct permission
If sending SMS is disabled for a service, it should not be possible to add or modify SMS templates. If a user tries to do this, they should see a different page with a link to go back. The same thing should happen with email templates.
This commit is contained in:
@@ -93,6 +93,16 @@ def send_messages(service_id, template_id):
|
|||||||
|
|
||||||
db_template = service_api_client.get_service_template(service_id, template_id)['data']
|
db_template = service_api_client.get_service_template(service_id, template_id)['data']
|
||||||
|
|
||||||
|
if (db_template['template_type'] in ['email', 'sms']) \
|
||||||
|
and (db_template['template_type'] not in current_service['permissions']):
|
||||||
|
return redirect(url_for(
|
||||||
|
'.action_blocked',
|
||||||
|
service_id=service_id,
|
||||||
|
notification_type=db_template['template_type'],
|
||||||
|
return_to='view_template',
|
||||||
|
template_id=template_id
|
||||||
|
))
|
||||||
|
|
||||||
template = get_template(
|
template = get_template(
|
||||||
db_template,
|
db_template,
|
||||||
current_service,
|
current_service,
|
||||||
@@ -163,6 +173,18 @@ def send_test(service_id, template_id):
|
|||||||
session['recipient'] = None
|
session['recipient'] = None
|
||||||
session['placeholders'] = {}
|
session['placeholders'] = {}
|
||||||
session['send_test_letter_page_count'] = None
|
session['send_test_letter_page_count'] = None
|
||||||
|
|
||||||
|
db_template = service_api_client.get_service_template(service_id, template_id)['data']
|
||||||
|
|
||||||
|
if (db_template['template_type'] in ['email', 'sms']) \
|
||||||
|
and (db_template['template_type'] not in current_service['permissions']):
|
||||||
|
return redirect(url_for(
|
||||||
|
'.action_blocked',
|
||||||
|
service_id=service_id,
|
||||||
|
notification_type=db_template['template_type'],
|
||||||
|
return_to='view_template',
|
||||||
|
template_id=template_id))
|
||||||
|
|
||||||
return redirect(url_for(
|
return redirect(url_for(
|
||||||
{
|
{
|
||||||
'main.send_test': '.send_test_step',
|
'main.send_test': '.send_test_step',
|
||||||
|
|||||||
@@ -232,15 +232,42 @@ def add_template_by_type(service_id):
|
|||||||
template_id=blank_letter['data']['id'],
|
template_id=blank_letter['data']['id'],
|
||||||
))
|
))
|
||||||
|
|
||||||
return redirect(url_for(
|
if form.template_type.data in current_service['permissions']:
|
||||||
'.add_service_template',
|
return redirect(url_for(
|
||||||
service_id=service_id,
|
'.add_service_template',
|
||||||
template_type=form.template_type.data,
|
service_id=service_id,
|
||||||
))
|
template_type=form.template_type.data,
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
return redirect(url_for(
|
||||||
|
'.action_blocked',
|
||||||
|
service_id=service_id,
|
||||||
|
notification_type=form.template_type.data,
|
||||||
|
return_to='add_new_template',
|
||||||
|
template_id='0'
|
||||||
|
))
|
||||||
|
|
||||||
return render_template('views/templates/add.html', form=form)
|
return render_template('views/templates/add.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@main.route("/services/<service_id>/templates/action-blocked/<notification_type>/<return_to>/<template_id>")
|
||||||
|
@login_required
|
||||||
|
@user_has_permissions('manage_templates', admin_override=True)
|
||||||
|
def action_blocked(service_id, notification_type, return_to, template_id):
|
||||||
|
if notification_type == 'sms':
|
||||||
|
notification_type = 'text messages'
|
||||||
|
elif notification_type == 'email':
|
||||||
|
notification_type = 'emails'
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'views/templates/action_blocked.html',
|
||||||
|
service_id=service_id,
|
||||||
|
notification_type=notification_type,
|
||||||
|
return_to=return_to,
|
||||||
|
template_id=template_id
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<service_id>/templates/add-<template_type>", methods=['GET', 'POST'])
|
@main.route("/services/<service_id>/templates/add-<template_type>", methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@user_has_permissions('manage_templates', admin_override=True)
|
@user_has_permissions('manage_templates', admin_override=True)
|
||||||
@@ -277,13 +304,21 @@ def add_service_template(service_id, template_type):
|
|||||||
return redirect(
|
return redirect(
|
||||||
url_for('.view_template', service_id=service_id, template_id=new_template['data']['id'])
|
url_for('.view_template', service_id=service_id, template_id=new_template['data']['id'])
|
||||||
)
|
)
|
||||||
|
if (template_type in ['email', 'sms']) and (template_type not in current_service['permissions']):
|
||||||
return render_template(
|
return redirect(url_for(
|
||||||
'views/edit-{}-template.html'.format(template_type),
|
'.action_blocked',
|
||||||
form=form,
|
service_id=service_id,
|
||||||
template_type=template_type,
|
notification_type=template_type,
|
||||||
heading_action='Add'
|
return_to='templates',
|
||||||
)
|
template_id='0'
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
return render_template(
|
||||||
|
'views/edit-{}-template.html'.format(template_type),
|
||||||
|
form=form,
|
||||||
|
template_type=template_type,
|
||||||
|
heading_action='Add',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def abort_403_if_not_admin_user():
|
def abort_403_if_not_admin_user():
|
||||||
@@ -354,13 +389,26 @@ def edit_service_template(service_id, template_id):
|
|||||||
service_id=service_id,
|
service_id=service_id,
|
||||||
template_id=template_id
|
template_id=template_id
|
||||||
))
|
))
|
||||||
return render_template(
|
|
||||||
'views/edit-{}-template.html'.format(template['template_type']),
|
db_template = service_api_client.get_service_template(service_id, template_id)['data']
|
||||||
form=form,
|
|
||||||
template_id=template_id,
|
if (db_template['template_type'] in ['email', 'sms']) \
|
||||||
template_type=template['template_type'],
|
and (db_template['template_type'] not in current_service['permissions']):
|
||||||
heading_action='Edit'
|
return redirect(url_for(
|
||||||
)
|
'.action_blocked',
|
||||||
|
service_id=service_id,
|
||||||
|
notification_type=db_template['template_type'],
|
||||||
|
return_to='view_template',
|
||||||
|
template_id=template_id
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
return render_template(
|
||||||
|
'views/edit-{}-template.html'.format(template['template_type']),
|
||||||
|
form=form,
|
||||||
|
template_id=template_id,
|
||||||
|
template_type=template['template_type'],
|
||||||
|
heading_action='Edit'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@main.route("/services/<service_id>/templates/<template_id>/delete", methods=['GET', 'POST'])
|
@main.route("/services/<service_id>/templates/<template_id>/delete", methods=['GET', 'POST'])
|
||||||
|
|||||||
47
app/templates/views/templates/action_blocked.html
Normal file
47
app/templates/views/templates/action_blocked.html
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{% extends "withnav_template.html" %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
|
||||||
|
{% block service_page_title %}
|
||||||
|
Emails
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block maincolumn_content %}
|
||||||
|
|
||||||
|
<div class="grid-row">
|
||||||
|
<div class="column-five-sixths">
|
||||||
|
<h1 class="heading-large">{{ notification_type.capitalize() }}</h1>
|
||||||
|
<p>
|
||||||
|
Sending {{ notification_type }} is an invitation‑only feature.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If you want to try it out,
|
||||||
|
<a href="{{ url_for('.support') }}">get in touch with the GOV.UK Notify team</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{% set
|
||||||
|
back_link_dict = {
|
||||||
|
'add_new_template': {
|
||||||
|
'url' : '.add_template_by_type', 'text': 'Back to add new template'
|
||||||
|
},
|
||||||
|
'templates': {
|
||||||
|
'url' : '.choose_template', 'text' : 'Back to templates'
|
||||||
|
},
|
||||||
|
'view_template': {
|
||||||
|
'url' :'.view_template', 'text' : 'Back to the template'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
{{ page_footer(
|
||||||
|
back_link=url_for(
|
||||||
|
back_link_dict[return_to]['url'],
|
||||||
|
service_id=current_service.id,
|
||||||
|
template_id=template_id),
|
||||||
|
back_link_text=back_link_dict[return_to]['text']
|
||||||
|
) }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -38,6 +38,32 @@ def test_that_test_files_exist():
|
|||||||
assert len(test_non_spreadsheet_files) == 6
|
assert len(test_non_spreadsheet_files) == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_not_allow_files_to_be_uploaded_without_the_correct_permission(
|
||||||
|
logged_in_client,
|
||||||
|
mock_get_service_template,
|
||||||
|
service_one,
|
||||||
|
fake_uuid,
|
||||||
|
):
|
||||||
|
template_id = fake_uuid
|
||||||
|
service_one['permissions'] = []
|
||||||
|
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'.send_messages',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id),
|
||||||
|
follow_redirects=True)
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert page.select('main p')[0].text.strip() == "Sending text messages is an invitation‑only feature."
|
||||||
|
assert page.select(".page-footer-back-link")[0].text == "Back to the template"
|
||||||
|
assert page.select(".page-footer-back-link")[0]['href'] == url_for(
|
||||||
|
'.view_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"filename, acceptable_file",
|
"filename, acceptable_file",
|
||||||
list(zip(
|
list(zip(
|
||||||
@@ -310,6 +336,32 @@ def test_send_test_step_redirects_if_session_not_setup(
|
|||||||
assert session['recipient'] == expected_recipient
|
assert session['recipient'] == expected_recipient
|
||||||
|
|
||||||
|
|
||||||
|
def test_send_one_off_does_not_send_without_the_correct_permissions(
|
||||||
|
logged_in_client,
|
||||||
|
mock_get_service_template,
|
||||||
|
service_one,
|
||||||
|
fake_uuid,
|
||||||
|
):
|
||||||
|
template_id = fake_uuid
|
||||||
|
service_one['permissions'] = []
|
||||||
|
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'.send_one_off',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id),
|
||||||
|
follow_redirects=True)
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert page.select('main p')[0].text.strip() == "Sending text messages is an invitation‑only feature."
|
||||||
|
assert page.select(".page-footer-back-link")[0].text == "Back to the template"
|
||||||
|
assert page.select(".page-footer-back-link")[0]['href'] == url_for(
|
||||||
|
'.view_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('template_mock, partial_url, expected_h1, tour_shown', [
|
@pytest.mark.parametrize('template_mock, partial_url, expected_h1, tour_shown', [
|
||||||
(
|
(
|
||||||
mock_get_service_template_with_placeholders,
|
mock_get_service_template_with_placeholders,
|
||||||
@@ -542,11 +594,15 @@ def test_send_test_redirects_to_start_if_index_out_of_bounds_and_some_placeholde
|
|||||||
])
|
])
|
||||||
def _redirects_with_help_argument(
|
def _redirects_with_help_argument(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
|
mocker,
|
||||||
service_one,
|
service_one,
|
||||||
fake_uuid,
|
fake_uuid,
|
||||||
endpoint,
|
endpoint,
|
||||||
expected_redirect,
|
expected_redirect,
|
||||||
):
|
):
|
||||||
|
template = {'data': {'template_type': 'sms'}}
|
||||||
|
mocker.patch('app.service_api_client.get_service_template', return_value=template)
|
||||||
|
|
||||||
response = logged_in_client.get(
|
response = logged_in_client.get(
|
||||||
url_for(endpoint, service_id=service_one['id'], template_id=fake_uuid, help=1)
|
url_for(endpoint, service_id=service_one['id'], template_id=fake_uuid, help=1)
|
||||||
)
|
)
|
||||||
@@ -848,6 +904,8 @@ def test_send_test_clears_session(
|
|||||||
service_one,
|
service_one,
|
||||||
fake_uuid,
|
fake_uuid,
|
||||||
):
|
):
|
||||||
|
template = {'data': {'template_type': 'sms'}}
|
||||||
|
mocker.patch('app.service_api_client.get_service_template', return_value=template)
|
||||||
|
|
||||||
with logged_in_client.session_transaction() as session:
|
with logged_in_client.session_transaction() as session:
|
||||||
session['recipient'] = '07700900001'
|
session['recipient'] = '07700900001'
|
||||||
|
|||||||
@@ -266,6 +266,64 @@ def test_dont_show_preview_letter_templates_for_bad_filetype(
|
|||||||
assert mock_get_service_template.called is False
|
assert mock_get_service_template.called is False
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('type_of_template', ['email', 'sms'])
|
||||||
|
def test_should_not_allow_creation_of_template_through_form_without_correct_permission(
|
||||||
|
logged_in_client,
|
||||||
|
service_one,
|
||||||
|
mocker,
|
||||||
|
type_of_template,
|
||||||
|
):
|
||||||
|
service_one['permissions'] = []
|
||||||
|
template_description = {'sms': 'text messages', 'email': 'emails'}
|
||||||
|
|
||||||
|
response = logged_in_client.post(url_for(
|
||||||
|
'.add_template_by_type',
|
||||||
|
service_id=service_one['id']),
|
||||||
|
data={'template_type': type_of_template},
|
||||||
|
follow_redirects=True)
|
||||||
|
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert page.select('main p')[0].text.strip() == \
|
||||||
|
"Sending {} is an invitation‑only feature.".format(template_description[type_of_template])
|
||||||
|
assert page.select(".page-footer-back-link")[0].text == "Back to add new template"
|
||||||
|
assert page.select(".page-footer-back-link")[0]['href'] == url_for(
|
||||||
|
'.add_template_by_type',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id='0',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('type_of_template', ['email', 'sms'])
|
||||||
|
def test_should_not_allow_creation_of_a_template_without_correct_permission(
|
||||||
|
logged_in_client,
|
||||||
|
service_one,
|
||||||
|
mocker,
|
||||||
|
type_of_template,
|
||||||
|
):
|
||||||
|
service_one['permissions'] = []
|
||||||
|
template_description = {'sms': 'text messages', 'email': 'emails'}
|
||||||
|
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'.add_service_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_type=type_of_template),
|
||||||
|
follow_redirects=True)
|
||||||
|
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert page.select('main p')[0].text.strip() == \
|
||||||
|
"Sending {} is an invitation‑only feature.".format(template_description[type_of_template])
|
||||||
|
assert page.select(".page-footer-back-link")[0].text == "Back to templates"
|
||||||
|
assert page.select(".page-footer-back-link")[0]['href'] == url_for(
|
||||||
|
'.choose_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id='0',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_should_redirect_when_saving_a_template(
|
def test_should_redirect_when_saving_a_template(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
active_user_with_permissions,
|
active_user_with_permissions,
|
||||||
@@ -336,6 +394,32 @@ def test_should_edit_content_when_process_type_is_priority_not_platform_admin(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_should_not_allow_template_edits_without_correct_permission(
|
||||||
|
logged_in_client,
|
||||||
|
mock_get_service_template,
|
||||||
|
service_one,
|
||||||
|
fake_uuid,
|
||||||
|
):
|
||||||
|
template_id = fake_uuid
|
||||||
|
service_one['permissions'] = ['email']
|
||||||
|
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'.edit_service_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id),
|
||||||
|
follow_redirects=True)
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert page.select('main p')[0].text.strip() == "Sending text messages is an invitation‑only feature."
|
||||||
|
assert page.select(".page-footer-back-link")[0].text == "Back to the template"
|
||||||
|
assert page.select(".page-footer-back-link")[0]['href'] == url_for(
|
||||||
|
'.view_template',
|
||||||
|
service_id=service_one['id'],
|
||||||
|
template_id=template_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_should_403_when_edit_template_with_process_type_of_priority_for_non_platform_admin(
|
def test_should_403_when_edit_template_with_process_type_of_priority_for_non_platform_admin(
|
||||||
client,
|
client,
|
||||||
active_user_with_permissions,
|
active_user_with_permissions,
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ def mock_get_service(mocker, api_user_active):
|
|||||||
@pytest.fixture(scope='function')
|
@pytest.fixture(scope='function')
|
||||||
def mock_get_international_service(mocker, api_user_active):
|
def mock_get_international_service(mocker, api_user_active):
|
||||||
def _get(service_id):
|
def _get(service_id):
|
||||||
service = service_json(service_id, users=[api_user_active.id], permissions=['international_sms'])
|
service = service_json(service_id, users=[api_user_active.id], permissions=['sms', 'international_sms'])
|
||||||
return {'data': service}
|
return {'data': service}
|
||||||
|
|
||||||
return mocker.patch('app.service_api_client.get_service', side_effect=_get)
|
return mocker.patch('app.service_api_client.get_service', side_effect=_get)
|
||||||
|
|||||||
Reference in New Issue
Block a user