From b6ee8523a998dd7b1ab9858f60a02bcda1f0726b Mon Sep 17 00:00:00 2001 From: Paul Marillonnet Date: Tue, 19 Apr 2022 11:23:16 +0200 Subject: [PATCH] views: use MELLON_OPENED_SESSION to anchor local session to the global session (#66747) If the MELLON_OPENED_SESSION cookie change or disappear during an opened session, the user is automatically logged out. If it changes after a previous passive login try, passive login is allowed again. --- mellon/middleware.py | 14 +++++++++++--- mellon/views.py | 7 +++++++ tests/test_sso_slo.py | 23 ++++++++++++++++++++++- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/mellon/middleware.py b/mellon/middleware.py index 0bcd59f..2d6eb2f 100644 --- a/mellon/middleware.py +++ b/mellon/middleware.py @@ -14,6 +14,7 @@ # along with this program. If not, see . +from django.contrib import auth from django.http import HttpResponseRedirect from django.urls import reverse from django.utils.deprecation import MiddlewareMixin @@ -58,11 +59,18 @@ class PassiveAuthenticationMiddleware(MiddlewareMixin): if not app_settings.OPENED_SESSION_COOKIE_NAME: return if hasattr(request, 'user') and request.user.is_authenticated: - return - if PASSIVE_TRIED_COOKIE in request.COOKIES: + # close current session if the opened session cookie changed... + old_opened_session_cookie = request.session.get('mellon_opened_session_cookie') + if old_opened_session_cookie and old_opened_session_cookie != request.COOKIES.get( + app_settings.OPENED_SESSION_COOKIE_NAME + ): + auth.logout(request) return if app_settings.OPENED_SESSION_COOKIE_NAME not in request.COOKIES: return + opened_session_cookie = request.COOKIES[app_settings.OPENED_SESSION_COOKIE_NAME] + if request.COOKIES.get(PASSIVE_TRIED_COOKIE) == opened_session_cookie: + return # all is good, try passive login params = { 'next': request.build_absolute_uri(), @@ -71,5 +79,5 @@ class PassiveAuthenticationMiddleware(MiddlewareMixin): url = reverse('mellon_login') + '?%s' % urlencode(params) response = HttpResponseRedirect(url) # prevent loops - response.set_cookie(PASSIVE_TRIED_COOKIE, value='1', max_age=None) + response.set_cookie(PASSIVE_TRIED_COOKIE, value=opened_session_cookie, max_age=None) return response diff --git a/mellon/views.py b/mellon/views.py index 67f5873..e993378 100644 --- a/mellon/views.py +++ b/mellon/views.py @@ -335,6 +335,13 @@ class LoginView(ProfileMixin, LogMixin, View): return utils.login(self.request, user) + if ( + app_settings.OPENED_SESSION_COOKIE_NAME + and app_settings.OPENED_SESSION_COOKIE_NAME in self.request.COOKIES + ): + self.request.session['mellon_opened_session_cookie'] = self.request.COOKIES[ + app_settings.OPENED_SESSION_COOKIE_NAME + ] session_index = attributes['session_index'] if session_index: if not self.request.session.session_key: diff --git a/tests/test_sso_slo.py b/tests/test_sso_slo.py index 7eb5fae..ccf553c 100644 --- a/tests/test_sso_slo.py +++ b/tests/test_sso_slo.py @@ -676,13 +676,14 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings): settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION' assert 'MELLON_PASSIVE_TRIED' not in app.cookies # webtest-lint is against unicode - app.set_cookie('IDP_SESSION', '1') + app.set_cookie('IDP_SESSION', '1234') response = app.get('/', headers={'Accept': 'text/html'}, status=302) assert urlparse.urlparse(response.location).path == '/login/' assert urlparse.parse_qs(urlparse.urlparse(response.location).query, keep_blank_values=True) == { 'next': ['http://testserver/'], 'passive': [''], } + assert app.cookies['MELLON_PASSIVE_TRIED'] == '1234' # simulate closing of session at IdP app.cookiejar.clear('testserver.local', '/', 'IDP_SESSION') @@ -703,6 +704,26 @@ def test_passive_auth_middleware_ok(db, app, idp, caplog, settings): } assert 'MELLON_PASSIVE_TRIED' in app.cookies + # but not two times + response = app.get('/', headers={'Accept': 'text/html'}, status=200) + # if session change at IdP + app.set_cookie('IDP_SESSION', 'abcd') + # then we try again... + response = app.get('/', headers={'Accept': 'text/html'}, status=302) + + # ok, let's change again and login + app.set_cookie('IDP_SESSION', '5678') + response = app.get(reverse('mellon_login') + '?next=/whatever/') + url, body, relay_state = idp.process_authn_request_redirect(response['Location']) + response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state}) + assert app.session['mellon_opened_session_cookie'] == '5678' + assert '_auth_user_id' in app.session + # ok change the idp session id + app.set_cookie('IDP_SESSION', '1234') + response = app.get('/', headers={'Accept': 'text/html'}, status=200) + # we are automatically unlogged + assert '_auth_user_id' not in app.session + def test_passive_auth_middleware_no_passive_auth_parameter(db, app, idp, caplog, settings): settings.MELLON_OPENED_SESSION_COOKIE_NAME = 'IDP_SESSION' -- 2.35.1