diff --git a/app/__init__.py b/app/__init__.py index 813aa3e41..d17998fc6 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -13,7 +13,7 @@ from pygments.lexers import JavascriptLexer from pygments.formatters import HtmlFormatter from werkzeug.exceptions import abort -from app.notify_client.api_client import NotificationsAdminAPIClient +from app.notify_client.api_client import ServiceAPIClient from app.notify_client.api_key_api_client import ApiKeyApiClient from app.notify_client.user_api_client import UserApiClient from app.notify_client.job_api_client import JobApiClient @@ -25,12 +25,13 @@ from app.its_dangerous_session import ItsdangerousSessionInterface from app.asset_fingerprinter import AssetFingerprinter from utils.recipients import validate_phone_number, InvalidPhoneError import app.proxy_fix +from config import configs from utils import logging login_manager = LoginManager() csrf = CsrfProtect() -notifications_api_client = NotificationsAdminAPIClient() +service_api_client = ServiceAPIClient() user_api_client = UserApiClient() api_key_api_client = ApiKeyApiClient() job_api_client = JobApiClient() @@ -50,7 +51,7 @@ def create_app(): logging.init_app(application) init_csrf(application) - notifications_api_client.init_app(application) + service_api_client.init_app(application) user_api_client.init_app(application) api_key_api_client.init_app(application) job_api_client.init_app(application) diff --git a/app/main/__init__.py b/app/main/__init__.py index 39f95b71d..3a0af5a11 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -22,5 +22,6 @@ from app.main.views import ( choose_service, api_keys, manage_users, - invites + invites, + all_services ) diff --git a/app/main/dao/services_dao.py b/app/main/dao/services_dao.py index 39417fceb..69b8946b1 100644 --- a/app/main/dao/services_dao.py +++ b/app/main/dao/services_dao.py @@ -1,10 +1,10 @@ from flask import url_for, current_app -from app import notifications_api_client +from app import service_api_client from app.utils import BrowsableItem def update_service(service): - return notifications_api_client.update_service( + return service_api_client.update_service( service['id'], service['name'], service['active'], @@ -14,24 +14,24 @@ def update_service(service): def get_service_by_id(id_): - return notifications_api_client.get_service(id_) + return service_api_client.get_service(id_) def get_service_by_id_or_404(id_): - return notifications_api_client.get_service(id_)['data'] + return service_api_client.get_service(id_)['data'] def get_services(user_id=None): if user_id: - return notifications_api_client.get_services({'user_id': str(user_id)}) + return service_api_client.get_services({'user_id': str(user_id)}) else: - return notifications_api_client.get_services() + return service_api_client.get_services() def unrestrict_service(service_id): - resp = notifications_api_client.get_service(service_id) + resp = service_api_client.get_service(service_id) if resp['data']['restricted']: - resp = notifications_api_client.update_service( + resp = service_api_client.update_service( service_id, resp['data']['name'], resp['data']['active'], @@ -41,9 +41,9 @@ def unrestrict_service(service_id): def activate_service(service_id): - resp = notifications_api_client.get_service(service_id) + resp = service_api_client.get_service(service_id) if not resp['data']['active']: - resp = notifications_api_client.update_service( + resp = service_api_client.update_service( service_id, resp['data']['name'], True, @@ -54,7 +54,7 @@ def activate_service(service_id): # TODO Fix when functionality is added to the api. def find_service_by_service_name(service_name, user_id=None): - resp = notifications_api_client.get_services(user_id) + resp = service_api_client.get_services(user_id) retval = None for srv_json in resp['data']: if srv_json['name'] == service_name: @@ -64,11 +64,11 @@ def find_service_by_service_name(service_name, user_id=None): def delete_service(id_): - return notifications_api_client.delete_service(id_) + return service_api_client.delete_service(id_) def find_all_service_names(user_id=None): - resp = notifications_api_client.get_services(user_id) + resp = service_api_client.get_services(user_id) return [x['name'] for x in resp['data']] diff --git a/app/main/dao/templates_dao.py b/app/main/dao/templates_dao.py index c6a22ccf2..63e5466bf 100644 --- a/app/main/dao/templates_dao.py +++ b/app/main/dao/templates_dao.py @@ -1,28 +1,28 @@ from flask import url_for -from app import notifications_api_client +from app import service_api_client from app.utils import BrowsableItem def insert_service_template(name, type_, content, service_id, subject=None): - return notifications_api_client.create_service_template( + return service_api_client.create_service_template( name, type_, content, service_id, subject) def update_service_template(id_, name, type_, content, service_id, subject=None): - return notifications_api_client.update_service_template( + return service_api_client.update_service_template( id_, name, type_, content, service_id) def get_service_templates(service_id): - return notifications_api_client.get_service_templates(service_id) + return service_api_client.get_service_templates(service_id) def get_service_template_or_404(service_id, template_id): - return notifications_api_client.get_service_template(service_id, template_id) + return service_api_client.get_service_template(service_id, template_id) def delete_service_template(service_id, template_id): - return notifications_api_client.delete_service_template( + return service_api_client.delete_service_template( service_id, template_id) diff --git a/app/main/views/add_service.py b/app/main/views/add_service.py index f1ef77ef7..9a30f7782 100644 --- a/app/main/views/add_service.py +++ b/app/main/views/add_service.py @@ -15,7 +15,7 @@ from app.notify_client.models import InvitedUser from app import ( invite_api_client, user_api_client, - notifications_api_client + service_api_client ) @@ -36,7 +36,7 @@ def add_service(): heading = 'Which service do you want to set up notifications for?' if form.validate_on_submit(): session['service_name'] = form.name.data - service_id = notifications_api_client.create_service( + service_id = service_api_client.create_service( session['service_name'], False, current_app.config['DEFAULT_SERVICE_LIMIT'], True, session['user_id']) return redirect(url_for('main.service_dashboard', service_id=service_id)) diff --git a/app/main/views/all_services.py b/app/main/views/all_services.py new file mode 100644 index 000000000..0efd1171d --- /dev/null +++ b/app/main/views/all_services.py @@ -0,0 +1,15 @@ +from flask import render_template +from flask_login import login_required + +from app import service_api_client +from app.main import main +from app.main.dao import services_dao +from app.utils import user_has_permissions + + +@main.route("/all-services") +@login_required +@user_has_permissions(None, admin_override=True) +def show_all_services(): + services = [services_dao.ServicesBrowsableItem(x) for x in service_api_client.get_services()['data']] + return render_template('views/all-services.html', services=services) diff --git a/app/main/views/manage_users.py b/app/main/views/manage_users.py index e42fbb9f5..cb77fb625 100644 --- a/app/main/views/manage_users.py +++ b/app/main/views/manage_users.py @@ -24,7 +24,7 @@ from app.utils import user_has_permissions @main.route("/services//users") @login_required -@user_has_permissions('manage_users', 'manage_templates', 'manage_settings') +@user_has_permissions('manage_users', admin_override=True) def manage_users(service_id): users = user_api_client.get_users_for_service(service_id=service_id) invited_users = invite_api_client.get_invites_for_service(service_id=service_id) @@ -41,9 +41,8 @@ def manage_users(service_id): @main.route("/services//users/invite", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_users', 'manage_templates', 'manage_settings') +@user_has_permissions('manage_users', admin_override=True) def invite_user(service_id): - service = get_service_by_id(service_id) form = InviteUserForm(current_user.email_address) @@ -51,6 +50,7 @@ def invite_user(service_id): email_address = form.email_address.data permissions = _get_permissions(request.form) invited_user = invite_api_client.create_invite(current_user.id, service_id, email_address, permissions) + flash('Invite sent to {}'.format(invited_user.email_address), 'default_with_tick') return redirect(url_for('.manage_users', service_id=service_id)) @@ -64,7 +64,7 @@ def invite_user(service_id): @main.route("/services//users/", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_users', 'manage_templates', 'manage_settings') +@user_has_permissions('manage_users', admin_override=True) def edit_user_permissions(service_id, user_id): # TODO we should probably using the service id here in the get user # call as well. eg. /user/?&service_id=service_id @@ -74,11 +74,11 @@ def edit_user_permissions(service_id, user_id): # Do it through the template or the form class? form = PermisisonsForm(**{ 'send_messages': 'yes' if user.has_permissions( - ['send_texts', 'send_emails', 'send_letters']) else 'no', + permissions=['send_texts', 'send_emails', 'send_letters']) else 'no', 'manage_service': 'yes' if user.has_permissions( - ['manage_users', 'manage_templates', 'manage_settings']) else 'no', + permissions=['manage_users', 'manage_templates', 'manage_settings']) else 'no', 'manage_api_keys': 'yes' if user.has_permissions( - ['manage_api_keys', 'access_developer_docs']) else 'no' + permissions=['manage_api_keys', 'access_developer_docs']) else 'no' }) if form.validate_on_submit(): @@ -101,7 +101,7 @@ def edit_user_permissions(service_id, user_id): @main.route("/services//cancel-invited-user/", methods=['GET']) -@user_has_permissions('manage_users', 'manage_templates', 'manage_settings') +@user_has_permissions('manage_users', admin_override=True) def cancel_invited_user(service_id, invited_user_id): invite_api_client.cancel_invited_user(service_id=service_id, invited_user_id=invited_user_id) diff --git a/app/main/views/send.py b/app/main/views/send.py index 41face291..76b78e69f 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -66,7 +66,8 @@ def get_page_headings(template_type): @main.route("/services//send/", methods=['GET']) @login_required -@user_has_permissions('send_texts', 'send_emails', 'send_letters', 'manage_templates', 'manage_api_keys', or_=True) +@user_has_permissions('send_texts', 'send_emails', 'send_letters', 'manage_templates', 'manage_api_keys', + admin_override=True, or_=True) def choose_template(service_id, template_type): service = services_dao.get_service_by_id_or_404(service_id) diff --git a/app/main/views/service_settings.py b/app/main/views/service_settings.py index 6c7232e77..f596ee1dd 100644 --- a/app/main/views/service_settings.py +++ b/app/main/views/service_settings.py @@ -3,7 +3,6 @@ from flask import ( redirect, request, url_for, - abort, session, flash ) @@ -29,7 +28,7 @@ from app.main.forms import ConfirmPasswordForm, ServiceNameForm @main.route("/services//service-settings") @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_settings(service_id): service = get_service_by_id(service_id)['data'] @@ -42,7 +41,7 @@ def service_settings(service_id): @main.route("/services//service-settings/name", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_name_change(service_id): service = get_service_by_id(service_id)['data'] @@ -61,7 +60,7 @@ def service_name_change(service_id): @main.route("/services//service-settings/name/confirm", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_name_change_confirm(service_id): service = get_service_by_id(service_id)['data'] @@ -95,7 +94,7 @@ def service_name_change_confirm(service_id): @main.route("/services//service-settings/request-to-go-live", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_request_to_go_live(service_id): service = get_service_by_id(service_id)['data'] if request.method == 'GET': @@ -112,7 +111,7 @@ def service_request_to_go_live(service_id): @main.route("/services//service-settings/status", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_status_change(service_id): service = get_service_by_id(service_id)['data'] @@ -128,7 +127,7 @@ def service_status_change(service_id): @main.route("/services//service-settings/status/confirm", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_status_change_confirm(service_id): service = get_service_by_id(service_id)['data'] @@ -151,7 +150,7 @@ def service_status_change_confirm(service_id): @main.route("/services//service-settings/delete", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_delete(service_id): service = get_service_by_id(service_id)['data'] @@ -167,7 +166,7 @@ def service_delete(service_id): @main.route("/services//service-settings/delete/confirm", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_settings') +@user_has_permissions('manage_settings', admin_override=True) def service_delete_confirm(service_id): service = get_service_by_id(service_id)['data'] diff --git a/app/main/views/templates.py b/app/main/views/templates.py index f390ff9af..7e34cc79f 100644 --- a/app/main/views/templates.py +++ b/app/main/views/templates.py @@ -21,7 +21,7 @@ page_headings = { @main.route("/services//templates/add-", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_templates') +@user_has_permissions('manage_templates', admin_override=True) def add_service_template(service_id, template_type): service = sdao.get_service_by_id_or_404(service_id) @@ -54,7 +54,7 @@ def add_service_template(service_id, template_type): @main.route("/services//templates/", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_templates') +@user_has_permissions('manage_templates', admin_override=True) def edit_service_template(service_id, template_id): template = tdao.get_service_template_or_404(service_id, template_id)['data'] template['template_content'] = template['content'] @@ -83,7 +83,7 @@ def edit_service_template(service_id, template_id): @main.route("/services//templates//delete", methods=['GET', 'POST']) @login_required -@user_has_permissions('manage_templates') +@user_has_permissions('manage_templates', admin_override=True) def delete_service_template(service_id, template_id): template = tdao.get_service_template_or_404(service_id, template_id)['data'] diff --git a/app/main/views/two_factor.py b/app/main/views/two_factor.py index bc6a2e945..063819b2d 100644 --- a/app/main/views/two_factor.py +++ b/app/main/views/two_factor.py @@ -7,8 +7,7 @@ from flask import ( request ) -from flask_login import login_user - +from flask_login import login_user, current_user from app.main import main from app.main.dao import users_dao, services_dao from app.main.forms import TwoFactorForm @@ -43,6 +42,8 @@ def two_factor(): if next_url and _is_safe_redirect_url(next_url): return redirect(next_url) + if current_user.platform_admin: + return redirect(url_for('main.show_all_services')) if len(services) == 1: return redirect(url_for('main.service_dashboard', service_id=services[0]['id'])) else: diff --git a/app/notify_client/api_client.py b/app/notify_client/api_client.py index dbe66d2d4..6f703e622 100644 --- a/app/notify_client/api_client.py +++ b/app/notify_client/api_client.py @@ -2,14 +2,14 @@ from __future__ import unicode_literals from notifications_python_client.notifications import NotificationsAPIClient -class NotificationsAdminAPIClient(NotificationsAPIClient): +class ServiceAPIClient(NotificationsAPIClient): # Fudge assert in the super __init__ so # we can set those variables later. def __init__(self): - super(NotificationsAdminAPIClient, self).__init__("api_url", - "client", - "secret") + super(ServiceAPIClient, self).__init__("api_url", + "client", + "secret") def init_app(self, application): self.base_url = application.config['API_HOST_NAME'] @@ -128,21 +128,3 @@ class NotificationsAdminAPIClient(NotificationsAPIClient): """ endpoint = "/service/{0}/template/{1}".format(service_id, template_id) return self.delete(endpoint) - - # The implementation of these will change after the notifications-api - # functionality updates to include the ability to send notifications. - def send_sms(self, - mobile_number, - message, - job_id=None, - description=None): - self.send_sms_notification(mobile_number, message) - - def send_email(self, - email_address, - message, - from_address, - subject, - job_id=None, - description=None): - self.send_email_notification(email_address, message, from_address, subject) diff --git a/app/notify_client/models.py b/app/notify_client/models.py index 3c27c09e4..12664fb1f 100644 --- a/app/notify_client/models.py +++ b/app/notify_client/models.py @@ -1,5 +1,4 @@ from flask.ext.login import (UserMixin, login_fresh) -from flask import session class User(UserMixin): @@ -13,6 +12,7 @@ class User(UserMixin): self._failed_login_count = fields.get('failed_login_count') self._state = fields.get('state') self.max_failed_login_count = max_failed_login_count + self.platform_admin = fields.get('platform_admin') def get_id(self): return self.id @@ -82,9 +82,18 @@ class User(UserMixin): def permissions(self, permissions): raise AttributeError("Read only property") - def has_permissions(self, permissions, service_id=None, or_=False): - if service_id is None: - service_id = session.get('service_id', '') + def has_permissions(self, permissions=[], or_=False, admin_override=False): + # Only available to the platform admin user + if admin_override and self.platform_admin: + return True + # Not available to the non platform admin users. + # For example the list all-services page is only available to platform admin users and is not service specific + if admin_override and not permissions: + return False + + from flask import request + # Service id is always set on the request for service specific views. + service_id = request.view_args.get('service_id', None) if service_id in self._permissions: if or_: return any([x in self._permissions[service_id] for x in permissions]) diff --git a/app/templates/main_nav.html b/app/templates/main_nav.html index 03b0e87fc..f7aa3aed9 100644 --- a/app/templates/main_nav.html +++ b/app/templates/main_nav.html @@ -7,13 +7,13 @@
  • Send text messages
  • Send emails
  • - {% elif current_user.has_permissions(['manage_templates', 'manage_api_keys'], or_=True) %} + {% elif current_user.has_permissions(['manage_templates','manage_api_keys'], admin_override=True, or_=True) %} {% endif %} - {% if current_user.has_permissions(['manage_users', 'manage_settings']) %} + {% if current_user.has_permissions(['manage_users', 'manage_settings'], admin_override=True) %} {% endif %} + + {% if current_user.has_permissions(admin_override=True) %} + + {% endif %} diff --git a/app/templates/views/all-services.html b/app/templates/views/all-services.html new file mode 100644 index 000000000..c278c8c5b --- /dev/null +++ b/app/templates/views/all-services.html @@ -0,0 +1,16 @@ +{% extends "withoutnav_template.html" %} +{% from "components/browse-list.html" import browse_list %} + +{% block page_title %} + All service – GOV.UK Notify +{% endblock %} + +{% block maincolumn_content %} + +

    + All services +

    + + {{ browse_list(services) }} + +{% endblock %} \ No newline at end of file diff --git a/app/templates/views/choose-service.html b/app/templates/views/choose-service.html index 38fe8efc2..d99e888e1 100644 --- a/app/templates/views/choose-service.html +++ b/app/templates/views/choose-service.html @@ -10,6 +10,14 @@

    Choose service

    + {% if current_user.has_permissions(admin_override=True) %} + {{ browse_list([ + { + 'title': 'List all services', + 'link': url_for('.show_all_services') + } + ]) }} + {% endif %} {{ browse_list(services) }} {{ browse_list([ @@ -19,4 +27,5 @@ }, ]) }} + {% endblock %} diff --git a/app/templates/views/choose-template.html b/app/templates/views/choose-template.html index e4c58e947..9cb126643 100644 --- a/app/templates/views/choose-template.html +++ b/app/templates/views/choose-template.html @@ -14,7 +14,7 @@

    {{ page_heading }}

    - {% if current_user.has_permissions(['manage_templates']) %} + {% if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) %} Add a new template {% else %}

    You need to ask your service manager to add templates before you can send messages

    @@ -26,7 +26,7 @@

    {{ page_heading }}

    - {% if current_user.has_permissions(['manage_templates']) %} + {% if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) %} @@ -34,7 +34,7 @@ {% if not has_jobs %} - {% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters'], or_=True) %} + {% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'], or_=True) %} {{ banner( """ Send yourself a test @@ -54,7 +54,7 @@ name=template.name, edit_link=( url_for(".edit_service_template", service_id=service_id, template_id=template.id) - if current_user.has_permissions(['manage_templates']) else + if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) else None ) ) }} @@ -64,7 +64,7 @@ name=template.name, edit_link=( url_for(".edit_service_template", service_id=service_id, template_id=template.id) - if current_user.has_permissions(['manage_templates']) else + if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) else None ) ) }} @@ -72,11 +72,11 @@
    diff --git a/app/templates/views/manage-users.html b/app/templates/views/manage-users.html index e0f7fe1a5..5334b1b43 100644 --- a/app/templates/views/manage-users.html +++ b/app/templates/views/manage-users.html @@ -33,9 +33,9 @@ Manage users – GOV.UK Notify {% call field() %} {{ item.name }} {% endcall %} - {{ boolean_field(item.has_permissions(['send_texts', 'send_emails', 'send_letters'], service_id=service_id)) }} - {{ boolean_field(item.has_permissions(['manage_users', 'manage_templates', 'manage_settings'], service_id=service_id)) }} - {{ boolean_field(item.has_permissions(['manage_api_keys', 'access_developer_docs'], service_id=service_id)) }} + {{ boolean_field(item.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'])) }} + {{ boolean_field(item.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings'])) }} + {{ boolean_field(item.has_permissions(permissions=['manage_api_keys', 'access_developer_docs'])) }} {% call field(align='right') %} {% if current_user.id != item.id %} Edit permission @@ -50,9 +50,9 @@ Manage users – GOV.UK Notify {% call field() %} {{ item.email_address }} {% endcall %} - {{ boolean_field(item.has_permissions(['send_texts', 'send_emails', 'send_letters'])) }} - {{ boolean_field(item.has_permissions(['manage_users', 'manage_templates', 'manage_settings'])) }} - {{ boolean_field(item.has_permissions(['manage_api_keys', 'access_developer_docs'])) }} + {{ boolean_field(item.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'])) }} + {{ boolean_field(item.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings'])) }} + {{ boolean_field(item.has_permissions(permissions=['manage_api_keys', 'access_developer_docs'])) }} {% if item.status == 'pending' %} {% call field(align='right') %} Cancel invitation diff --git a/app/templates/views/service_dashboard.html b/app/templates/views/service_dashboard.html index d21a0d991..1c235dc91 100644 --- a/app/templates/views/service_dashboard.html +++ b/app/templates/views/service_dashboard.html @@ -27,12 +27,12 @@ {% if not template_count and not jobs %} {% call banner_wrapper(subhead='Get started', type="tip") %}
      - {% if current_user.has_permissions(['manage_templates']) %} + {% if current_user.has_permissions(permissions=['manage_templates'], admin_override=True) %}
    1. Add a template
    2. {% endif %} - {% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %} + {% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']) %}
    3. Send yourself a text message
    4. @@ -41,7 +41,7 @@ {% endcall %} {% elif not jobs %} {% call banner_wrapper(subhead='Next step', type="tip") %} - {% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %} + {% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']) %} Send yourself a text message {% endif %} {% endcall %} @@ -63,7 +63,7 @@ {% endcall %} {% endcall %} {% if more_jobs_to_show %} - {% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %} + {% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']) %} diff --git a/app/utils.py b/app/utils.py index 6bf79718f..b8c023744 100644 --- a/app/utils.py +++ b/app/utils.py @@ -30,12 +30,13 @@ class BrowsableItem(object): pass -def user_has_permissions(*permissions, or_=False): +def user_has_permissions(*permissions, admin_override=False, or_=False): def wrap(func): @wraps(func) def wrap_func(*args, **kwargs): from flask_login import current_user - if current_user and current_user.has_permissions(permissions, or_=or_): + if current_user and current_user.has_permissions(permissions=permissions, + admin_override=admin_override, or_=or_): return func(*args, **kwargs) else: abort(403) diff --git a/tests/__init__.py b/tests/__init__.py index 609314462..8d250a4c5 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,21 +1,20 @@ import pytest from flask.testing import FlaskClient from flask import url_for +from flask_login import login_user class TestClient(FlaskClient): def login(self, user): # Skipping authentication here and just log them in with self.session_transaction() as session: - session['user_details'] = { - "email": user.email_address, - "id": user.id} - # Include mock_login fixture in test for this to work. - # TODO would be better for it to be mocked in this - # function - response = self.post( - url_for('main.two_factor'), data={'sms_code': '12345'}) - assert response.status_code == 302 + session['user_id'] = user.id + session['_fresh'] = True + + login_user(user, remember=True) + + def login_fresh(self): + return True def logout(self, user): self.get(url_for("main.logout")) @@ -168,18 +167,16 @@ def validate_route_permission(mocker, 'app.user_api_client.check_verify_code', return_value=(True, '')) mocker.patch( - 'app.notifications_api_client.get_services', + 'app.service_api_client.get_services', return_value={'data': []}) mocker.patch('app.user_api_client.get_user', return_value=usr) mocker.patch('app.user_api_client.get_user_by_email', return_value=usr) - mocker.patch('app.notifications_api_client.get_service', return_value={'data': service}) - + mocker.patch('app.service_api_client.get_service', return_value={'data': service}) + mocker.patch('app.user_api_client.get_users_for_service', return_value=[usr]) with app_.test_request_context(): with app_.test_client() as client: client.login(usr) resp = None - with client.session_transaction() as session: - session['service_id'] = str(service['id']) if method == 'GET': resp = client.get(route) elif method == 'POST': diff --git a/tests/app/main/test_permissions.py b/tests/app/main/test_permissions.py index 62c5b5058..30a00e336 100644 --- a/tests/app/main/test_permissions.py +++ b/tests/app/main/test_permissions.py @@ -1,16 +1,16 @@ import pytest -from flask import url_for - from app.utils import user_has_permissions from app.main.views.index import index from werkzeug.exceptions import Forbidden +from flask import request -def _test_permissions(app_, usr, permissions, will_succeed, or_=False): - with app_.test_request_context(): +def _test_permissions(app_, usr, permissions, service_id, will_succeed, or_=False, admin_override=False): + with app_.test_request_context() as ctx: + request.view_args.update({'service_id': service_id}) with app_.test_client() as client: client.login(usr) - decorator = user_has_permissions(*permissions, or_=or_) + decorator = user_has_permissions(*permissions, or_=or_, admin_override=admin_override) decorated_index = decorator(index) if will_succeed: response = decorated_index() @@ -22,57 +22,103 @@ def _test_permissions(app_, usr, permissions, will_succeed, or_=False): pass -def test_user_has_permissions_on_endpoint_fail(app_, - api_user_active, - mock_login, - mock_get_user_with_permissions): +def test_user_has_permissions_on_endpoint_fail(app_, mocker): + user = _user_with_permissions() + mocker.patch('app.user_api_client.get_user', return_value=user) _test_permissions( app_, - api_user_active, + user, ['something'], + '', False) def test_user_has_permissions_success(app_, - api_user_active, - mock_login, - mock_get_user_with_permissions): + mocker): + user = _user_with_permissions() + mocker.patch('app.user_api_client.get_user', return_value=user) _test_permissions( app_, - api_user_active, + user, ['manage_users'], + '', True) -def test_user_has_permissions_or(app_, - api_user_active, - mock_login, - mock_get_user_with_permissions): +def test_user_has_permissions_or(app_, mocker): + user = _user_with_permissions() + mocker.patch('app.user_api_client.get_user', return_value=user) _test_permissions( app_, - api_user_active, + user, ['something', 'manage_users'], + '', True, or_=True) def test_user_has_permissions_multiple(app_, - api_user_active, - mock_login, - mock_get_user_with_permissions): + mocker): + user = _user_with_permissions() + mocker.patch('app.user_api_client.get_user', return_value=user) _test_permissions( app_, - api_user_active, + user, ['manage_templates', 'manage_users'], - True) + '', + will_succeed=True) def test_exact_permissions(app_, - api_user_active, - mock_login, - mock_get_user_with_permissions): + mocker): + user = _user_with_permissions() + mocker.patch('app.user_api_client.get_user', return_value=user) _test_permissions( app_, - api_user_active, + user, ['manage_users', 'manage_templates', 'manage_settings'], + '', True) + + +def test_platform_admin_user_can_access_page(app_, + platform_admin_user, + mocker): + mocker.patch('app.user_api_client.get_user', return_value=platform_admin_user) + _test_permissions( + app_, + platform_admin_user, + [], + '', + will_succeed=True, + admin_override=True) + + +def test_platform_admin_user_can_not_access_page(app_, + platform_admin_user, + mocker): + mocker.patch('app.user_api_client.get_user', return_value=platform_admin_user) + _test_permissions( + app_, + platform_admin_user, + [], + '', + will_succeed=False, + admin_override=False) + + +def _user_with_permissions(): + from app.notify_client.user_api_client import User + + user_data = {'id': 999, + 'name': 'Test User', + 'password': 'somepassword', + 'email_address': 'test@user.gov.uk', + 'mobile_number': '+4412341234', + 'state': 'active', + 'failed_login_count': 0, + 'permissions': {'': ['manage_users', 'manage_templates', 'manage_settings']}, + 'platform_admin': False + } + user = User(user_data) + return user diff --git a/tests/app/main/views/test_all_services.py b/tests/app/main/views/test_all_services.py new file mode 100644 index 000000000..28756451d --- /dev/null +++ b/tests/app/main/views/test_all_services.py @@ -0,0 +1,35 @@ +from bs4 import BeautifulSoup +from flask import url_for + +import app + + +def test_all_services_should_render_all_services_template(app_, + platform_admin_user, + service_one, + mocker): + with app_.test_request_context(): + with app_.test_client() as client: + _login_user(client, mocker, platform_admin_user, service_one) + mocker.patch('app.service_api_client.get_services', return_value={'data': [service_one]}) + response = client.get(url_for('main.show_all_services')) + assert response.status_code == 200 + page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') + assert page.h1.string.strip() == 'All services' + assert app.service_api_client.get_services.call_count == 1 + + +def test_all_service_returns_403_when_not_a_platform_admin(app_, + active_user_with_permissions, + service_one, + mocker): + with app_.test_request_context(): + with app_.test_client() as client: + _login_user(client, mocker, active_user_with_permissions, service_one) + response = client.get(url_for('main.show_all_services')) + assert response.status_code == 403 + + +def _login_user(client, mocker, platform_admin_user, service_one): + mocker.patch('app.user_api_client.get_user', return_value=platform_admin_user) + client.login(platform_admin_user) diff --git a/tests/app/main/views/test_choose_services.py b/tests/app/main/views/test_choose_services.py index d373da631..02b0845a5 100644 --- a/tests/app/main/views/test_choose_services.py +++ b/tests/app/main/views/test_choose_services.py @@ -20,3 +20,24 @@ def test_should_show_choose_services_page(app_, assert mock_get_services.called assert services['data'][0]['name'] in resp_data assert services['data'][1]['name'] in resp_data + assert 'List all services' not in resp_data + + +def test_should_show_all_services_for_platform_admin_user(app_, + platform_admin_user, + mock_get_services, + mocker): + with app_.test_request_context(): + with app_.test_client() as client: + mocker.patch('app.user_api_client.get_user', return_value=platform_admin_user) + client.login(platform_admin_user) + response = client.get(url_for('main.choose_service')) + + assert response.status_code == 200 + resp_data = response.get_data(as_text=True) + assert 'Choose service' in resp_data + services = mock_get_services.side_effect() + assert mock_get_services.called + assert services['data'][0]['name'] in resp_data + assert services['data'][1]['name'] in resp_data + assert 'List all services' in resp_data diff --git a/tests/app/main/views/test_dashboard.py b/tests/app/main/views/test_dashboard.py index 9fc3df630..7345a19bd 100644 --- a/tests/app/main/views/test_dashboard.py +++ b/tests/app/main/views/test_dashboard.py @@ -1,5 +1,4 @@ -from flask import url_for, session -from bs4 import BeautifulSoup +from flask import url_for def test_should_show_recent_jobs_on_dashboard(app_, @@ -30,11 +29,11 @@ def _test_dashboard_menu(mocker, app_, usr, service, permissions): 'app.user_api_client.check_verify_code', return_value=(True, '')) mocker.patch( - 'app.notifications_api_client.get_services', + 'app.service_api_client.get_services', return_value={'data': []}) mocker.patch('app.user_api_client.get_user', return_value=usr) mocker.patch('app.user_api_client.get_user_by_email', return_value=usr) - mocker.patch('app.notifications_api_client.get_service', return_value={'data': service}) + mocker.patch('app.service_api_client.get_service', return_value={'data': service}) mocker.patch('app.statistics_api_client.get_statistics_for_service', return_value={'data': [{}]}) client.login(usr) return client.get(url_for('main.service_dashboard', service_id=service['id'])) @@ -62,6 +61,8 @@ def test_menu_send_messages(mocker, app_, api_user_active, service_one, mock_get assert url_for('main.service_settings', service_id=service_one['id']) not in page assert url_for('main.api_keys', service_id=service_one['id']) not in page + assert url_for('main.documentation', service_id=service_one['id']) not in page + assert url_for('main.show_all_services') not in page def test_menu_manage_service(mocker, app_, api_user_active, service_one, mock_get_service_templates, mock_get_jobs): @@ -86,6 +87,7 @@ def test_menu_manage_service(mocker, app_, api_user_active, service_one, mock_ge assert url_for('main.service_settings', service_id=service_one['id']) in page assert url_for('main.api_keys', service_id=service_one['id']) not in page + assert url_for('main.show_all_services') not in page def test_menu_manage_api_keys(mocker, app_, api_user_active, service_one, mock_get_service_templates, mock_get_jobs): @@ -108,6 +110,26 @@ def test_menu_manage_api_keys(mocker, app_, api_user_active, service_one, mock_g assert url_for('main.manage_users', service_id=service_one['id']) not in page assert url_for('main.service_settings', service_id=service_one['id']) not in page + assert url_for('main.show_all_services') not in page assert url_for('main.api_keys', service_id=service_one['id']) in page - assert url_for('main.documentation') in page + + +def test_menu_all_services_for_platform_admin_user(mocker, app_, platform_admin_user, service_one, + mock_get_service_templates, mock_get_jobs): + with app_.test_request_context(): + resp = _test_dashboard_menu( + mocker, + app_, + platform_admin_user, + service_one, + []) + page = resp.get_data(as_text=True) + assert url_for('main.show_all_services') in page + assert url_for('main.choose_template', service_id=service_one['id'], template_type='sms') in page + assert url_for('main.choose_template', service_id=service_one['id'], template_type='email') in page + assert url_for('main.manage_users', service_id=service_one['id']) in page + assert url_for('main.service_settings', service_id=service_one['id']) in page + assert url_for('main.view_notifications', service_id=service_one['id']) in page + assert url_for('main.view_jobs', service_id=service_one['id']) in page + assert url_for('main.api_keys', service_id=service_one['id']) not in page diff --git a/tests/app/main/views/test_manage_users.py b/tests/app/main/views/test_manage_users.py index d0ed7e571..d5922433a 100644 --- a/tests/app/main/views/test_manage_users.py +++ b/tests/app/main/views/test_manage_users.py @@ -1,72 +1,77 @@ from flask import url_for - from bs4 import BeautifulSoup - +import app from app.notify_client.models import InvitedUser +from tests.conftest import service_one as service_1 def test_should_show_overview_page( app_, - api_user_active, - mock_login, - mock_get_service, - mock_get_users_by_service, - mock_get_invites_for_service, - mock_has_permissions + active_user_with_permissions, + mocker, + mock_get_invites_for_service ): + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) - response = client.get(url_for('main.manage_users', service_id=55555)) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) + client.login(active_user_with_permissions) + mocker.patch('app.user_api_client.get_users_for_service', return_value=[active_user_with_permissions]) + response = client.get(url_for('main.manage_users', service_id=service['id'])) assert 'Manage team' in response.get_data(as_text=True) assert response.status_code == 200 - mock_get_users_by_service.assert_called_once_with(service_id='55555') + app.user_api_client.get_users_for_service.assert_called_once_with(service_id=service['id']) def test_should_show_page_for_one_user( app_, - api_user_active, - mock_login, - mock_get_service, - mock_has_permissions + active_user_with_permissions, + mocker, + mock_login ): + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) - response = client.get(url_for('main.edit_user_permissions', service_id=55555, user_id=0)) + mocker.patch('app.user_api_client.get_user', return_value=active_user_with_permissions) + mocker.patch('app.service_api_client.get_service', return_value=service) + mocker.patch('app.service_api_client.get_services', return_value={'data': [service]}) + client.login(active_user_with_permissions) + response = client.get(url_for('main.edit_user_permissions', service_id=service['id'], user_id=0)) assert response.status_code == 200 def test_edit_user_permissions( app_, - api_user_active, + active_user_with_permissions, mock_login, - mock_get_service, - mock_get_users_by_service, + mocker, mock_get_invites_for_service, - mock_has_permissions, mock_set_user_permissions ): + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - service_id = '55555' - client.login(api_user_active) + + mocker.patch('app.user_api_client.get_user', return_value=active_user_with_permissions) + mocker.patch('app.service_api_client.get_service', return_value=service) + mocker.patch('app.service_api_client.get_services', return_value={'data': [service]}) + client.login(active_user_with_permissions) response = client.post(url_for( - 'main.edit_user_permissions', service_id=service_id, user_id=api_user_active.id - ), data={'email_address': api_user_active.email_address, + 'main.edit_user_permissions', service_id=service['id'], user_id=active_user_with_permissions.id + ), data={'email_address': active_user_with_permissions.email_address, 'send_messages': 'yes', 'manage_service': 'yes', 'manage_api_keys': 'yes'}) assert response.status_code == 302 assert response.location == url_for( - 'main.manage_users', service_id=service_id, _external=True + 'main.manage_users', service_id=service['id'], _external=True ) mock_set_user_permissions.assert_called_with( - str(api_user_active.id), - service_id, + str(active_user_with_permissions.id), + service['id'], ['send_texts', 'send_emails', 'send_letters', @@ -79,21 +84,24 @@ def test_edit_user_permissions( def test_edit_some_user_permissions( app_, - api_user_active, - mock_login, - mock_get_service, - mock_get_users_by_service, + mocker, + active_user_with_permissions, + sample_invite, mock_get_invites_for_service, - mock_has_permissions, mock_set_user_permissions ): + service = service_1(active_user_with_permissions) + data = [InvitedUser(**sample_invite)] with app_.test_request_context(): with app_.test_client() as client: - service_id = '55555' - client.login(api_user_active) + client.login(active_user_with_permissions) + service_id = service['id'] + + mocker.patch('app.invite_api_client.get_invites_for_service', return_value=data) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) response = client.post(url_for( - 'main.edit_user_permissions', service_id=service_id, user_id=api_user_active.id - ), data={'email_address': api_user_active.email_address, + 'main.edit_user_permissions', service_id=service_id, user_id=active_user_with_permissions.id + ), data={'email_address': active_user_with_permissions.email_address, 'send_messages': 'yes', 'manage_service': 'no', 'manage_api_keys': 'no'}) @@ -103,25 +111,30 @@ def test_edit_some_user_permissions( 'main.manage_users', service_id=service_id, _external=True ) mock_set_user_permissions.assert_called_with( - str(api_user_active.id), + str(active_user_with_permissions.id), service_id, ['send_texts', 'send_emails', 'send_letters']) +def _mocks_for_test_manage_users(mocker, active_user_with_permissions, service): + mocker.patch('app.user_api_client.get_user', return_value=active_user_with_permissions) + mocker.patch('app.service_api_client.get_service', return_value=service) + mocker.patch('app.user_api_client.get_users_for_service', return_value=[active_user_with_permissions]) + + def test_should_show_page_for_inviting_user( app_, - api_user_active, - mock_login, - mock_get_user, - mock_get_service, - mock_has_permissions + active_user_with_permissions, + mocker ): + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) - response = client.get(url_for('main.invite_user', service_id=55555)) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) + client.login(active_user_with_permissions) + response = client.get(url_for('main.invite_user', service_id=service['id'])) assert 'Invite a team member' in response.get_data(as_text=True) assert response.status_code == 200 @@ -129,26 +142,24 @@ def test_should_show_page_for_inviting_user( def test_invite_user( app_, - service_one, - api_user_active, - mock_login, - mock_get_user, - mock_get_service, - mock_get_users_by_service, - mock_create_invite, - mock_get_invites_for_service, - mock_has_permissions + active_user_with_permissions, + mocker, + sample_invite ): - from_user = api_user_active.id - service_id = service_one['id'] + service = service_1(active_user_with_permissions) email_address = 'test@example.gov.uk' - permissions = 'send_messages,manage_service,manage_api_keys' + sample_invite['email_address'] = 'test@example.gov.uk' + data = [InvitedUser(**sample_invite)] with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) + client.login(active_user_with_permissions) + 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]) + mocker.patch('app.invite_api_client.create_invite', return_value=InvitedUser(**sample_invite)) response = client.post( - url_for('main.invite_user', service_id=service_id), + url_for('main.invite_user', service_id=service['id']), data={'email_address': email_address, 'send_messages': 'yes', 'manage_service': 'yes', @@ -157,8 +168,6 @@ def test_invite_user( ) assert response.status_code == 200 - mock_create_invite.assert_called_with(from_user, service_id, email_address, permissions) - mock_get_invites_for_service.assert_called_with(service_id=service_id) page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') assert page.h1.string.strip() == 'Manage team' flash_banner = page.find('div', class_='banner-default-with-tick').string.strip() @@ -166,45 +175,39 @@ def test_invite_user( def test_cancel_invited_user_cancels_user_invitations(app_, - api_user_active, - mock_login, - mocker, - mock_has_permissions): + active_user_with_permissions, + mocker + ): with app_.test_request_context(): with app_.test_client() as client: mocker.patch('app.invite_api_client.cancel_invited_user') import uuid invited_user_id = uuid.uuid4() - client.login(api_user_active) - service_id = uuid.uuid4() - response = client.get(url_for('main.cancel_invited_user', service_id=service_id, + service = service_1(active_user_with_permissions) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) + client.login(active_user_with_permissions) + response = client.get(url_for('main.cancel_invited_user', service_id=service['id'], invited_user_id=invited_user_id)) assert response.status_code == 302 - assert response.location == url_for('main.manage_users', service_id=service_id, _external=True) + assert response.location == url_for('main.manage_users', service_id=service['id'], _external=True) def test_manage_users_shows_invited_user(app_, mocker, - api_user_active, - mock_get_service, - mock_login, - mock_has_permissions, - mock_get_users_by_service, + active_user_with_permissions, sample_invite): - - import uuid - invited_user_id = uuid.uuid4() - sample_invite['id'] = invited_user_id + service = service_1(active_user_with_permissions) data = [InvitedUser(**sample_invite)] - with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) + _mocks_for_test_manage_users(mocker, active_user_with_permissions, service) + client.login(active_user_with_permissions) 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]) - response = client.get(url_for('main.manage_users', service_id=55555)) + response = client.get(url_for('main.manage_users', service_id=service['id'])) assert response.status_code == 200 page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') @@ -217,11 +220,8 @@ def test_manage_users_shows_invited_user(app_, def test_manage_users_does_not_show_accepted_invite(app_, mocker, - api_user_active, - mock_get_service, + active_user_with_permissions, mock_login, - mock_has_permissions, - mock_get_users_by_service, sample_invite): import uuid @@ -229,14 +229,17 @@ def test_manage_users_does_not_show_accepted_invite(app_, sample_invite['id'] = invited_user_id sample_invite['status'] = 'accepted' data = [InvitedUser(**sample_invite)] - + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) - + mocker.patch('app.user_api_client.get_user', return_value=active_user_with_permissions) + mocker.patch('app.service_api_client.get_service', return_value=service) + mocker.patch('app.service_api_client.get_services', return_value={'data': [service]}) + client.login(active_user_with_permissions) + mocker.patch('app.user_api_client.get_users_for_service', return_value=[active_user_with_permissions]) mocker.patch('app.invite_api_client.get_invites_for_service', return_value=data) - response = client.get(url_for('main.manage_users', service_id=55555)) + response = client.get(url_for('main.manage_users', service_id=service['id'])) assert response.status_code == 200 page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser') @@ -248,27 +251,22 @@ def test_manage_users_does_not_show_accepted_invite(app_, def test_user_cant_invite_themselves( app_, - service_one, - api_user_active, mock_login, - mock_get_user, - mock_get_service, - mock_get_users_by_service, + mocker, + active_user_with_permissions, mock_create_invite, - mock_get_invites_for_service, - mock_has_permissions + mock_get_invites_for_service ): - from_user = api_user_active.id - service_id = service_one['id'] - email_address = api_user_active.email_address - permissions = 'send_messages,manage_service,manage_api_keys' - + service = service_1(active_user_with_permissions) with app_.test_request_context(): with app_.test_client() as client: - client.login(api_user_active) + mocker.patch('app.user_api_client.get_user', return_value=active_user_with_permissions) + mocker.patch('app.service_api_client.get_service', return_value=service) + mocker.patch('app.service_api_client.get_services', return_value={'data': [service]}) + client.login(active_user_with_permissions) response = client.post( - url_for('main.invite_user', service_id=service_id), - data={'email_address': email_address, + url_for('main.invite_user', service_id=service['id']), + data={'email_address': active_user_with_permissions.email_address, 'send_messages': 'yes', 'manage_service': 'yes', 'manage_api_keys': 'yes'}, diff --git a/tests/app/main/views/test_send.py b/tests/app/main/views/test_send.py index 363f90bab..f45ed0c17 100644 --- a/tests/app/main/views/test_send.py +++ b/tests/app/main/views/test_send.py @@ -390,10 +390,8 @@ def test_route_choose_template_manage_service_permissions(mocker, def test_route_choose_template_send_messages_permissions(mocker, app_, - api_user_active, + active_user_with_permissions, service_one, - mock_login, - mock_get_user, mock_get_service, mock_check_verify_code, mock_get_service_templates, @@ -410,7 +408,7 @@ def test_route_choose_template_send_messages_permissions(mocker, service_id=service_one['id'], template_type='sms'), ['send_texts', 'send_emails', 'send_letters'], - api_user_active, + active_user_with_permissions, service_one) page = resp.get_data(as_text=True) assert url_for( @@ -431,7 +429,6 @@ def test_route_choose_template_manage_api_keys_permissions(mocker, app_, api_user_active, service_one, - mock_login, mock_get_user, mock_get_service, mock_check_verify_code, diff --git a/tests/app/main/views/test_service_settings.py b/tests/app/main/views/test_service_settings.py index 65f7030e7..59af4e3d6 100644 --- a/tests/app/main/views/test_service_settings.py +++ b/tests/app/main/views/test_service_settings.py @@ -422,3 +422,26 @@ def test_route_invalid_permissions(mocker, app_, api_user_active, service_one): ['blah'], api_user_active, service_one) + + +def test_route_for_platform_admin(mocker, app_, platform_admin_user, service_one): + routes = [ + 'main.service_settings', + 'main.service_name_change', + 'main.service_name_change_confirm', + 'main.service_request_to_go_live', + 'main.service_status_change', + 'main.service_status_change_confirm', + 'main.service_delete', + 'main.service_delete_confirm' + ] + with app_.test_request_context(): + for route in routes: + validate_route_permission(mocker, + app_, + "GET", + 200, + url_for(route, service_id=service_one['id']), + [], + platform_admin_user, + service_one) diff --git a/tests/app/main/views/test_sign_out.py b/tests/app/main/views/test_sign_out.py index 1d4860a71..d3b099f58 100644 --- a/tests/app/main/views/test_sign_out.py +++ b/tests/app/main/views/test_sign_out.py @@ -18,7 +18,8 @@ def test_sign_out_user(app_, mock_get_service_templates, mock_get_service_statistics, mock_login, - mock_get_jobs): + mock_get_jobs, + mock_has_permissions): with app_.test_request_context(): with app_.test_client() as client: client.login(api_user_active) diff --git a/tests/conftest.py b/tests/conftest.py index 85b681d46..d93a14ebf 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -38,19 +38,13 @@ def app_(request): @pytest.fixture(scope='function') -def service_one(request, api_user_active): - import uuid - return service_json(str(uuid.uuid4()), 'service one', [api_user_active.id]) +def service_one(api_user_active): + return service_json(SERVICE_ONE_ID, 'service one', [api_user_active.id]) @pytest.fixture(scope='function') def mock_send_sms(request, mocker): - return mocker.patch("app.notifications_api_client.send_sms") - - -@pytest.fixture(scope='function') -def mock_send_email(request, mocker): - return mocker.patch("app.notifications_api_client.send_email", autospec=True) + return mocker.patch("app.service_api_client.send_sms") @pytest.fixture(scope='function') @@ -61,7 +55,7 @@ def mock_get_service(mocker, api_user_active): active=False, restricted=True) return {'data': service} - return mocker.patch('app.notifications_api_client.get_service', side_effect=_get) + return mocker.patch('app.service_api_client.get_service', side_effect=_get) @pytest.fixture(scope='function') @@ -73,7 +67,7 @@ def mock_create_service(mocker): return service['id'] return mocker.patch( - 'app.notifications_api_client.create_service', side_effect=_create) + 'app.service_api_client.create_service', side_effect=_create) @pytest.fixture(scope='function') @@ -90,12 +84,11 @@ def mock_update_service(mocker): return {'data': service} return mocker.patch( - 'app.notifications_api_client.update_service', side_effect=_update) + 'app.service_api_client.update_service', side_effect=_update) @pytest.fixture(scope='function') def mock_update_service_raise_httperror_duplicate_name(mocker): - def _update(service_id, service_name, active, @@ -108,7 +101,7 @@ def mock_update_service_raise_httperror_duplicate_name(mocker): raise http_error return mocker.patch( - 'app.notifications_api_client.update_service', side_effect=_update) + 'app.service_api_client.update_service', side_effect=_update) SERVICE_ONE_ID = "596364a0-858e-42c8-9062-a8fe822260eb" @@ -118,7 +111,7 @@ SERVICE_TWO_ID = "147ad62a-2951-4fa1-9ca0-093cd1a52c52" @pytest.fixture(scope='function') def mock_get_services(mocker, user=None): if user is None: - user = api_user_active() + user = active_user_with_permissions() def _create(user_id=None): service_one = service_json( @@ -128,7 +121,7 @@ def mock_get_services(mocker, user=None): return {'data': [service_one, service_two]} return mocker.patch( - 'app.notifications_api_client.get_services', side_effect=_create) + 'app.service_api_client.get_services', side_effect=_create) @pytest.fixture(scope='function') @@ -142,7 +135,7 @@ def mock_get_services_with_one_service(mocker, user=None): )]} return mocker.patch( - 'app.notifications_api_client.get_services', side_effect=_create) + 'app.service_api_client.get_services', side_effect=_create) @pytest.fixture(scope='function') @@ -151,7 +144,7 @@ def mock_delete_service(mocker, mock_get_service): return mock_get_service.side_effect(service_id) return mocker.patch( - 'app.notifications_api_client.delete_service', side_effect=_delete) + 'app.service_api_client.delete_service', side_effect=_delete) @pytest.fixture(scope='function') @@ -171,7 +164,7 @@ def mock_get_service_template(mocker): return {'data': template} return mocker.patch( - 'app.notifications_api_client.get_service_template', side_effect=_create) + 'app.service_api_client.get_service_template', side_effect=_create) @pytest.fixture(scope='function') @@ -182,18 +175,18 @@ def mock_get_service_email_template(mocker): return {'data': template} return mocker.patch( - 'app.notifications_api_client.get_service_template', side_effect=_create) + 'app.service_api_client.get_service_template', side_effect=_create) @pytest.fixture(scope='function') def mock_create_service_template(mocker): def _create(name, type_, content, service): template = template_json( - service, 101, name, type_, content) + 101, name, type_, content, service) return {'data': template} return mocker.patch( - 'app.notifications_api_client.create_service_template', + 'app.service_api_client.create_service_template', side_effect=_create) @@ -201,11 +194,11 @@ def mock_create_service_template(mocker): def mock_update_service_template(mocker): def _update(id_, name, type_, content, service): template = template_json( - service, id_, name, type_, content) + id_, name, type_, content, service) return {'data': template} return mocker.patch( - 'app.notifications_api_client.update_service_template', + 'app.service_api_client.update_service_template', side_effect=_update) @@ -214,17 +207,21 @@ def mock_get_service_templates(mocker): def _create(service_id): return {'data': [ template_json( - service_id, 1, "sms_template_one", "sms", "sms template one content"), + service_id, 1, "sms_template_one", "sms", "sms template one content" + ), template_json( - service_id, 2, "sms_template_two", "sms", "sms template two content"), + service_id, 2, "sms_template_two", "sms", "sms template two content" + ), template_json( - service_id, 3, "email_template_one", "email", "email template one content"), + service_id, 3, "email_template_one", "email", "email template one content" + ), template_json( - service_id, 4, "email_template_two", "email", "email template two content") + service_id, 4, "email_template_two", "email", "email template two content" + ) ]} return mocker.patch( - 'app.notifications_api_client.get_service_templates', + 'app.service_api_client.get_service_templates', side_effect=_create) @@ -236,7 +233,7 @@ def mock_delete_service_template(mocker): return {'data': template} return mocker.patch( - 'app.notifications_api_client.delete_service_template', side_effect=_delete) + 'app.service_api_client.delete_service_template', side_effect=_delete) @pytest.fixture(scope='function') @@ -255,6 +252,23 @@ def api_user_pending(): return user +@pytest.fixture(scope='function') +def platform_admin_user(): + from app.notify_client.user_api_client import User + user_data = {'id': 222, + 'name': 'Platform admin user', + 'password': 'somepassword', + 'email_address': 'platform@admin.gov.uk', + 'mobile_number': '+4472341234', + 'state': 'active', + 'failed_login_count': 0, + 'permissions': {}, + 'platform_admin': True + } + user = User(user_data) + return user + + @pytest.fixture(scope='function') def api_user_active(): from app.notify_client.user_api_client import User @@ -271,6 +285,31 @@ def api_user_active(): return user +@pytest.fixture(scope='function') +def active_user_with_permissions(): + from app.notify_client.user_api_client import User + + user_data = {'id': 222, + 'name': 'Test User', + 'password': 'somepassword', + 'email_address': 'test@user.gov.uk', + 'mobile_number': '+4412341234', + 'state': 'active', + 'failed_login_count': 0, + 'permissions': {SERVICE_ONE_ID: ['send_texts', + 'send_emails', + 'send_letters', + 'manage_users', + 'manage_templates', + 'manage_settings', + 'manage_api_keys', + 'access_developer_docs']}, + 'platform_admin': False + } + user = User(user_data) + return user + + @pytest.fixture(scope='function') def api_user_locked(): from app.notify_client.user_api_client import User @@ -494,7 +533,6 @@ def mock_get_no_api_keys(mocker): @pytest.fixture(scope='function') def mock_login(mocker, mock_get_user, mock_update_user): - def _verify_code(user_id, code, code_type): return True, '' @@ -507,7 +545,7 @@ def mock_login(mocker, mock_get_user, mock_update_user): side_effect=_verify_code ), mocker.patch( - 'app.notifications_api_client.get_services', + 'app.service_api_client.get_services', side_effect=_no_services ) ) @@ -614,7 +652,7 @@ def mock_get_notifications_with_previous_next(mocker): @pytest.fixture(scope='function') def mock_has_permissions(mocker): - def _has_permission(permissions, service_id=None, or_=False): + def _has_permission(permissions=None, or_=False, admin_override=False): return True return mocker.patch( 'app.notify_client.user_api_client.User.has_permissions', @@ -627,7 +665,14 @@ def mock_get_users_by_service(mocker): data = [{'id': 1, 'logged_in_at': None, 'mobile_number': '+447700900986', - 'permissions': [], + 'permissions': {SERVICE_ONE_ID: ['send_texts', + 'send_emails', + 'send_letters', + 'manage_users', + 'manage_templates', + 'manage_settings', + 'manage_api_keys', + 'access_developer_docs']}, 'state': 'active', 'password_changed_at': None, 'name': 'Test User',