diff options
author | Jenkins <jenkins@review.openstack.org> | 2015-09-18 03:07:40 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2015-09-18 03:07:40 +0000 |
commit | d8041e2c57190c3ba7ec069fc25db0934168c7da (patch) | |
tree | df580fcfe1e0e6fa08781c4b9923e51a8d2853d5 | |
parent | 783f7ac5861201b28b18058700c05dfb47c4b41f (diff) | |
parent | 463b2ff3a616b02edd2ca9c7fd31953bf7c938e5 (diff) | |
download | django_openstack_auth-d8041e2c57190c3ba7ec069fc25db0934168c7da.tar.gz |
Merge "IDP specific websso"
-rw-r--r-- | openstack_auth/tests/tests.py | 35 | ||||
-rw-r--r-- | openstack_auth/utils.py | 74 | ||||
-rw-r--r-- | openstack_auth/views.py | 10 |
3 files changed, 111 insertions, 8 deletions
diff --git a/openstack_auth/tests/tests.py b/openstack_auth/tests/tests.py index 39ecfa0..0901429 100644 --- a/openstack_auth/tests/tests.py +++ b/openstack_auth/tests/tests.py @@ -11,6 +11,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import uuid + from django.conf import settings from django.contrib import auth from django.core.urlresolvers import reverse @@ -863,14 +865,24 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase): self.data = data_v3.generate_test_data() self.ks_client_module = client_v3 + self.idp_id = uuid.uuid4().hex + self.idp_oidc_id = uuid.uuid4().hex + self.idp_saml2_id = uuid.uuid4().hex + settings.OPENSTACK_API_VERSIONS['identity'] = 3 settings.OPENSTACK_KEYSTONE_URL = 'http://localhost:5000/v3' settings.WEBSSO_ENABLED = True settings.WEBSSO_CHOICES = ( ('credentials', 'Keystone Credentials'), ('oidc', 'OpenID Connect'), - ('saml2', 'Security Assertion Markup Language') + ('saml2', 'Security Assertion Markup Language'), + (self.idp_oidc_id, 'IDP OIDC'), + (self.idp_saml2_id, 'IDP SAML2') ) + settings.WEBSSO_IDP_MAPPING = { + self.idp_oidc_id: (self.idp_id, 'oidc'), + self.idp_saml2_id: (self.idp_id, 'saml2') + } self.mox.StubOutClassWithMocks(token_endpoint, 'Token') self.mox.StubOutClassWithMocks(auth_v3, 'Token') @@ -885,8 +897,10 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase): self.assertContains(response, 'credentials') self.assertContains(response, 'oidc') self.assertContains(response, 'saml2') + self.assertContains(response, self.idp_oidc_id) + self.assertContains(response, self.idp_saml2_id) - def test_websso_redirect(self): + def test_websso_redirect_by_protocol(self): origin = 'http://testserver/auth/websso/' protocol = 'oidc' redirect_url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' % @@ -901,6 +915,23 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin, test.TestCase): self.assertRedirects(response, redirect_url, status_code=302, target_status_code=404) + def test_websso_redirect_by_idp(self): + origin = 'http://testserver/auth/websso/' + protocol = 'oidc' + redirect_url = ('%s/auth/OS-FEDERATION/identity_providers/%s' + '/protocols/%s/websso?origin=%s' % + (settings.OPENSTACK_KEYSTONE_URL, self.idp_id, + protocol, origin)) + + form_data = {'auth_type': self.idp_oidc_id, + 'region': settings.OPENSTACK_KEYSTONE_URL} + url = reverse('login') + + # POST to the page and redirect to keystone. + response = self.client.post(url, form_data) + self.assertRedirects(response, redirect_url, status_code=302, + target_status_code=404) + def test_websso_login(self): projects = [self.data.project_one, self.data.project_two] unscoped = self.data.federated_unscoped_access_info diff --git a/openstack_auth/utils.py b/openstack_auth/utils.py index 1998bcf..1bc212e 100644 --- a/openstack_auth/utils.py +++ b/openstack_auth/utils.py @@ -186,6 +186,80 @@ def build_absolute_uri(request, relative_url): return request.build_absolute_uri(webroot + relative_url) +def get_websso_url(request, auth_url, websso_auth): + """Return the keystone endpoint for initiating WebSSO. + + Generate the keystone WebSSO endpoint that will redirect the user + to the login page of the federated identity provider. + + Based on the authentication type selected by the user in the login + form, it will construct the keystone WebSSO endpoint. + + :param request: Django http request object. + :type request: django.http.HttpRequest + :param auth_url: Keystone endpoint configured in the horizon setting. + The value is derived from: + - OPENSTACK_KEYSTONE_URL + - AVAILABLE_REGIONS + :type auth_url: string + :param websso_auth: Authentication type selected by the user from the + login form. The value is derived from the horizon + setting WEBSSO_CHOICES. + :type websso_auth: string + + Example of horizon WebSSO setting:: + WEBSSO_CHOICES = ( + ("credentials", "Keystone Credentials"), + ("oidc", "OpenID Connect"), + ("saml2", "Security Assertion Markup Language"), + ("acme_oidc", "ACME - OpenID Connect"), + ("acme_saml2", "ACME - SAML2") + ) + + WEBSSO_IDP_MAPPING = { + "acme_oidc": ("acme", "oidc"), + "acme_saml2": ("acme", "saml2") + } + } + + The value of websso_auth will be looked up in the WEBSSO_IDP_MAPPING + dictionary, if a match is found it will return a IdP specific WebSSO + endpoint using the values found in the mapping. + + The value in WEBSSO_IDP_MAPPING is expected to be a tuple formatted as + (<idp_id>, <protocol_id>). Using the values found, a IdP/protocol + specific URL will be constructed: + /auth/OS-FEDERATION/identity_providers/<idp_id> + /protocols/<protocol_id>/websso + + If no value is found from the WEBSSO_IDP_MAPPING dictionary, it will + treat the value as the global WebSSO protocol <protocol_id> and + construct the WebSSO URL by: + /auth/OS-FEDERATION/websso/<protocol_id> + + :returns: Keystone WebSSO endpoint. + :rtype: string + + """ + origin = build_absolute_uri(request, '/auth/websso/') + idp_mapping = getattr(settings, 'WEBSSO_IDP_MAPPING', {}) + idp_id, protocol_id = idp_mapping.get(websso_auth, + (None, websso_auth)) + + if idp_id: + # Use the IDP specific WebSSO endpoint + url = ('%s/auth/OS-FEDERATION/identity_providers/%s' + '/protocols/%s/websso?origin=%s' % + (auth_url, idp_id, protocol_id, origin)) + else: + # If no IDP mapping found for the identifier, + # perform WebSSO by protocol. + url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' % + (auth_url, protocol_id, origin)) + + return url + + def has_in_url_path(url, sub): """Test if the `sub` string is in the `url` path.""" scheme, netloc, path, query, fragment = urlparse.urlsplit(url) diff --git a/openstack_auth/views.py b/openstack_auth/views.py index 1ef4710..52f2d48 100644 --- a/openstack_auth/views.py +++ b/openstack_auth/views.py @@ -59,12 +59,10 @@ def login(request, template_name=None, extra_context=None, **kwargs): # If the user enabled websso and selects default protocol # from the dropdown, We need to redirect user to the websso url if request.method == 'POST': - protocol = request.POST.get('auth_type', 'credentials') - if utils.is_websso_enabled() and protocol != 'credentials': - region = request.POST.get('region') - origin = utils.build_absolute_uri(request, '/auth/websso/') - url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' % - (region, protocol, origin)) + auth_type = request.POST.get('auth_type', 'credentials') + if utils.is_websso_enabled() and auth_type != 'credentials': + auth_url = request.POST.get('region') + url = utils.get_websso_url(request, auth_url, auth_type) return shortcuts.redirect(url) if not request.is_ajax(): |