Allow to specify redirections on single IdP scenarios

In scenarios where the cloud operators have only a single Identity Provider,
we can have a default redirection to remove unnecessary user clicks and
improve user experience.

Closes-bug: #1784368

Change-Id: I251703dcaeac43174fbcba7e0658c6f92098b2e0
This commit is contained in:
Jose Castro Leon 2018-07-27 15:59:47 +02:00
parent a4443f4be7
commit 7fc8018956
9 changed files with 163 additions and 6 deletions

View File

@ -1522,6 +1522,49 @@ Default: ``"credentials"``
Specifies the default authentication mechanism. When user lands on the login Specifies the default authentication mechanism. When user lands on the login
page, this is the first choice they will see. page, this is the first choice they will see.
WEBSSO_DEFAULT_REDIRECT
~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 15.0.0(Stein)
Default: ``False``
Allows to redirect on login to the IdP provider defined on PROTOCOL and REGION
In cases you have a single IdP providing websso, in order to improve user
experience, you can redirect on the login page to the IdP directly by
specifying WEBSSO_DEFAULT_REDIRECT_PROTOCOL and WEBSSO_DEFAULT_REDIRECT_REGION
variables.
WEBSSO_DEFAULT_REDIRECT_PROTOCOL
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 15.0.0(Stein)
Default: ``None``
Allows to specify the protocol for the IdP to contact if the
WEBSSO_DEFAULT_REDIRECT is set to True
WEBSSO_DEFAULT_REDIRECT_REGION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 15.0.0(Stein)
Default: ``OPENSTACK_KEYSTONE_URL``
Allows to specify thee region of the IdP to contact if the
WEBSSO_DEFAULT_REDIRECT is set to True
WEBSSO_DEFAULT_REDIRECT_LOGOUT
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 15.0.0(Stein)
Default: ``None``
Allows to specify a callback to the IdP to cleanup the SSO resources.
Once the user logs out it will redirect to the IdP log out method.
Neutron Neutron
------- -------

View File

@ -1237,4 +1237,33 @@ class OpenStackAuthTestsWebSSO(OpenStackAuthTestsMixin,
response = self.client.post(url, form_data) response = self.client.post(url, form_data)
self.assertRedirects(response, settings.LOGIN_REDIRECT_URL) self.assertRedirects(response, settings.LOGIN_REDIRECT_URL)
def test_websso_login_default_redirect(self):
origin = 'http://testserver/auth/websso/'
protocol = 'oidc'
redirect_url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' %
(settings.OPENSTACK_KEYSTONE_URL, protocol, origin))
settings.WEBSSO_DEFAULT_REDIRECT = True
settings.WEBSSO_DEFAULT_REDIRECT_PROTOCOL = 'oidc'
settings.WEBSSO_DEFAULT_REDIRECT_REGION = (
settings.OPENSTACK_KEYSTONE_URL)
url = reverse('login')
# POST to the page and redirect to keystone.
response = self.client.get(url)
self.assertRedirects(response, redirect_url, status_code=302,
target_status_code=404)
def test_websso_logout_default_redirect(self):
settings.WEBSSO_DEFAULT_REDIRECT = True
settings.WEBSSO_DEFAULT_REDIRECT_LOGOUT = 'http://idptest/logout'
url = reverse('logout')
# POST to the page and redirect to logout method from idp.
response = self.client.get(url)
self.assertRedirects(response, settings.WEBSSO_DEFAULT_REDIRECT_LOGOUT,
status_code=302, target_status_code=301)
load_tests = load_tests_apply_scenarios load_tests = load_tests_apply_scenarios

View File

@ -12,6 +12,7 @@
# limitations under the License. # limitations under the License.
from django.conf.urls import url from django.conf.urls import url
from django.views import generic
from openstack_auth import utils from openstack_auth import utils
from openstack_auth import views from openstack_auth import views
@ -33,4 +34,8 @@ urlpatterns = [
] ]
if utils.is_websso_enabled(): if utils.is_websso_enabled():
urlpatterns.append(url(r"^websso/$", views.websso, name='websso')) urlpatterns += [
url(r"^websso/$", views.websso, name='websso'),
url(r"^error/$",
generic.TemplateView.as_view(template_name="403.html"))
]

View File

@ -38,6 +38,16 @@ def set_session_from_user(request, user):
request.user = user request.user = user
def unset_session_user_variables(request):
request.session['token'] = None
request.session['user_id'] = None
request.session['region_endpoint'] = None
request.session['services_region'] = None
# Update the user object cached in the request
request._cached_user = None
request.user = None
def create_user_from_token(request, token, endpoint, services_region=None): def create_user_from_token(request, token, endpoint, services_region=None):
# if the region is provided, use that, otherwise use the preferred region # if the region is provided, use that, otherwise use the preferred region
svc_region = services_region or \ svc_region = services_region or \

View File

@ -146,6 +146,30 @@ def is_websso_enabled():
return websso_enabled and keystonev3_plus return websso_enabled and keystonev3_plus
def is_websso_default_redirect():
"""Checks if the websso default redirect is available.
As with websso, this is only supported in Keystone version 3.
"""
websso_default_redirect = getattr(settings,
'WEBSSO_DEFAULT_REDIRECT', False)
keystonev3_plus = (get_keystone_version() >= 3)
return websso_default_redirect and keystonev3_plus
def get_websso_default_redirect_protocol():
return getattr(settings, 'WEBSSO_DEFAULT_REDIRECT_PROTOCOL', None)
def get_websso_default_redirect_region():
return getattr(settings, 'WEBSSO_DEFAULT_REDIRECT_REGION',
settings.OPENSTACK_KEYSTONE_URL)
def get_websso_default_redirect_logout():
return getattr(settings, 'WEBSSO_DEFAULT_REDIRECT_LOGOUT', None)
def build_absolute_uri(request, relative_url): def build_absolute_uri(request, relative_url):
"""Ensure absolute_uri are relative to WEBROOT.""" """Ensure absolute_uri are relative to WEBROOT."""
webroot = getattr(settings, 'WEBROOT', '') webroot = getattr(settings, 'WEBROOT', '')

View File

@ -55,6 +55,17 @@ LOG = logging.getLogger(__name__)
def login(request, template_name=None, extra_context=None, **kwargs): def login(request, template_name=None, extra_context=None, **kwargs):
"""Logs a user in using the :class:`~openstack_auth.forms.Login` form.""" """Logs a user in using the :class:`~openstack_auth.forms.Login` form."""
# If the user enabled websso and the default redirect
# redirect to the default websso url
if (request.method == 'GET' and utils.is_websso_enabled and
utils.is_websso_default_redirect()):
protocol = utils.get_websso_default_redirect_protocol()
region = utils.get_websso_default_redirect_region()
origin = request.build_absolute_uri('/auth/websso/')
url = ('%s/auth/OS-FEDERATION/websso/%s?origin=%s' %
(region, protocol, origin))
return shortcuts.redirect(url)
# If the user enabled websso and selects default protocol # If the user enabled websso and selects default protocol
# from the dropdown, We need to redirect user to the websso url # from the dropdown, We need to redirect user to the websso url
if request.method == 'POST': if request.method == 'POST':
@ -151,9 +162,12 @@ def websso(request):
request.user = auth.authenticate(request=request, auth_url=auth_url, request.user = auth.authenticate(request=request, auth_url=auth_url,
token=token) token=token)
except exceptions.KeystoneAuthException as exc: except exceptions.KeystoneAuthException as exc:
msg = 'Login failed: %s' % six.text_type(exc) if utils.is_websso_default_redirect():
res = django_http.HttpResponseRedirect(settings.LOGIN_URL) res = django_http.HttpResponseRedirect(settings.LOGIN_ERROR)
res.set_cookie('logout_reason', msg, max_age=10) else:
msg = 'Login failed: %s' % six.text_type(exc)
res = django_http.HttpResponseRedirect(settings.LOGIN_URL)
res.set_cookie('logout_reason', msg, max_age=10)
return res return res
auth_user.set_session_from_user(request, request.user) auth_user.set_session_from_user(request, request.user)
@ -178,8 +192,15 @@ def logout(request, login_url=None, **kwargs):
LOG.info(msg) LOG.info(msg)
""" Securely logs a user out. """ """ Securely logs a user out. """
return django_auth_views.logout_then_login(request, login_url=login_url, if (utils.is_websso_enabled and utils.is_websso_default_redirect() and
**kwargs) utils.get_websso_default_redirect_logout()):
auth_user.unset_session_user_variables(request)
return django_http.HttpResponseRedirect(
utils.get_websso_default_redirect_logout())
else:
return django_auth_views.logout_then_login(request,
login_url=login_url,
**kwargs)
@login_required @login_required

View File

@ -26,6 +26,7 @@ DEBUG = True
WEBROOT = '/' WEBROOT = '/'
#LOGIN_URL = WEBROOT + 'auth/login/' #LOGIN_URL = WEBROOT + 'auth/login/'
#LOGOUT_URL = WEBROOT + 'auth/logout/' #LOGOUT_URL = WEBROOT + 'auth/logout/'
#LOGIN_ERROR = WEBROOT + 'auth/error/'
# #
# LOGIN_REDIRECT_URL can be used as an alternative for # LOGIN_REDIRECT_URL can be used as an alternative for
# HORIZON_CONFIG.user_home, if user_home is not set. # HORIZON_CONFIG.user_home, if user_home is not set.
@ -231,6 +232,21 @@ OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_"
# "acme_saml2": ("acme", "saml2"), # "acme_saml2": ("acme", "saml2"),
#} #}
# Enables redirection on login to the identity provider defined on
# WEBSSO_DEFAULT_REDIRECT_PROTOCOL and WEBSSO_DEFAULT_REDIRECT_REGION
#WEBSSO_DEFAULT_REDIRECT = False
# Specifies the protocol to use for default redirection on login
#WEBSSO_DEFAULT_REDIRECT_PROTOCOL = None
# Specifies the region to which the connection will be established on login
#WEBSSO_DEFAULT_REDIRECT_REGION = OPENSTACK_KEYSTONE_URL
# Enables redirection on logout to the method specified on the identity provider.
# Once logout the client will be redirected to the address specified in this
# variable.
#WEBSSO_DEFAULT_REDIRECT_LOGOUT = None
# The Keystone Provider drop down uses Keystone to Keystone federation # The Keystone Provider drop down uses Keystone to Keystone federation
# to switch between Keystone service providers. # to switch between Keystone service providers.
# Set display name for Identity Provider (dropdown display name) # Set display name for Identity Provider (dropdown display name)

View File

@ -54,6 +54,7 @@ SITE_BRANDING = 'OpenStack Dashboard'
WEBROOT = '/' WEBROOT = '/'
LOGIN_URL = None LOGIN_URL = None
LOGOUT_URL = None LOGOUT_URL = None
LOGIN_ERROR = None
LOGIN_REDIRECT_URL = None LOGIN_REDIRECT_URL = None
MEDIA_ROOT = None MEDIA_ROOT = None
MEDIA_URL = None MEDIA_URL = None
@ -418,6 +419,8 @@ if LOGIN_URL is None:
LOGIN_URL = WEBROOT + 'auth/login/' LOGIN_URL = WEBROOT + 'auth/login/'
if LOGOUT_URL is None: if LOGOUT_URL is None:
LOGOUT_URL = WEBROOT + 'auth/logout/' LOGOUT_URL = WEBROOT + 'auth/logout/'
if LOGIN_ERROR is None:
LOGIN_ERROR = WEBROOT + 'auth/error/'
if LOGIN_REDIRECT_URL is None: if LOGIN_REDIRECT_URL is None:
LOGIN_REDIRECT_URL = WEBROOT LOGIN_REDIRECT_URL = WEBROOT

View File

@ -0,0 +1,6 @@
---
features:
- Adds the possibility to redirect the login to an identity provider by
default. For that purpose the following variables have been added,
``WEBSSO_DEFAULT_REDIRECT``, ``WEBSSO_DEFAULT_REDIRECT_PROTOCOL``,
``WEBSSO_DEFAULT_REDIRECT_REGION`` and ``WEBSSO_DEFAULT_REDIRECT_LOGOUT``.