diff --git a/ironic_lib/auth_basic.py b/ironic_lib/auth_basic.py index 5c5e27fd..2ccd2dc2 100644 --- a/ironic_lib/auth_basic.py +++ b/ironic_lib/auth_basic.py @@ -18,6 +18,7 @@ import binascii import bcrypt from oslo_log import log +import webob from ironic_lib.common.i18n import _ from ironic_lib import exception @@ -34,6 +35,16 @@ class BasicAuthMiddleware(object): self.auth_file = auth_file validate_auth_file(auth_file) + def format_exception(self, e): + result = {'error': {'message': str(e), 'code': e.code}} + headers = list(e.headers.items()) + [ + ('Content-Type', 'application/json') + ] + return webob.Response(content_type='application/json', + status_code=e.code, + json_body=result, + headerlist=headers) + def __call__(self, env, start_response): try: @@ -44,9 +55,8 @@ class BasicAuthMiddleware(object): return self.app(env, start_response) except exception.IronicException as e: - status = '%s %s' % (int(e.code), str(e)) - headers = [(k, v) for k, v in e.headers.items()] - start_response(status, headers) + response = self.format_exception(e) + return response(env, start_response) def authenticate(auth_file, username, password): @@ -182,6 +192,4 @@ def unauthorized(message=None): """ if not message: message = _('Incorrect username or password') - e = exception.Unauthorized(message) - e.headers['WWW-Authenticate'] = 'Basic realm="Baremetal API"' - raise e + raise exception.Unauthorized(message) diff --git a/ironic_lib/exception.py b/ironic_lib/exception.py index 0062d60b..226010a8 100644 --- a/ironic_lib/exception.py +++ b/ironic_lib/exception.py @@ -176,6 +176,7 @@ class BadRequest(IronicException): class Unauthorized(IronicException): code = http_client.UNAUTHORIZED + headers = {'WWW-Authenticate': 'Basic realm="Baremetal API"'} class ConfigInvalid(IronicException): diff --git a/ironic_lib/tests/test_basic_auth.py b/ironic_lib/tests/test_basic_auth.py index 7e3bf201..cbdee717 100644 --- a/ironic_lib/tests/test_basic_auth.py +++ b/ironic_lib/tests/test_basic_auth.py @@ -14,6 +14,7 @@ # under the License. import base64 +import json import os import tempfile from unittest import mock @@ -55,13 +56,18 @@ class TestAuthBasic(base.IronicLibTestCase): app = mock.Mock() start_response = mock.Mock() middleware = auth_basic.BasicAuthMiddleware(app, auth_file) - env = {} + env = {'REQUEST_METHOD': 'GET'} - middleware(env, start_response) + body = middleware(env, start_response) + decoded = json.loads(body[0].decode()) + self.assertEqual({'error': {'message': 'Authorization required', + 'code': 401}}, decoded) start_response.assert_called_once_with( - '401 Authorization required', - [('WWW-Authenticate', 'Basic realm="Baremetal API"')] + '401 Unauthorized', + [('WWW-Authenticate', 'Basic realm="Baremetal API"'), + ('Content-Type', 'application/json'), + ('Content-Length', str(len(body[0])))] ) app.assert_not_called() diff --git a/requirements.txt b/requirements.txt index 53d867c0..84b9647e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -13,3 +13,4 @@ requests>=2.14.2 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 zeroconf>=0.24.0 # LGPL bcrypt>=3.1.3 # Apache-2.0 +WebOb>=1.7.1 # MIT