From fbc470998f159aefb6d4cc1a7a47c2b8a2fc5e1b Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Tue, 9 Jul 2019 12:27:06 -0400 Subject: [PATCH 1/2] proper status codes for raised OWSExceptions --- CHANGES.rst | 3 +++ tests/common.py | 2 +- tests/test_owsproxy.py | 4 ++-- twitcher/owsexceptions.py | 37 ++++++++++++++++++++++++++++++++----- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index ef88390f..229bc8a4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,12 +5,15 @@ Unreleased ========== New Features: + * Adds route `/info` which returns contents of `twitcher.__version__`. * Adds route `/versions` which returns version details such as `Twitcher` app version and employed adapter version. Changes: + * Updated `README.rst` to match recent development, reference and docker image link. * Adds URI of `/info` and `/versions` routes in the frontpage response. +* Corresponding HTTP status codes are returned for raised ``OWSException``. Fixes: diff --git a/tests/common.py b/tests/common.py index f8625a12..8696ca06 100644 --- a/tests/common.py +++ b/tests/common.py @@ -11,7 +11,7 @@ WMS_CAPS_NCWMS2_111_XML = os.path.join(RESOURCES_PATH, 'wms_caps_ncwms2_111.xml') WMS_CAPS_NCWMS2_130_XML = os.path.join(RESOURCES_PATH, 'wms_caps_ncwms2_130.xml') -WPS_TEST_SERVICE = 'http://localhost:5000/wps' +WPS_TEST_SERVICE = os.getenv("TWITCHER_TEST_WPS", "http://localhost:5000/wps") def dummy_request(dbsession): diff --git a/tests/test_owsproxy.py b/tests/test_owsproxy.py index ca1bc0c9..c07c6bfe 100644 --- a/tests/test_owsproxy.py +++ b/tests/test_owsproxy.py @@ -27,10 +27,10 @@ def tearDown(self): def test_badrequest_url(self): request = DummyRequest(scheme='http') response = owsproxy_view(request) - assert isinstance(response, OWSAccessFailed) is True + assert isinstance(response, OWSAccessFailed) def test_badrequest_netloc(self): request = DummyRequest(scheme='http', params={'url': 'http://'}) response = owsproxy_view(request) - assert isinstance(response, OWSAccessFailed) is True + assert isinstance(response, OWSAccessFailed) diff --git a/twitcher/owsexceptions.py b/twitcher/owsexceptions.py index c105d874..13d16e53 100644 --- a/twitcher/owsexceptions.py +++ b/twitcher/owsexceptions.py @@ -13,11 +13,21 @@ from pyramid.interfaces import IExceptionResponse from pyramid.response import Response +from pyramid.httpexceptions import ( + HTTPException, + HTTPUnauthorized, + HTTPBadRequest, + HTTPInternalServerError, + HTTPNotImplemented, + status_map, +) + +import inspect @implementer(IExceptionResponse) class OWSException(Response, Exception): - + status_base = HTTPNotImplemented code = 'NoApplicableCode' value = None locator = 'NoApplicableCode' @@ -34,16 +44,29 @@ class OWSException(Response, Exception): ''') - def __init__(self, detail=None, value=None, **kw): - Response.__init__(self, status='200 OK', **kw) + def __init__(self, detail=None, value=None, status_base=None, **kw): + if status_base is not None: + self.status_base = status_base + Response.__init__(self, status=self._get_status_string(), **kw) Exception.__init__(self, detail) self.message = detail or self.explanation if value: self.locator = value - def __str__(self): + def __str__(self, skip_body=False): return self.message + def _get_status_string(self): + if inspect.isclass(self.status_base) and issubclass(self.status_base, HTTPException): + status = self.status_base().status + elif isinstance(self.status_base, HTTPException): + status = self.status_base.status + elif isinstance(self.status_base, int): + status = status_map[self.status_base]().status + else: + raise ValueError("Invalid status base configuration.") + return status + def prepare(self, environ): if not self.body: self.content_type = 'text/xml' @@ -77,21 +100,24 @@ def __call__(self, environ, start_response): class OWSAccessForbidden(OWSException): + status_base = HTTPUnauthorized locator = "AccessForbidden" explanation = "Access to this service is forbidden" class OWSAccessFailed(OWSException): + status_base = HTTPBadRequest locator = "NotAcceptable" explanation = "Access to this service failed" class OWSNoApplicableCode(OWSException): - pass + status_base = HTTPInternalServerError class OWSMissingParameterValue(OWSException): """MissingParameterValue WPS Exception""" + status_base = HTTPBadRequest code = "MissingParameterValue" locator = "" explanation = "Parameter value is missing" @@ -99,6 +125,7 @@ class OWSMissingParameterValue(OWSException): class OWSInvalidParameterValue(OWSException): """InvalidParameterValue WPS Exception""" + status_base = HTTPBadRequest code = "InvalidParameterValue" locator = "" explanation = "Parameter value is invalid" From 19c713e203c4d5e0a9e121fb7baefb4e446b1619 Mon Sep 17 00:00:00 2001 From: Francis Charette Migneault Date: Tue, 9 Jul 2019 12:33:49 -0400 Subject: [PATCH 2/2] 404 for not found service --- twitcher/owssecurity.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/twitcher/owssecurity.py b/twitcher/owssecurity.py index dfd3991b..62c0e07a 100644 --- a/twitcher/owssecurity.py +++ b/twitcher/owssecurity.py @@ -2,6 +2,7 @@ from twitcher.owsexceptions import OWSAccessForbidden, OWSInvalidParameterValue from twitcher.utils import path_elements, parse_service_name from twitcher.owsrequest import OWSRequest +from pyramid.httpexceptions import HTTPNotFound import logging LOGGER = logging.getLogger("TWITCHER") @@ -71,7 +72,7 @@ def check_request(self, request): LOGGER.warning('public access for service %s', service_name) except ServiceNotFound: raise OWSInvalidParameterValue( - "Service not found", value="service") + "Service not found", value="service", status_base=HTTPNotFound) ows_request = OWSRequest(request) if not ows_request.service_allowed(): raise OWSInvalidParameterValue(