summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlin-hua-cheng <os.lcheng@gmail.com>2015-08-31 14:45:16 -0700
committerlin-hua-cheng <os.lcheng@gmail.com>2015-09-09 19:50:38 -0700
commit463b2ff3a616b02edd2ca9c7fd31953bf7c938e5 (patch)
treebd65b58d74db69b9a2cccb900e5bd8ba881babd5
parent87f2158899ddea28fb6e8387889a29ee4d026fd9 (diff)
downloaddjango_openstack_auth-463b2ff3a616b02edd2ca9c7fd31953bf7c938e5.tar.gz
IDP specific websso
Allow handling websso requests per IDP. Change-Id: Ie20e21eb95c2250e301165012eef5591243620e9 Implements: bp federation-idp-websso
-rw-r--r--openstack_auth/tests/tests.py35
-rw-r--r--openstack_auth/utils.py74
-rw-r--r--openstack_auth/views.py10
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():