Refactor project and domain scoping
Moves the project and domain logic into their own functions inside the plugin object. Change-Id: I3aa026364443220c9b3fa38ec306fed4d9e878cc
This commit is contained in:
parent
2475fd33d9
commit
597e6d79b4
|
@ -20,7 +20,6 @@ import pytz
|
|||
from django.conf import settings
|
||||
from django.utils.module_loading import import_string # noqa
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
|
||||
from openstack_auth import exceptions
|
||||
from openstack_auth import user as auth_user
|
||||
|
@ -110,51 +109,23 @@ class KeystoneBackend(object):
|
|||
'configuration error that should be addressed.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
session = utils.get_session()
|
||||
keystone_client_class = utils.get_keystone_client().Client
|
||||
|
||||
try:
|
||||
unscoped_auth_ref = unscoped_auth.get_access(session)
|
||||
except keystone_exceptions.ConnectFailure as exc:
|
||||
LOG.error(str(exc))
|
||||
msg = _('Unable to establish connection to keystone endpoint.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
except (keystone_exceptions.Unauthorized,
|
||||
keystone_exceptions.Forbidden,
|
||||
keystone_exceptions.NotFound) as exc:
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(_('Invalid credentials.'))
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure) as exc:
|
||||
msg = _("An error occurred authenticating. "
|
||||
"Please try again later.")
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
# the recent project id a user might have set in a cookie
|
||||
recent_project = None
|
||||
request = kwargs.get('request')
|
||||
if request:
|
||||
# Grab recent_project found in the cookie, try to scope
|
||||
# to the last project used.
|
||||
recent_project = request.COOKIES.get('recent_project')
|
||||
unscoped_auth_ref = plugin.get_access_info(unscoped_auth)
|
||||
|
||||
# Check expiry for our unscoped auth ref.
|
||||
self.check_auth_expiry(unscoped_auth_ref)
|
||||
|
||||
# domain support can require domain scoped tokens to perform
|
||||
# identity operations depending on the policy files being used
|
||||
# for keystone.
|
||||
domain_auth = None
|
||||
domain_auth_ref = None
|
||||
if utils.get_keystone_version() >= 3 and 'user_domain_name' in kwargs:
|
||||
try:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
domain_auth = utils.get_token_auth_plugin(
|
||||
auth_url,
|
||||
token,
|
||||
domain_name=kwargs['user_domain_name'])
|
||||
domain_auth_ref = domain_auth.get_access(session)
|
||||
except Exception:
|
||||
LOG.debug('Error getting domain scoped token.', exc_info=True)
|
||||
|
||||
projects = plugin.list_projects(session,
|
||||
unscoped_auth,
|
||||
unscoped_auth_ref)
|
||||
# Attempt to scope only to enabled projects
|
||||
projects = [project for project in projects if project.enabled]
|
||||
domain_name = kwargs.get('user_domain_name', None)
|
||||
domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth(
|
||||
unscoped_auth, unscoped_auth_ref, domain_name)
|
||||
scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth(
|
||||
unscoped_auth, unscoped_auth_ref, recent_project=recent_project)
|
||||
|
||||
# Abort if there are no projects for this user and a valid domain
|
||||
# token has not been obtained
|
||||
|
@ -171,54 +142,17 @@ class KeystoneBackend(object):
|
|||
# token)
|
||||
# 3) user cannot obtain a domain scoped token, but can
|
||||
# obtain a project scoped token
|
||||
if not projects and not domain_auth_ref:
|
||||
if not scoped_auth_ref and domain_auth_ref:
|
||||
# if the user can't obtain a project scoped token, set the scoped
|
||||
# token to be the domain token, if valid
|
||||
scoped_auth = domain_auth
|
||||
scoped_auth_ref = domain_auth_ref
|
||||
elif not scoped_auth_ref and not domain_auth_ref:
|
||||
msg = _('You are not authorized for any projects.')
|
||||
if utils.get_keystone_version() >= 3:
|
||||
msg = _('You are not authorized for any projects or domains.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
# the recent project id a user might have set in a cookie
|
||||
recent_project = None
|
||||
request = kwargs.get('request')
|
||||
|
||||
if request:
|
||||
# Grab recent_project found in the cookie, try to scope
|
||||
# to the last project used.
|
||||
recent_project = request.COOKIES.get('recent_project')
|
||||
|
||||
# if a most recent project was found, try using it first
|
||||
if recent_project:
|
||||
for pos, project in enumerate(projects):
|
||||
if project.id == recent_project:
|
||||
# move recent project to the beginning
|
||||
projects.pop(pos)
|
||||
projects.insert(0, project)
|
||||
break
|
||||
|
||||
for project in projects:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
scoped_auth = utils.get_token_auth_plugin(auth_url,
|
||||
token=token,
|
||||
project_id=project.id)
|
||||
|
||||
try:
|
||||
scoped_auth_ref = scoped_auth.get_access(session)
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
pass
|
||||
else:
|
||||
break
|
||||
else:
|
||||
# if the user can't obtain a project scoped token, set the scoped
|
||||
# token to be the domain token, if valid
|
||||
if domain_auth_ref:
|
||||
scoped_auth = domain_auth
|
||||
scoped_auth_ref = domain_auth_ref
|
||||
else:
|
||||
# if no domain or project token for user, abort
|
||||
msg = _("Unable to authenticate to any available projects.")
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
# Check expiry for our new scoped token.
|
||||
self.check_auth_expiry(scoped_auth_ref)
|
||||
|
||||
|
@ -276,6 +210,8 @@ class KeystoneBackend(object):
|
|||
session_time = min(timeout, int(token_life.total_seconds()))
|
||||
request.session.set_expiry(session_time)
|
||||
|
||||
keystone_client_class = utils.get_keystone_client().Client
|
||||
session = utils.get_session()
|
||||
scoped_client = keystone_client_class(session=session,
|
||||
auth=scoped_auth)
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
# under the License.
|
||||
|
||||
import abc
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from keystoneauth1 import exceptions as keystone_exceptions
|
||||
|
@ -21,6 +22,7 @@ import six
|
|||
from openstack_auth import exceptions
|
||||
from openstack_auth import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
__all__ = ['BasePlugin']
|
||||
|
||||
|
||||
|
@ -95,3 +97,114 @@ class BasePlugin(object):
|
|||
keystone_exceptions.AuthorizationFailure):
|
||||
msg = _('Unable to retrieve authorized projects.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
|
||||
def get_access_info(self, keystone_auth):
|
||||
"""Get the access info from an unscoped auth
|
||||
|
||||
This function provides the base functionality that the
|
||||
plugins will use to authenticate and get the access info object.
|
||||
|
||||
:param keystone_auth: keystoneauth1 identity plugin
|
||||
:raises: exceptions.KeystoneAuthException on auth failure
|
||||
:returns: keystoneclient.access.AccessInfo
|
||||
"""
|
||||
session = utils.get_session()
|
||||
|
||||
try:
|
||||
unscoped_auth_ref = keystone_auth.get_access(session)
|
||||
except keystone_exceptions.ConnectFailure as exc:
|
||||
LOG.error(str(exc))
|
||||
msg = _('Unable to establish connection to keystone endpoint.')
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
except (keystone_exceptions.Unauthorized,
|
||||
keystone_exceptions.Forbidden,
|
||||
keystone_exceptions.NotFound) as exc:
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(_('Invalid credentials.'))
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure) as exc:
|
||||
msg = _("An error occurred authenticating. "
|
||||
"Please try again later.")
|
||||
LOG.debug(str(exc))
|
||||
raise exceptions.KeystoneAuthException(msg)
|
||||
return unscoped_auth_ref
|
||||
|
||||
def get_project_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
|
||||
recent_project=None):
|
||||
"""Get the project scoped keystone auth and access info
|
||||
|
||||
This function returns a project scoped keystone token plugin
|
||||
and AccessInfo object.
|
||||
|
||||
:param unscoped_auth: keystone auth plugin
|
||||
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
|
||||
:param recent_project: project that we should try to scope to
|
||||
:return: keystone token auth plugin, AccessInfo object
|
||||
"""
|
||||
auth_url = unscoped_auth.auth_url
|
||||
session = utils.get_session()
|
||||
|
||||
projects = self.list_projects(
|
||||
session, unscoped_auth, unscoped_auth_ref)
|
||||
# Attempt to scope only to enabled projects
|
||||
projects = [project for project in projects if project.enabled]
|
||||
|
||||
# if a most recent project was found, try using it first
|
||||
if recent_project:
|
||||
for pos, project in enumerate(projects):
|
||||
if project.id == recent_project:
|
||||
# move recent project to the beginning
|
||||
projects.pop(pos)
|
||||
projects.insert(0, project)
|
||||
break
|
||||
|
||||
scoped_auth = None
|
||||
scoped_auth_ref = None
|
||||
for project in projects:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
scoped_auth = utils.get_token_auth_plugin(auth_url,
|
||||
token=token,
|
||||
project_id=project.id)
|
||||
try:
|
||||
scoped_auth_ref = scoped_auth.get_access(session)
|
||||
except (keystone_exceptions.ClientException,
|
||||
keystone_exceptions.AuthorizationFailure):
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
return scoped_auth, scoped_auth_ref
|
||||
|
||||
def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref,
|
||||
domain_name=None):
|
||||
"""Get the domain scoped keystone auth and access info
|
||||
|
||||
This function returns a domain scoped keystone token plugin
|
||||
and AccessInfo object.
|
||||
|
||||
:param unscoped_auth: keystone auth plugin
|
||||
:param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None.
|
||||
:param domain_name: domain that we should try to scope to
|
||||
:return: keystone token auth plugin, AccessInfo object
|
||||
"""
|
||||
session = utils.get_session()
|
||||
auth_url = unscoped_auth.auth_url
|
||||
|
||||
if not domain_name or utils.get_keystone_version() < 3:
|
||||
return None, None
|
||||
|
||||
# domain support can require domain scoped tokens to perform
|
||||
# identity operations depending on the policy files being used
|
||||
# for keystone.
|
||||
domain_auth = None
|
||||
domain_auth_ref = None
|
||||
try:
|
||||
token = unscoped_auth_ref.auth_token
|
||||
domain_auth = utils.get_token_auth_plugin(
|
||||
auth_url,
|
||||
token,
|
||||
domain_name=domain_name)
|
||||
domain_auth_ref = domain_auth.get_access(session)
|
||||
except Exception:
|
||||
LOG.debug('Error getting domain scoped token.', exc_info=True)
|
||||
return domain_auth, domain_auth_ref
|
||||
|
|
|
@ -55,6 +55,7 @@ class OpenStackAuthTestsMixin(object):
|
|||
plugin = self._create_password_auth()
|
||||
plugin.get_access(mox.IsA(session.Session)). \
|
||||
AndReturn(self.data.unscoped_access_info)
|
||||
plugin.auth_url = settings.OPENSTACK_KEYSTONE_URL
|
||||
return self.ks_client_module.Client(session=mox.IsA(session.Session),
|
||||
auth=plugin)
|
||||
|
||||
|
@ -810,6 +811,7 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
|
|||
token=unscoped.auth_token,
|
||||
url=settings.OPENSTACK_KEYSTONE_URL)
|
||||
plugin.get_access(mox.IsA(session.Session)).AndReturn(unscoped)
|
||||
plugin.auth_url = settings.OPENSTACK_KEYSTONE_URL
|
||||
|
||||
return self.ks_client_module.Client(session=mox.IsA(session.Session),
|
||||
auth=plugin)
|
||||
|
|
Loading…
Reference in New Issue