Allow anonymous access for health and versions

Updates the context middleware to check for authentication upon routing
the request instead of pre-routing. Adds a list to the base resource
that means all requests must be authenticated by default, but allows for
child resources to specify the HTTP methods that should not be authenticated.

Change-Id: I369087430b403355ce551384b8d798465738b684
This commit is contained in:
Bryan Strassner 2017-11-10 18:21:13 -06:00
parent 59b7d7f7b1
commit 8cc63164d2
4 changed files with 46 additions and 14 deletions

View File

@ -20,6 +20,15 @@ from deckhand import context
class BaseResource(object):
"""Base resource class for implementing API resources."""
# Shadowing no_authentication_methods and supplying the HTTP method as a
# value (e.g. 'GET') allows that method to run without authentication. By
# default all require authentication.
# Warning: This method of skipping authentication is applied to a HTTP
# method, which ultimately maps to a resource's on_ methods.
# If a method such as on_get were to service both a list and a single
# response, both would share the skipped authentication.
no_authentication_methods = []
def on_options(self, req, resp):
self_attrs = dir(self)

View File

@ -18,9 +18,14 @@ from deckhand.control.base import BaseResource
class HealthResource(BaseResource):
"""A resource that allows other UCP components to access and validate
"""Basic health check for Deckhand
A resource that allows other UCP components to access and validate
Deckhand's health status. The response must be returned within 30 seconds
for Deckhand to be deemed "healthy".
Unauthenticated GET.
"""
no_authentication_methods = ['GET']
def on_get(self, req, resp):
resp.status = falcon.HTTP_204

View File

@ -29,25 +29,37 @@ LOG = logging.getLogger(__name__)
class ContextMiddleware(object):
def process_request(self, req, resp):
"""Convert authentication information into a request context.
def process_resource(self, req, resp, resource, params):
"""Handle the authentication needs of the routed request.
Generate a ``deckhand.context.RequestContext`` object from the
available authentication headers and store in the ``context`` attribute
of the ``req`` object.
:param req: ``falcon`` request object that will be given the context
object.
:param req: ``falcon`` request object that will be examined for method
:param resource: ``falcon`` resource class that will be examined for
authentication needs by looking at the no_authentication_methods
list of http methods. By default, this will assume that all
requests need authentication unless noted in this array.
Note that this does not bypass any authorization checks, which will
fail if the user is not authenticated.
:raises: falcon.HTTPUnauthorized: when value of the
'X-Identity-Status' header is not 'Confirmed' and anonymous access
is disallowed.
"""
if req.headers.get('X-IDENTITY-STATUS') == 'Confirmed':
req.context = deckhand.context.RequestContext.from_environ(req.env)
elif CONF.allow_anonymous_access:
req.context = deckhand.context.get_context()
authentication_required = True
try:
if req.method in resource.no_authentication_methods:
authentication_required = False
except AttributeError:
# assume that authentication is required.
pass
if authentication_required:
if req.headers.get('X-IDENTITY-STATUS') == 'Confirmed':
req.context = deckhand.context.RequestContext.from_environ(
req.env)
elif CONF.allow_anonymous_access:
req.context = deckhand.context.get_context()
else:
raise falcon.HTTPUnauthorized()
else:
raise falcon.HTTPUnauthorized()
req.context = deckhand.context.RequestContext.from_environ(req.env)
class HookableMiddlewareMixin(object):

View File

@ -18,6 +18,12 @@ from deckhand.control import base as api_base
class VersionsResource(api_base.BaseResource):
"""Versions resource
Returns the list of supported versions of the Deckhand API.
Unauthenticated GET.
"""
no_authentication_methods = ['GET']
def on_get(self, req, resp):
resp.body = {