From c675925fd186f10d4ba1057df849ad0e165fc237 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Thu, 21 Mar 2019 16:41:22 +0000 Subject: [PATCH 1/3] Upgrade pyexcel-io, which also upgrades Werkzeug Upgraded pyexcel-io from 0.5.14 to 0.5.16. This change causes Werkzeug to be upgraded from 0.14.1 to 0.15.1 which requires some changes: * ProxyFix now needs to be imported from a different location * The status code of RequestRedirect has changed from 301 to 308. Since status code 308 is not currently supported on Internet Explorer with Windows 7 and 8.1, this subclasses RequestRedirect to keep the status code of 301. changelog: https://werkzeug.palletsprojects.com/en/0.15.x/changes/#version-0-15-0 --- app/__init__.py | 2 +- app/main/views/choose_account.py | 6 +++--- app/main/views/send.py | 6 +++--- app/main/views/verify.py | 2 +- app/proxy_fix.py | 2 +- app/utils.py | 10 ++++++++++ requirements-app.txt | 2 +- requirements.txt | 8 ++++---- 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index 0a2ef8724..b93fb4876 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -634,7 +634,7 @@ def register_errorhandlers(application): # noqa (C901 too complex) @application.errorhandler(WerkzeugHTTPException) def handle_http_error(error): if error.code == 301: - # RequestRedirect exception + # PermanentRedirect exception return error return _error_response(error.code) diff --git a/app/main/views/choose_account.py b/app/main/views/choose_account.py index 82d6d96ab..7af74bc0b 100644 --- a/app/main/views/choose_account.py +++ b/app/main/views/choose_account.py @@ -1,19 +1,19 @@ from flask import redirect, render_template, session, url_for from flask_login import current_user, login_required -from werkzeug.routing import RequestRedirect from app import user_api_client from app.main import main +from app.utils import PermanentRedirect @main.route("/services") def choose_service(): - raise RequestRedirect(url_for('.choose_account')) + raise PermanentRedirect(url_for('.choose_account')) @main.route("/services-or-dashboard") def services_or_dashboard(): - raise RequestRedirect(url_for('.show_accounts_or_dashboard')) + raise PermanentRedirect(url_for('.show_accounts_or_dashboard')) @main.route("/accounts") diff --git a/app/main/views/send.py b/app/main/views/send.py index dc6fb6aaa..92d8d3509 100644 --- a/app/main/views/send.py +++ b/app/main/views/send.py @@ -24,7 +24,6 @@ from notifications_utils.recipients import ( ) from notifications_utils.sanitise_text import SanitiseASCII from orderedset import OrderedSet -from werkzeug.routing import RequestRedirect from xlrd.biffh import XLRDError from xlrd.xldate import XLDateError @@ -49,6 +48,7 @@ from app.s3_client.s3_csv_client import ( ) from app.template_previews import TemplatePreview, get_page_count_for_letter from app.utils import ( + PermanentRedirect, Spreadsheet, email_or_sms_not_enabled, get_errors_for_csv, @@ -512,7 +512,7 @@ def _check_messages(service_id, template_id, upload_id, preview_row, letters_as_ # If we just return a `redirect` (302) object here, we'll get # errors when we try and unpack in the check_messages route. # Rasing a werkzeug.routing redirect means that doesn't happen. - raise RequestRedirect(url_for( + raise PermanentRedirect(url_for( '.send_messages', service_id=service_id, template_id=template_id @@ -875,7 +875,7 @@ def _check_notification(service_id, template_id, exception=None): ) or not all_placeholders_in_session(template.placeholders) ): - raise RequestRedirect(back_link) + raise PermanentRedirect(back_link) template.values = get_recipient_and_placeholders_from_session(template.template_type) return dict( diff --git a/app/main/views/verify.py b/app/main/views/verify.py index 653eef31a..65d5f6f60 100644 --- a/app/main/views/verify.py +++ b/app/main/views/verify.py @@ -64,7 +64,7 @@ def verify_email(token): session['user_details'] = {"email": user.email_address, "id": user.id} user_api_client.send_verify_code(user.id, 'sms', user.mobile_number) - return redirect('verify') + return redirect(url_for('main.verify')) def activate_user(user_id): diff --git a/app/proxy_fix.py b/app/proxy_fix.py index a572672d7..2027cb212 100644 --- a/app/proxy_fix.py +++ b/app/proxy_fix.py @@ -1,4 +1,4 @@ -from werkzeug.contrib.fixers import ProxyFix +from werkzeug.middleware.proxy_fix import ProxyFix class CustomProxyFix(object): diff --git a/app/utils.py b/app/utils.py index d0da4e6c3..2a70ab3fb 100644 --- a/app/utils.py +++ b/app/utils.py @@ -36,6 +36,7 @@ from notifications_utils.template import ( from notifications_utils.timezones import convert_utc_to_bst from orderedset._orderedset import OrderedSet from werkzeug.datastructures import MultiDict +from werkzeug.routing import RequestRedirect SENDING_STATUSES = ['created', 'pending', 'sending', 'pending-virus-check'] DELIVERED_STATUSES = ['delivered', 'sent', 'returned-letter'] @@ -680,3 +681,12 @@ def redact_mobile_number(mobile_number, spacing=""): for i in indices: mobile_number_list[i] = redact_character return "".join(mobile_number_list) + + +class PermanentRedirect(RequestRedirect): + """ + In Werkzeug 0.15.0 the status code for RequestRedirect changed from 301 to 308. + 308 status codes are not supported when Internet Explorer is used with Windows 7 + and Windows 8.1, so this class keeps the original status code of 301. + """ + code = 301 diff --git a/requirements-app.txt b/requirements-app.txt index 53707dde2..648510edc 100644 --- a/requirements-app.txt +++ b/requirements-app.txt @@ -8,7 +8,7 @@ Flask-Login==0.4.1 blinker==1.4 pyexcel==0.5.10 -pyexcel-io==0.5.14 +pyexcel-io==0.5.16 pyexcel-xls==0.5.8 pyexcel-xlsx==0.5.7 pyexcel-ods3==0.5.3 diff --git a/requirements.txt b/requirements.txt index 245b91c76..56384145b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,7 +10,7 @@ Flask-Login==0.4.1 blinker==1.4 pyexcel==0.5.10 -pyexcel-io==0.5.14 +pyexcel-io==0.5.16 pyexcel-xls==0.5.8 pyexcel-xlsx==0.5.7 pyexcel-ods3==0.5.3 @@ -28,10 +28,10 @@ itsdangerous==0.24 # pyup: <1.0.0 git+https://github.com/alphagov/notifications-utils.git@31.2.2#egg=notifications-utils==31.2.2 ## The following requirements were added by pip freeze: -awscli==1.16.125 +awscli==1.16.129 bleach==3.1.0 boto3==1.6.16 -botocore==1.12.115 +botocore==1.12.119 certifi==2019.3.9 chardet==3.0.4 Click==7.0 @@ -72,7 +72,7 @@ statsd==3.3.0 texttable==1.6.1 urllib3==1.24.1 webencodings==0.5.1 -Werkzeug==0.14.1 +Werkzeug==0.15.1 WTForms==2.2.1 xlrd==1.2.0 xlwt==1.3.0 From 615abf21e5b0bd6bdee61407136c9ed63a87def8 Mon Sep 17 00:00:00 2001 From: Katie Smith Date: Thu, 21 Mar 2019 16:59:01 +0000 Subject: [PATCH 2/3] Upgrade pyexcel from 0.5.10 to 0.5.13 --- requirements-app.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-app.txt b/requirements-app.txt index 648510edc..e44d51732 100644 --- a/requirements-app.txt +++ b/requirements-app.txt @@ -7,7 +7,7 @@ Flask-WTF==0.14.2 Flask-Login==0.4.1 blinker==1.4 -pyexcel==0.5.10 +pyexcel==0.5.13 pyexcel-io==0.5.16 pyexcel-xls==0.5.8 pyexcel-xlsx==0.5.7 diff --git a/requirements.txt b/requirements.txt index 56384145b..51265544e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ Flask-WTF==0.14.2 Flask-Login==0.4.1 blinker==1.4 -pyexcel==0.5.10 +pyexcel==0.5.13 pyexcel-io==0.5.16 pyexcel-xls==0.5.8 pyexcel-xlsx==0.5.7 From db3cee63baf64d00b2d2ac4fcf726f287b6d7af2 Mon Sep 17 00:00:00 2001 From: Chris Hill-Scott Date: Mon, 25 Mar 2019 10:52:21 +0000 Subject: [PATCH 3/3] Update call to proxy fix to use new method signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Old method: ```python ProxyFix(app, num_proxies=1) ``` https://werkzeug.palletsprojects.com/en/0.14.x/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix This uses forwarded values for `REMOTE_ADDR` and `HTTP_HOST`. New method: ```python ProxyFix(app, num_proxies=None, x_for=1, x_proto=0, x_host=0, x_port=0, x_prefix=0) ``` https://werkzeug.palletsprojects.com/en/0.15.x/middleware/proxy_fix/#module-werkzeug.middleware.proxy_fix Setting `x_for=1` preserves the same behaviour as `num_proxies=1`. Setting `x_proto=1` and `x_host=1` will cause `REMOTE_ADDR`, `HTTP_HOST`, `SERVER_NAME` and `SERVER_PORT` to be forwarded. So we will be forwarding `SERVER_NAME` and `SERVER_PORT` which we weren’t before, but we think this is OK. --- app/proxy_fix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/proxy_fix.py b/app/proxy_fix.py index 2027cb212..795597438 100644 --- a/app/proxy_fix.py +++ b/app/proxy_fix.py @@ -3,7 +3,7 @@ from werkzeug.middleware.proxy_fix import ProxyFix class CustomProxyFix(object): def __init__(self, app, forwarded_proto): - self.app = ProxyFix(app) + self.app = ProxyFix(app, x_for=1, x_proto=1, x_host=1, x_port=0, x_prefix=0) self.forwarded_proto = forwarded_proto def __call__(self, environ, start_response):