From 7b44beb1fd34694d02f634f8b6552fc04e93cad4 Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Wed, 13 Apr 2016 09:01:42 -0400 Subject: [PATCH 1/2] Fix `WWW-Authenticate` header on 401 --- endpoints/api/__init__.py | 4 ++-- test/test_auth.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/endpoints/api/__init__.py b/endpoints/api/__init__.py index 8045b0f1d..1b424aaca 100644 --- a/endpoints/api/__init__.py +++ b/endpoints/api/__init__.py @@ -39,9 +39,9 @@ api.decorators = [csrf_protect, @crossdomain(origin='*', headers=['Authorization', 'Content-Type']) def handle_api_error(error): response = Response(json.dumps(error.to_dict()), error.status_code, mimetype='application/problem+json') - if error.status_code is 401: + if error.status_code == 401: response.headers['WWW-Authenticate'] = ('Bearer error="%s" error_description="%s"' % - (error.error_type, error.error_description)) + (error.error_type.value, error.error_description)) return response def resource(*urls, **kwargs): diff --git a/test/test_auth.py b/test/test_auth.py index 9c5f6e6a4..8bf78f897 100644 --- a/test/test_auth.py +++ b/test/test_auth.py @@ -103,6 +103,11 @@ class TestAuth(ApiTestCase): self.conduct_basic_auth('$oauthtoken', 'foobar') self.verify_no_identity() + def test_oauth_invalid_http_response(self): + rv = self.app.get(api.url_for(User), headers={'Authorization': 'Bearer bad_token'}) + assert 'WWW-Authenticate' in rv.headers + self.assertEquals(401, rv.status_code) + def test_oauth_valid_user(self): user = model.user.get_user(ADMIN_ACCESS_USER) self.create_oauth(user) From e1b33124954f48ea9c27bf7388b1717e770117ee Mon Sep 17 00:00:00 2001 From: Evan Cordell Date: Wed, 13 Apr 2016 09:02:24 -0400 Subject: [PATCH 2/2] Add back `error_message` and `error_type` for backwards-compatibility --- endpoints/api/discovery.py | 58 ++++++++++++++++++++++---------------- endpoints/exception.py | 2 ++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/endpoints/api/discovery.py b/endpoints/api/discovery.py index b66bef698..2e0536808 100644 --- a/endpoints/api/discovery.py +++ b/endpoints/api/discovery.py @@ -180,30 +180,38 @@ def swagger_route_data(include_internal=False, compact=False): models[response_schema_name] = view_class.schemas[response_schema_name] models['ApiError'] = { - 'type': 'object', - 'properties': { - 'status': { - 'type': 'integer', - 'description': 'Status code of the response.' - }, - 'type': { - 'type': 'string', - 'description': 'Reference to the type of the error.' - }, - 'detail': { - 'type': 'string', - 'description': 'Details about the specific instance of the error.' - }, - 'title': { - 'type': 'string', - 'description': 'Unique error code to identify the type of error.' - } + 'type': 'object', + 'properties': { + 'status': { + 'type': 'integer', + 'description': 'Status code of the response.' }, - 'required': [ - 'status', - 'type', - 'title', - ] + 'type': { + 'type': 'string', + 'description': 'Reference to the type of the error.' + }, + 'detail': { + 'type': 'string', + 'description': 'Details about the specific instance of the error.' + }, + 'title': { + 'type': 'string', + 'description': 'Unique error code to identify the type of error.' + }, + 'error_message': { + 'type': 'string', + 'description': 'Deprecated; alias for detail' + }, + 'error_type': { + 'type': 'string', + 'description': 'Deprecated; alias for detail' + } + }, + 'required': [ + 'status', + 'type', + 'title', + ] } responses = { @@ -224,8 +232,8 @@ def swagger_route_data(include_internal=False, compact=False): }, } - for status, body in responses.items(): - body['schema'] = {'$ref': '#/definitions/ApiError'} + for _, body in responses.items(): + body['schema'] = {'$ref': '#/definitions/ApiError'} if method_name == 'DELETE': responses['204'] = { diff --git a/endpoints/exception.py b/endpoints/exception.py index 41b113748..d11266153 100644 --- a/endpoints/exception.py +++ b/endpoints/exception.py @@ -67,7 +67,9 @@ class ApiException(Exception): if self.error_description is not None: rv['detail'] = self.error_description + rv['error_message'] = self.error_description # TODO: deprecate + rv['error_type'] = self.error_type.value # TODO: deprecate rv['title'] = self.error_type.value rv['type'] = url_for('error', error_type=self.error_type.value, _external=True) rv['status'] = self.status_code