Merge "Auth Method Handlers now return a response object always"
This commit is contained in:
commit
d4a1bbda0b
keystone
@ -433,8 +433,10 @@ class Auth(controller.V3Controller):
|
||||
self._check_and_set_default_scoping(auth_info, auth_context)
|
||||
(domain_id, project_id, trust, unscoped) = auth_info.get_scope()
|
||||
|
||||
method_names = auth_info.get_method_names()
|
||||
method_names += auth_context.get('method_names', [])
|
||||
# NOTE(notmorgan): only methods that actually run and succeed will
|
||||
# be in the auth_context['method_names'] list. Do not blindly take
|
||||
# the values from auth_info, look at the authoritative values.
|
||||
method_names = auth_context.get('method_names', [])
|
||||
# make sure the list is unique
|
||||
method_names = list(set(method_names))
|
||||
expires_at = auth_context.get('expires_at')
|
||||
@ -527,6 +529,7 @@ class Auth(controller.V3Controller):
|
||||
# of the attributes set by the plugins. This check to ensure
|
||||
# `auth_context` is an instance of AuthContext is extra insurance and
|
||||
# will prevent regressions.
|
||||
|
||||
if not isinstance(auth_context, AuthContext):
|
||||
LOG.error(
|
||||
_LE('`auth_context` passed to the Auth controller '
|
||||
@ -542,9 +545,15 @@ class Auth(controller.V3Controller):
|
||||
if request.remote_user:
|
||||
try:
|
||||
external = get_auth_method('external')
|
||||
external.authenticate(request,
|
||||
auth_info,
|
||||
auth_context)
|
||||
resp = external.authenticate(request,
|
||||
auth_info,
|
||||
auth_context)
|
||||
if resp and resp.status:
|
||||
# NOTE(notmorgan): ``external`` plugin cannot be multi-step
|
||||
# it is either a plain success/fail.
|
||||
auth_context.setdefault(
|
||||
'method_names', []).insert(0, 'external')
|
||||
|
||||
except exception.AuthMethodNotSupported:
|
||||
# This will happen there is no 'external' plugin registered
|
||||
# and the container is performing authentication.
|
||||
@ -567,8 +576,12 @@ class Auth(controller.V3Controller):
|
||||
auth_info.get_method_data(method_name),
|
||||
auth_context)
|
||||
if resp:
|
||||
auth_response['methods'].append(method_name)
|
||||
auth_response[method_name] = resp
|
||||
if resp.status:
|
||||
auth_context.setdefault(
|
||||
'method_names', []).insert(0, method_name)
|
||||
elif resp.response_body:
|
||||
auth_response['methods'].append(method_name)
|
||||
auth_response[method_name] = resp.response_body
|
||||
|
||||
if auth_response["methods"]:
|
||||
# authentication continuation required
|
||||
|
@ -13,12 +13,17 @@
|
||||
# under the License.
|
||||
|
||||
import abc
|
||||
import collections
|
||||
|
||||
import six
|
||||
|
||||
from keystone import exception
|
||||
|
||||
|
||||
AuthHandlerResponse = collections.namedtuple('AuthHandlerResponse',
|
||||
'status, response_body')
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class AuthMethodHandler(object):
|
||||
"""Abstract base class for an authentication plugin."""
|
||||
@ -45,7 +50,8 @@ class AuthMethodHandler(object):
|
||||
``method_name`` is used to convey any additional authentication methods
|
||||
in case authentication is for re-scoping. For example, if the
|
||||
authentication is for re-scoping, plugin must append the previous
|
||||
method names into ``method_names``. Also, plugin may add any additional
|
||||
method names into ``method_names``; NOTE: This behavior is exclusive
|
||||
to the re-scope type action. Also, plugin may add any additional
|
||||
information into ``extras``. Anything in ``extras`` will be conveyed in
|
||||
the token's ``extras`` attribute. Here's an example of ``auth_context``
|
||||
on successful authentication::
|
||||
@ -88,10 +94,11 @@ class AuthMethodHandler(object):
|
||||
}
|
||||
}
|
||||
|
||||
:returns: None if authentication is successful.
|
||||
Authentication payload in the form of a dictionary for the
|
||||
next authentication step if this is a multi step
|
||||
authentication.
|
||||
:returns: AuthHandlerResponse with status set to ``True`` if auth was
|
||||
successful. If `status` is ``False`` and this is a multi-step
|
||||
auth, the ``response_body`` can be in a form of a dict for
|
||||
the next step in authentication.
|
||||
|
||||
:raises keystone.exception.Unauthorized: for authentication failure
|
||||
"""
|
||||
raise exception.Unauthorized()
|
||||
|
@ -52,6 +52,8 @@ class Base(base.AuthMethodHandler):
|
||||
if 'kerberos' in CONF.token.bind and auth_type == 'negotiate':
|
||||
auth_context['bind']['kerberos'] = user_ref['name']
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
||||
@abc.abstractmethod
|
||||
def _authenticate(self, request):
|
||||
"""Look up the user in the identity backend.
|
||||
|
@ -73,6 +73,8 @@ class Mapped(base.AuthMethodHandler):
|
||||
self.assignment_api,
|
||||
self.role_api)
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
||||
|
||||
def handle_scoped_token(request, auth_context, token_ref,
|
||||
federation_api, identity_api):
|
||||
|
@ -62,3 +62,5 @@ class OAuth(base.AuthMethodHandler):
|
||||
auth_context['user_id'] = acc_token['authorizing_user_id']
|
||||
auth_context['access_token_id'] = access_token_id
|
||||
auth_context['project_id'] = acc_token['project_id']
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
@ -40,3 +40,5 @@ class Password(base.AuthMethodHandler):
|
||||
raise exception.Unauthorized(msg)
|
||||
|
||||
auth_context['user_id'] = user_info.user_id
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
@ -51,6 +51,8 @@ class Token(base.AuthMethodHandler):
|
||||
else:
|
||||
token_authenticate(request, auth_context, token_ref)
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
||||
|
||||
def token_authenticate(request, user_context, token_ref):
|
||||
try:
|
||||
@ -98,7 +100,11 @@ def token_authenticate(request, user_context, token_ref):
|
||||
# TODO(morganfainberg: determine if token 'extras' can be removed
|
||||
# from the user_context
|
||||
user_context['extras'].update(token_ref.get('extras', {}))
|
||||
user_context['method_names'].extend(token_ref.methods)
|
||||
# NOTE(notmorgan): The Token auth method is *very* special and sets the
|
||||
# previous values to the method_names. This is because it can be used
|
||||
# for re-scoping and we want to maintain the values. Most
|
||||
# AuthMethodHandlers do no such thing and this is not required.
|
||||
user_context.setdefault('method_names', []).extend(token_ref.methods)
|
||||
|
||||
except AssertionError as e:
|
||||
LOG.error(six.text_type(e))
|
||||
|
@ -97,3 +97,5 @@ class TOTP(base.AuthMethodHandler):
|
||||
raise exception.Unauthorized(msg)
|
||||
|
||||
auth_context['user_id'] = user_info.user_id
|
||||
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
|
@ -34,13 +34,18 @@ DEMO_USER_ID = uuid.uuid4().hex
|
||||
|
||||
|
||||
class SimpleChallengeResponse(base.AuthMethodHandler):
|
||||
def authenticate(self, context, auth_payload, user_context):
|
||||
def authenticate(self, context, auth_payload, auth_context):
|
||||
if 'response' in auth_payload:
|
||||
if auth_payload['response'] != EXPECTED_RESPONSE:
|
||||
raise exception.Unauthorized('Wrong answer')
|
||||
user_context['user_id'] = DEMO_USER_ID
|
||||
|
||||
auth_context['user_id'] = DEMO_USER_ID
|
||||
return base.AuthHandlerResponse(status=True, response_body=None)
|
||||
else:
|
||||
return {"challenge": "What's the name of your high school?"}
|
||||
return base.AuthHandlerResponse(
|
||||
status=False,
|
||||
response_body={
|
||||
"challenge": "What's the name of your high school?"})
|
||||
|
||||
|
||||
class TestAuthPlugin(unit.SQLDriverOverrides, unit.TestCase):
|
||||
|
Loading…
x
Reference in New Issue
Block a user