Move the auth plugins abstract base class out of core

This patch moves the auth plugins abstract base class out of core and
into plugins/base.py

This removes dependencies where backend code references code in the
core. The reasoning being that the core should know about the backend
interface, but the backends should not know anything about the core
(separation of concerns). And part of the risk here is a potential for
circular dependencies.

Partial-Bug: #1563101

Change-Id: I4413ef01523d02c30af97e306069229252cb4971
This commit is contained in:
Ronald De Rose 2016-07-07 16:19:28 +00:00
parent 5f7377f5ab
commit 5d707d510d
11 changed files with 118 additions and 92 deletions

View File

@ -325,7 +325,7 @@ How to Implement an Authentication Plugin
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
All authentication plugins must extend the
:class:`keystone.auth.core.AuthMethodHandler` class and implement the
:class:`keystone.auth.plugins.base.AuthMethodHandler` class and implement the
``authenticate()`` method. The ``authenticate()`` method expects the following
parameters.

View File

@ -13,4 +13,3 @@
# under the License.
from keystone.auth import controllers # noqa
from keystone.auth.core import * # noqa

View File

@ -12,83 +12,15 @@
# License for the specific language governing permissions and limitations
# under the License.
import abc
from oslo_log import versionutils
import six
from keystone import exception
from keystone.auth.plugins import base
@six.add_metaclass(abc.ABCMeta)
class AuthMethodHandler(object):
"""Abstract base class for an authentication plugin."""
def __init__(self):
pass
@abc.abstractmethod
def authenticate(self, context, auth_payload, auth_context):
"""Authenticate user and return an authentication context.
:param context: keystone's request context
:param auth_payload: the content of the authentication for a given
method
:param auth_context: user authentication context, a dictionary shared
by all plugins. It contains "method_names" and
"extras" by default. "method_names" is a list and
"extras" is a dictionary.
If successful, plugin must set ``user_id`` in ``auth_context``.
``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
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::
{
"extras": {},
"methods": [
"password",
"token"
],
"user_id": "abc123"
}
Plugins are invoked in the order in which they are specified in the
``methods`` attribute of the ``identity`` object. For example,
``custom-plugin`` is invoked before ``password``, which is invoked
before ``token`` in the following authentication request::
{
"auth": {
"identity": {
"custom-plugin": {
"custom-data": "sdfdfsfsfsdfsf"
},
"methods": [
"custom-plugin",
"password",
"token"
],
"password": {
"user": {
"id": "s23sfad1",
"password": "secrete"
}
},
"token": {
"id": "sdfafasdfsfasfasdfds"
}
}
}
}
: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.
:raises keystone.exception.Unauthorized: for authentication failure
"""
raise exception.Unauthorized()
@versionutils.deprecated(
versionutils.deprecated.NEWTON,
what='keystone.auth.AuthMethodHandler',
in_favor_of='keystone.auth.plugins.base.AuthMethodHandler',
remove_in=+1)
class AuthMethodHandler(base.AuthMethodHandler):
pass

View File

@ -0,0 +1,94 @@
# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
from keystone import exception
@six.add_metaclass(abc.ABCMeta)
class AuthMethodHandler(object):
"""Abstract base class for an authentication plugin."""
def __init__(self):
pass
@abc.abstractmethod
def authenticate(self, context, auth_payload, auth_context):
"""Authenticate user and return an authentication context.
:param context: keystone's request context
:param auth_payload: the content of the authentication for a given
method
:param auth_context: user authentication context, a dictionary shared
by all plugins. It contains "method_names" and
"extras" by default. "method_names" is a list and
"extras" is a dictionary.
If successful, plugin must set ``user_id`` in ``auth_context``.
``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
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::
{
"extras": {},
"methods": [
"password",
"token"
],
"user_id": "abc123"
}
Plugins are invoked in the order in which they are specified in the
``methods`` attribute of the ``identity`` object. For example,
``custom-plugin`` is invoked before ``password``, which is invoked
before ``token`` in the following authentication request::
{
"auth": {
"identity": {
"custom-plugin": {
"custom-data": "sdfdfsfsfsdfsf"
},
"methods": [
"custom-plugin",
"password",
"token"
],
"password": {
"user": {
"id": "s23sfad1",
"password": "secrete"
}
},
"token": {
"id": "sdfafasdfsfasfasdfds"
}
}
}
}
: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.
:raises keystone.exception.Unauthorized: for authentication failure
"""
raise exception.Unauthorized()

View File

@ -18,7 +18,7 @@ import abc
import six
from keystone import auth
from keystone.auth.plugins import base
from keystone.common import dependency
import keystone.conf
from keystone import exception
@ -29,7 +29,7 @@ CONF = keystone.conf.CONF
@six.add_metaclass(abc.ABCMeta)
class Base(auth.AuthMethodHandler):
class Base(base.AuthMethodHandler):
def authenticate(self, request, auth_info, auth_context):
"""Use REMOTE_USER to look up the user in the identity backend.

View File

@ -15,8 +15,8 @@ import functools
from pycadf import cadftaxonomy as taxonomy
from six.moves.urllib import parse
from keystone import auth
from keystone.auth import plugins as auth_plugins
from keystone.auth.plugins import base
from keystone.common import dependency
from keystone import exception
from keystone.federation import constants as federation_constants
@ -31,7 +31,7 @@ METHOD_NAME = 'mapped'
@dependency.requires('federation_api', 'identity_api',
'resource_api', 'token_provider_api')
class Mapped(auth.AuthMethodHandler):
class Mapped(base.AuthMethodHandler):
def _get_token_ref(self, auth_payload):
token_id = auth_payload['id']

View File

@ -14,7 +14,7 @@
from oslo_utils import timeutils
from keystone import auth
from keystone.auth.plugins import base
from keystone.common import controller
from keystone.common import dependency
from keystone import exception
@ -24,7 +24,7 @@ from keystone.oauth1 import validator
@dependency.requires('oauth_api')
class OAuth(auth.AuthMethodHandler):
class OAuth(base.AuthMethodHandler):
def authenticate(self, request, auth_info, auth_context):
"""Turn a signed request with an access key into a keystone token."""
oauth_headers = oauth.get_oauth_headers(request.headers)

View File

@ -12,8 +12,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from keystone import auth
from keystone.auth import plugins as auth_plugins
from keystone.auth.plugins import base
from keystone.common import dependency
from keystone import exception
from keystone.i18n import _
@ -23,7 +23,7 @@ METHOD_NAME = 'password'
@dependency.requires('identity_api')
class Password(auth.AuthMethodHandler):
class Password(base.AuthMethodHandler):
def authenticate(self, request, auth_payload, auth_context):
"""Try to authenticate against the identity backend."""

View File

@ -15,7 +15,7 @@
from oslo_log import log
import six
from keystone import auth
from keystone.auth.plugins import base
from keystone.auth.plugins import mapped
from keystone.common import dependency
from keystone.common import wsgi
@ -31,7 +31,7 @@ CONF = keystone.conf.CONF
@dependency.requires('federation_api', 'identity_api', 'token_provider_api')
class Token(auth.AuthMethodHandler):
class Token(base.AuthMethodHandler):
def _get_token_ref(self, auth_payload):
token_id = auth_payload['id']

View File

@ -31,8 +31,8 @@ from oslo_log import log
from oslo_utils import timeutils
import six
from keystone import auth
from keystone.auth import plugins
from keystone.auth.plugins import base
from keystone.common import dependency
from keystone import exception
from keystone.i18n import _
@ -66,7 +66,7 @@ def _generate_totp_passcode(secret):
@dependency.requires('credential_api')
class TOTP(auth.AuthMethodHandler):
class TOTP(base.AuthMethodHandler):
def authenticate(self, request, auth_payload, auth_context):
"""Try to authenticate using TOTP."""

View File

@ -17,6 +17,7 @@ import uuid
import mock
from keystone import auth
from keystone.auth.plugins import base
from keystone import exception
from keystone.tests import unit
from keystone.tests.unit.ksfixtures import auth_plugins
@ -32,7 +33,7 @@ EXPECTED_RESPONSE = uuid.uuid4().hex
DEMO_USER_ID = uuid.uuid4().hex
class SimpleChallengeResponse(auth.AuthMethodHandler):
class SimpleChallengeResponse(base.AuthMethodHandler):
def authenticate(self, context, auth_payload, user_context):
if 'response' in auth_payload:
if auth_payload['response'] != EXPECTED_RESPONSE: