Browse Source

Add public api support to basic auth

This change implements an alternative middleware which supports the
same deferred auth mechanism as the keystone auth middleware.

When auth fails the header X-Identity-Status is set to Invalid, which
only becomes an Unauthorized response when the path is not public.

Without this change, the paths /, /v1 and /v1/continue
incorrectly require authentication when using basic auth.

Change-Id: I780151870f851ad5dcd45610aacedcca23607a71
Story: 2007656
Task: 39826
changes/06/742306/2
Steve Baker 2 years ago
parent
commit
92c64226f4
  1. 8
      ironic_inspector/test/unit/test_main.py
  2. 44
      ironic_inspector/utils.py
  3. 5
      releasenotes/notes/http-basic-public-api-2cf0e206bea4b34e.yaml

8
ironic_inspector/test/unit/test_main.py

@ -158,6 +158,14 @@ class TestBasicAuthApiIntrospect(TestApiIntrospect):
headers=self.headers)
self.assertEqual(401, res.status_code)
def test_unauthenticated_public_api(self):
res = self.app.get('/')
self.assertEqual(200, res.status_code)
res = self.app.get('/v1')
self.assertEqual(200, res.status_code)
res = self.app.get('/v1/introspection')
self.assertEqual(401, res.status_code)
class TestApiContinue(BaseAPITest):
def test_continue(self):

44
ironic_inspector/utils.py

@ -16,12 +16,14 @@ import logging as pylog
import futurist
from ironic_lib import auth_basic
from ironic_lib import exception
from keystonemiddleware import auth_token
from openstack.baremetal.v1 import node
from oslo_config import cfg
from oslo_log import log
from oslo_middleware import cors as cors_middleware
import pytz
import webob
from ironic_inspector.common.i18n import _
from ironic_inspector import policy
@ -169,6 +171,42 @@ class NoAvailableConductor(Error):
super(NoAvailableConductor, self).__init__(msg, code=503, **kwargs)
class DeferredBasicAuthMiddleware(object):
"""Middleware which sets X-Identity-Status header based on authentication
"""
def __init__(self, app, auth_file):
self.app = app
self.auth_file = auth_file
auth_basic.validate_auth_file(auth_file)
@webob.dec.wsgify()
def __call__(self, req):
headers = req.headers
try:
if 'Authorization' not in headers:
auth_basic.unauthorized()
token = auth_basic.parse_header({
'HTTP_AUTHORIZATION': headers.get('Authorization')
})
username, password = auth_basic.parse_token(token)
headers.update(
auth_basic.authenticate(self.auth_file, username, password))
headers['X-Identity-Status'] = 'Confirmed'
except exception.Unauthorized:
headers['X-Identity-Status'] = 'Invalid'
except exception.IronicException as e:
status = '%s %s' % (int(e.code), str(e))
resp = webob.Response(status=status)
resp.headers.update(e.headers)
return resp
return req.get_response(self.app)
def executor():
"""Return the current futures executor."""
global _EXECUTOR
@ -193,7 +231,7 @@ def add_basic_auth_middleware(app):
:param app: application.
"""
app.wsgi_app = auth_basic.BasicAuthMiddleware(
app.wsgi_app = DeferredBasicAuthMiddleware(
app.wsgi_app, CONF.http_basic_auth_user_file)
@ -216,11 +254,13 @@ def check_auth(request, rule=None, target=None):
:param target: dict-like structure to check rule against
:raises: utils.Error if access is denied
"""
if CONF.auth_strategy != 'keystone':
if CONF.auth_strategy not in ('keystone', 'http_basic'):
return
if not request.context.is_public_api:
if request.headers.get('X-Identity-Status', '').lower() == 'invalid':
raise Error(_('Authentication required'), code=401)
if CONF.auth_strategy != 'keystone':
return
target = {} if target is None else target
if not policy.authorize(rule, target, request.context.to_policy_values()):
raise Error(_("Access denied by policy"), code=403)

5
releasenotes/notes/http-basic-public-api-2cf0e206bea4b34e.yaml

@ -0,0 +1,5 @@
---
fixes:
- |
Using auth_strategy=http_basic incorrectly required authentication for
public paths such as / and /v1. These paths are now public.
Loading…
Cancel
Save