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.
This commit is contained in:
Paul Marillonnet 2022-04-19 11:23:16 +02:00 committed by Benjamin Dauvergne
parent 025cda4293
commit 1fa1541c02
3 changed files with 44 additions and 5 deletions

View File

@ -14,6 +14,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib import auth
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils.deprecation import MiddlewareMixin
@ -58,11 +59,21 @@ 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:
return
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
):
# close current session if the opened session cookie changed...
auth.logout(request)
# and continue with unlogged behaviour
else:
# otherwise, if current session is still active, do nothing
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 +82,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

View File

@ -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:

View File

@ -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')
# if we try a request, we are logged out and redirected to try a new passive login
response = app.get('/', headers={'Accept': 'text/html'}, status=302)
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'