Merge "Add K2K Auth Dropdown"

This commit is contained in:
Jenkins 2017-01-19 22:35:20 +00:00 committed by Gerrit Code Review
commit ea208c774f
9 changed files with 544 additions and 45 deletions

View File

@ -190,6 +190,9 @@ class KeystoneBackend(object):
services_region=region_name)
if request is not None:
# if no k2k providers exist then the function returns quickly
utils.store_initial_k2k_session(auth_url, request, scoped_auth_ref,
unscoped_auth_ref)
request.session['unscoped_token'] = unscoped_token
if domain_auth_ref:
# check django session engine, if using cookies, this will not

View File

@ -13,8 +13,10 @@
from openstack_auth.plugin.base import * # noqa
from openstack_auth.plugin.password import * # noqa
from openstack_auth.plugin.token import * # noqa
from openstack_auth.plugin.k2k import * # noqa
__all__ = ['BasePlugin',
'PasswordPlugin',
'TokenPlugin']
'TokenPlugin',
'K2KAuthPlugin']

View File

@ -0,0 +1,104 @@
# 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 logging
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from keystoneauth1.identity import v3 as v3_auth
from openstack_auth import exceptions
from openstack_auth.plugin import base
from openstack_auth import utils
LOG = logging.getLogger(__name__)
__all__ = ['K2KAuthPlugin']
class K2KAuthPlugin(base.BasePlugin):
def get_plugin(self, service_provider=None, auth_url=None, plugins=[],
**kwargs):
"""Authenticate using keystone to keystone federation.
This plugin uses other v3 plugins to authenticate a user to a
identity provider in order to authenticate the user to a service
provider
:param service_provider: service provider ID
:param auth_url: Keystone auth url
:param plugins: list of openstack_auth plugins to check
:returns Keystone2Keystone keystone auth plugin
"""
# service_provider being None prevents infinite recursion
if utils.get_keystone_version() < 3 or not service_provider:
return None
keystone_idp_id = getattr(settings, 'KEYSTONE_PROVIDER_IDP_ID',
'localkeystone')
if service_provider == keystone_idp_id:
return None
for plugin in plugins:
unscoped_idp_auth = plugin.get_plugin(plugins=plugins,
auth_url=auth_url, **kwargs)
if unscoped_idp_auth:
break
else:
LOG.debug('Could not find base authentication backend for '
'K2K plugin with the provided credentials.')
return None
idp_exception = None
scoped_idp_auth = None
unscoped_auth_ref = base.BasePlugin.get_access_info(
self, unscoped_idp_auth)
try:
scoped_idp_auth, __ = self.get_project_scoped_auth(
unscoped_idp_auth, unscoped_auth_ref)
except exceptions.KeystoneAuthException as idp_excp:
idp_exception = idp_excp
if not scoped_idp_auth or idp_exception:
msg = 'Identity provider authentication Failed.'
raise exceptions.KeystoneAuthException(msg)
session = utils.get_session()
if scoped_idp_auth.get_sp_auth_url(session, service_provider) is None:
msg = _('Could not find service provider ID on Keystone.')
raise exceptions.KeystoneAuthException(msg)
unscoped_auth = v3_auth.Keystone2Keystone(
base_plugin=scoped_idp_auth,
service_provider=service_provider)
return unscoped_auth
def get_access_info(self, unscoped_auth):
"""Get the access info object
We attempt to get the auth ref. If it fails and if the K2K auth plugin
was being used then we will prepend a message saying that the error was
on the service provider side.
:param: unscoped_auth: Keystone auth plugin for unscoped user
:returns: keystoneclient.access.AccessInfo object
"""
try:
unscoped_auth_ref = base.BasePlugin.get_access_info(
self, unscoped_auth)
except exceptions.KeystoneAuthException as excp:
msg = _('Service provider authentication failed. %s')
raise exceptions.KeystoneAuthException(msg % str(excp))
return unscoped_auth_ref

View File

@ -55,7 +55,8 @@ class TestResponse(requests.Response):
return self._text
def generate_test_data(pki=False):
def generate_test_data(pki=False, service_providers=False,
endpoint='localhost'):
'''Builds a set of test_data data as returned by Keystone V2.'''
test_data = TestDataContainer()
@ -64,19 +65,19 @@ def generate_test_data(pki=False):
'id': uuid.uuid4().hex,
'endpoints': [
{
'url': 'http://admin.localhost:35357/v3',
'url': 'http://admin.%s:35357/v3' % endpoint,
'region': 'RegionOne',
'interface': 'admin',
'id': uuid.uuid4().hex,
},
{
'url': 'http://internal.localhost:5000/v3',
'url': 'http://internal.%s:5000/v3' % endpoint,
'region': 'RegionOne',
'interface': 'internal',
'id': uuid.uuid4().hex
},
{
'url': 'http://public.localhost:5000/v3',
'url': 'http://public.%s:5000/v3' % endpoint,
'region': 'RegionOne',
'interface': 'public',
'id': uuid.uuid4().hex
@ -131,43 +132,43 @@ def generate_test_data(pki=False):
'id': uuid.uuid4().hex,
'endpoints': [
{
'url': ('http://nova-admin.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova-admin.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionOne',
'interface': 'admin',
'id': uuid.uuid4().hex,
},
{
'url': ('http://nova-internal.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova-internal.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionOne',
'interface': 'internal',
'id': uuid.uuid4().hex
},
{
'url': ('http://nova-public.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova-public.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionOne',
'interface': 'public',
'id': uuid.uuid4().hex
},
{
'url': ('http://nova2-admin.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova2-admin.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionTwo',
'interface': 'admin',
'id': uuid.uuid4().hex,
},
{
'url': ('http://nova2-internal.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova2-internal.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionTwo',
'interface': 'internal',
'id': uuid.uuid4().hex
},
{
'url': ('http://nova2-public.localhost:8774/v2.0/%s'
% (project_dict_1['id'])),
'url': ('http://nova2-public.%s:8774/v2.0/%s'
% (endpoint, project_dict_1['id'])),
'region': 'RegionTwo',
'interface': 'public',
'id': uuid.uuid4().hex
@ -218,6 +219,19 @@ def generate_test_data(pki=False):
}
}
sp_list = None
if service_providers:
test_data.sp_auth_url = 'http://service_provider_endp:5000/v3'
test_data.service_provider_id = 'k2kserviceprovider'
# The access info for the identity provider
# should return a list of service providers
sp_list = [
{'auth_url': test_data.sp_auth_url,
'id': test_data.service_provider_id,
'sp_url': 'https://k2kserviceprovider/sp_url'}
]
scoped_token_dict['token']['service_providers'] = sp_list
test_data.scoped_access_info = access.create(
resp=auth_response,
body=scoped_token_dict
@ -264,6 +278,9 @@ def generate_test_data(pki=False):
}
}
if service_providers:
unscoped_token_dict['token']['service_providers'] = sp_list
test_data.unscoped_access_info = access.create(
resp=auth_response,
body=unscoped_token_dict

View File

@ -70,3 +70,5 @@ TEMPLATES = [
'APP_DIRS': True,
},
]
AUTH_USER_MODEL = 'openstack_auth.User'

View File

@ -76,15 +76,18 @@ class OpenStackAuthTestsMixin(object):
plugin.get_access(mox.IsA(session.Session)).AndRaise(exc)
def _mock_scoped_client_for_tenant(self, auth_ref, tenant_id, url=None,
client=True):
client=True, token=None):
if url is None:
url = settings.OPENSTACK_KEYSTONE_URL
if not token:
token = self.data.unscoped_access_info.auth_token
plugin = self._create_token_auth(
tenant_id,
token=self.data.unscoped_access_info.auth_token,
token=token,
url=url)
self.scoped_token_auth = plugin
plugin.get_access(mox.IsA(session.Session)).AndReturn(auth_ref)
if client:
return self.ks_client_module.Client(
@ -98,6 +101,33 @@ class OpenStackAuthTestsMixin(object):
'username': user.name}
class OpenStackAuthFederatedTestsMixin(object):
"""Common functions for federation"""
def _mock_unscoped_federated_list_projects(self, client, projects):
client.federation = self.mox.CreateMockAnything()
client.federation.projects = self.mox.CreateMockAnything()
client.federation.projects.list().AndReturn(projects)
def _mock_unscoped_token_client(self, unscoped, auth_url=None,
client=True):
if not auth_url:
auth_url = settings.OPENSTACK_KEYSTONE_URL
plugin = self._create_token_auth(
None,
token=unscoped.auth_token,
url=auth_url)
plugin.get_access(mox.IsA(session.Session)).AndReturn(unscoped)
plugin.auth_url = auth_url
if client:
return self.ks_client_module.Client(
session=mox.IsA(session.Session),
auth=plugin)
def _mock_federated_client_list_projects(self, unscoped, projects):
client = self._mock_unscoped_token_client(unscoped)
self._mock_unscoped_federated_list_projects(client, projects)
class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase):
def setUp(self):
@ -431,7 +461,9 @@ class OpenStackAuthTestsV2(OpenStackAuthTestsMixin, test.TestCase):
self.assertEqual(tenant_list, expected_tenants)
class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, test.TestCase):
class OpenStackAuthTestsV3(OpenStackAuthTestsMixin,
OpenStackAuthFederatedTestsMixin,
test.TestCase):
def _mock_unscoped_client_list_projects(self, user, projects):
client = self._mock_unscoped_client(user)
@ -532,6 +564,7 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, test.TestCase):
self.mox.StubOutClassWithMocks(v3_auth, 'Token')
self.mox.StubOutClassWithMocks(v3_auth, 'Password')
self.mox.StubOutClassWithMocks(client_v3, 'Client')
self.mox.StubOutClassWithMocks(v3_auth, 'Keystone2Keystone')
def test_login(self):
projects = [self.data.project_one, self.data.project_two]
@ -774,6 +807,234 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, test.TestCase):
def test_switch_region_with_next(self, next=None):
self.test_switch_region(next='/next_url')
def test_switch_keystone_provider_remote_fail(self):
auth_url = settings.OPENSTACK_KEYSTONE_URL
target_provider = 'k2kserviceprovider'
self.data = data_v3.generate_test_data(service_providers=True)
self.sp_data = data_v3.generate_test_data(endpoint='http://sp2')
projects = [self.data.project_one, self.data.project_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
# mock authenticate
self._mock_unscoped_and_domain_list_projects(user, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
# mock switch
plugin = v3_auth.Token(auth_url=auth_url,
token=unscoped.auth_token,
project_id=None,
reauthenticate=False)
plugin.get_access(mox.IsA(session.Session)
).AndReturn(self.data.unscoped_access_info)
plugin.auth_url = auth_url
client = self.ks_client_module.Client(session=mox.IsA(session.Session),
auth=plugin)
self._mock_unscoped_list_projects(client, user, projects)
plugin = self._create_token_auth(
self.data.project_one.id,
token=self.data.unscoped_access_info.auth_token,
url=settings.OPENSTACK_KEYSTONE_URL)
plugin.get_access(mox.IsA(session.Session)).AndReturn(
settings.OPENSTACK_KEYSTONE_URL)
plugin.get_sp_auth_url(
mox.IsA(session.Session), target_provider
).AndReturn('https://k2kserviceprovider/sp_url')
# let the K2K plugin fail when logging in
plugin = v3_auth.Keystone2Keystone(
base_plugin=plugin, service_provider=target_provider)
plugin.get_access(mox.IsA(session.Session)).AndRaise(
keystone_exceptions.AuthorizationFailure)
self.mox.ReplayAll()
# Log in
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Switch
url = reverse('switch_keystone_provider', args=[target_provider])
form_data['keystone_provider'] = target_provider
response = self.client.get(url, form_data, follow=True)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Assert that provider has not changed because of failure
self.assertEqual(self.client.session['keystone_provider_id'],
'localkeystone')
# These should never change
self.assertEqual(self.client.session['k2k_base_unscoped_token'],
unscoped.auth_token)
self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
def test_switch_keystone_provider_remote(self):
auth_url = settings.OPENSTACK_KEYSTONE_URL
target_provider = 'k2kserviceprovider'
self.data = data_v3.generate_test_data(service_providers=True)
self.sp_data = data_v3.generate_test_data(endpoint='http://sp2')
projects = [self.data.project_one, self.data.project_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
# mock authenticate
self._mock_unscoped_and_domain_list_projects(user, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
# mock switch
plugin = v3_auth.Token(auth_url=auth_url,
token=unscoped.auth_token,
project_id=None,
reauthenticate=False)
plugin.get_access(mox.IsA(session.Session)).AndReturn(
self.data.unscoped_access_info)
plugin.auth_url = auth_url
client = self.ks_client_module.Client(session=mox.IsA(session.Session),
auth=plugin)
self._mock_unscoped_list_projects(client, user, projects)
plugin = self._create_token_auth(
self.data.project_one.id,
token=self.data.unscoped_access_info.auth_token,
url=settings.OPENSTACK_KEYSTONE_URL)
plugin.get_access(mox.IsA(session.Session)).AndReturn(
settings.OPENSTACK_KEYSTONE_URL)
plugin.get_sp_auth_url(
mox.IsA(session.Session), target_provider
).AndReturn('https://k2kserviceprovider/sp_url')
plugin = v3_auth.Keystone2Keystone(base_plugin=plugin,
service_provider=target_provider)
plugin.get_access(mox.IsA(session.Session)). \
AndReturn(self.sp_data.unscoped_access_info)
plugin.auth_url = 'http://service_provider_endp:5000/v3'
# mock authenticate for service provider
sp_projects = [self.sp_data.project_one, self.sp_data.project_two]
sp_unscoped = self.sp_data.federated_unscoped_access_info
client = self._mock_unscoped_token_client(sp_unscoped, plugin.auth_url)
self._mock_unscoped_federated_list_projects(client, sp_projects)
self._mock_scoped_client_for_tenant(sp_unscoped,
self.sp_data.project_one.id,
url=plugin.auth_url,
token=sp_unscoped.auth_token)
self.mox.ReplayAll()
# Log in
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Switch
url = reverse('switch_keystone_provider', args=[target_provider])
form_data['keystone_provider'] = target_provider
response = self.client.get(url, form_data, follow=True)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Assert keystone provider has changed
self.assertEqual(self.client.session['keystone_provider_id'],
target_provider)
# These should not change
self.assertEqual(self.client.session['k2k_base_unscoped_token'],
unscoped.auth_token)
self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
def test_switch_keystone_provider_local(self):
auth_url = settings.OPENSTACK_KEYSTONE_URL
self.data = data_v3.generate_test_data(service_providers=True)
keystone_provider = 'localkeystone'
projects = [self.data.project_one, self.data.project_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
# mock authenticate
self._mock_unscoped_and_domain_list_projects(user, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
self._mock_unscoped_token_client(unscoped,
auth_url=auth_url,
client=False)
client = self._mock_unscoped_token_client(unscoped, auth_url)
self._mock_unscoped_list_projects(client, user, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
self.mox.ReplayAll()
# Log in
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Switch
url = reverse('switch_keystone_provider', args=[keystone_provider])
form_data['keystone_provider'] = keystone_provider
response = self.client.get(url, form_data, follow=True)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Assert nothing has changed since we are going from local to local
self.assertEqual(self.client.session['keystone_provider_id'],
keystone_provider)
self.assertEqual(self.client.session['k2k_base_unscoped_token'],
unscoped.auth_token)
self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
def test_switch_keystone_provider_local_fail(self):
auth_url = settings.OPENSTACK_KEYSTONE_URL
self.data = data_v3.generate_test_data(service_providers=True)
keystone_provider = 'localkeystone'
projects = [self.data.project_one, self.data.project_two]
user = self.data.user
unscoped = self.data.unscoped_access_info
form_data = self.get_form_data(user)
# mock authenticate
self._mock_unscoped_and_domain_list_projects(user, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
# Let using the base token for logging in fail
plugin = v3_auth.Token(auth_url=auth_url,
token=unscoped.auth_token,
project_id=None,
reauthenticate=False)
plugin.get_access(mox.IsA(session.Session)). \
AndRaise(keystone_exceptions.AuthorizationFailure)
plugin.auth_url = auth_url
self.mox.ReplayAll()
# Log in
url = reverse('login')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Switch
url = reverse('switch_keystone_provider', args=[keystone_provider])
form_data['keystone_provider'] = keystone_provider
response = self.client.get(url, form_data, follow=True)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
# Assert
self.assertEqual(self.client.session['keystone_provider_id'],
keystone_provider)
self.assertEqual(self.client.session['k2k_base_unscoped_token'],
unscoped.auth_token)
self.assertEqual(self.client.session['k2k_auth_url'], auth_url)
def test_tenant_sorting(self):
projects = [self.data.project_two, self.data.project_one]
expected_projects = [self.data.project_one, self.data.project_two]
@ -791,7 +1052,9 @@ class OpenStackAuthTestsV3(OpenStackAuthTestsMixin, test.TestCase):
self.assertEqual(project_list, expected_projects)
class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin,
OpenStackAuthFederatedTestsMixin,
test.TestCase):
def _create_token_auth(self, project_id=None, token=None, url=None):
if not token:
@ -805,26 +1068,6 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
project_id=project_id,
reauthenticate=False)
def _mock_unscoped_client(self, unscoped):
plugin = self._create_token_auth(
None,
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)
def _mock_unscoped_federated_list_projects(self, client, projects):
client.federation = self.mox.CreateMockAnything()
client.federation.projects = self.mox.CreateMockAnything()
client.federation.projects.list().AndReturn(projects)
def _mock_unscoped_client_list_projects(self, unscoped, projects):
client = self._mock_unscoped_client(unscoped)
self._mock_unscoped_federated_list_projects(client, projects)
def setUp(self):
super(OpenStackAuthTestsWebSSO, self).setUp()
@ -908,7 +1151,7 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
token = unscoped.auth_token
form_data = {'token': token}
self._mock_unscoped_client_list_projects(unscoped, projects)
self._mock_federated_client_list_projects(unscoped, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
self.mox.ReplayAll()
@ -927,7 +1170,7 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase):
token = unscoped.auth_token
form_data = {'token': token}
self._mock_unscoped_client_list_projects(unscoped, projects)
self._mock_federated_client_list_projects(unscoped, projects)
self._mock_scoped_client_for_tenant(unscoped, self.data.project_one.id)
self.mox.ReplayAll()

View File

@ -26,7 +26,10 @@ urlpatterns = [
name='switch_tenants'),
url(r'^switch_services_region/(?P<region_name>[^/]+)/$',
views.switch_region,
name='switch_services_region')
name='switch_services_region'),
url(r'^switch_keystone_provider/(?P<keystone_provider>[^/]+)/$',
views.switch_keystone_provider,
name='switch_keystone_provider')
]
if utils.is_websso_enabled():

View File

@ -498,3 +498,54 @@ def get_client_ip(request):
request.META.get('REMOTE_ADDR')
)
return request.META.get('REMOTE_ADDR')
def store_initial_k2k_session(auth_url, request, scoped_auth_ref,
unscoped_auth_ref):
"""Stores session variables if there are k2k service providers
This stores variables related to Keystone2Keystone federation. This
function gets skipped if there are no Keystone service providers.
An unscoped token to the identity provider keystone gets stored
so that it can be used to do federated login into the service
providers when switching keystone providers.
The settings file can be configured to set the display name
of the local (identity provider) keystone by setting
KEYSTONE_PROVIDER_IDP_NAME. The KEYSTONE_PROVIDER_IDP_ID settings
variable is used for comparison against the service providers.
It should not conflict with any of the service provider ids.
:param auth_url: base token auth url
:param request: Django http request object
:param scoped_auth_ref: Scoped Keystone access info object
:param unscoped_auth_ref: Unscoped Keystone access info object
"""
keystone_provider_id = request.session.get('keystone_provider_id', None)
if keystone_provider_id:
return None
providers = getattr(scoped_auth_ref, 'service_providers', None)
if providers:
providers = getattr(providers, '_service_providers', None)
if providers:
keystone_idp_name = getattr(settings, 'KEYSTONE_PROVIDER_IDP_NAME',
'Local Keystone')
keystone_idp_id = getattr(
settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
keystone_identity_provider = {'name': keystone_idp_name,
'id': keystone_idp_id}
# (edtubill) We will use the IDs as the display names
# We may want to be able to set display names in the future.
keystone_providers = [
{'name': provider_id, 'id': provider_id}
for provider_id in providers]
keystone_providers.append(keystone_identity_provider)
# We treat the Keystone idp ID as None
request.session['keystone_provider_id'] = keystone_idp_id
request.session['keystone_providers'] = keystone_providers
request.session['k2k_base_unscoped_token'] =\
unscoped_auth_ref.auth_token
request.session['k2k_auth_url'] = auth_url

View File

@ -31,6 +31,8 @@ import six
from openstack_auth import exceptions
from openstack_auth import forms
from openstack_auth import plugin
# This is historic and is added back in to not break older versions of
# Horizon, fix to Horizon to remove this requirement was committed in
# Juno
@ -241,3 +243,75 @@ def switch_region(request, region_name,
utils.set_response_cookie(response, 'services_region',
request.session['services_region'])
return response
@login_required
def switch_keystone_provider(request, keystone_provider=None,
redirect_field_name=auth.REDIRECT_FIELD_NAME):
"""Switches the user's keystone provider using K2K Federation
If keystone_provider is given then we switch the user to
the keystone provider using K2K federation. Otherwise if keystone_provider
is None then we switch the user back to the Identity Provider Keystone
which a non federated token auth will be used.
"""
base_token = request.session.get('k2k_base_unscoped_token', None)
k2k_auth_url = request.session.get('k2k_auth_url', None)
keystone_providers = request.session.get('keystone_providers', None)
if not base_token or not k2k_auth_url:
msg = _('K2K Federation not setup for this session')
raise exceptions.KeystoneAuthException(msg)
redirect_to = request.GET.get(redirect_field_name, '')
if not is_safe_url(url=redirect_to, host=request.get_host()):
redirect_to = settings.LOGIN_REDIRECT_URL
unscoped_auth_ref = None
keystone_idp_id = getattr(
settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone')
if keystone_provider == keystone_idp_id:
current_plugin = plugin.TokenPlugin()
unscoped_auth = current_plugin.get_plugin(auth_url=k2k_auth_url,
token=base_token)
else:
# Switch to service provider using K2K federation
plugins = [plugin.TokenPlugin()]
current_plugin = plugin.K2KAuthPlugin()
unscoped_auth = current_plugin.get_plugin(
auth_url=k2k_auth_url, service_provider=keystone_provider,
plugins=plugins, token=base_token)
try:
# Switch to identity provider using token auth
unscoped_auth_ref = current_plugin.get_access_info(unscoped_auth)
except exceptions.KeystoneAuthException as exc:
msg = 'Switching to Keystone Provider %s has failed. %s' \
% (keystone_provider, (six.text_type(exc)))
messages.error(request, msg)
if unscoped_auth_ref:
try:
request.user = auth.authenticate(
request=request, auth_url=unscoped_auth.auth_url,
token=unscoped_auth_ref.auth_token)
except exceptions.KeystoneAuthException as exc:
msg = 'Keystone provider switch failed: %s' % six.text_type(exc)
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
res.set_cookie('logout_reason', msg, max_age=10)
return res
auth.login(request, request.user)
auth_user.set_session_from_user(request, request.user)
request.session['keystone_provider_id'] = keystone_provider
request.session['keystone_providers'] = keystone_providers
request.session['k2k_base_unscoped_token'] = base_token
request.session['k2k_auth_url'] = k2k_auth_url
message = (
_('Switch to Keystone Provider "%(keystone_provider)s"'
'successful.') % {'keystone_provider': keystone_provider})
messages.success(request, message)
response = shortcuts.redirect(redirect_to)
return response