views: allow overriding the default return url after logout (#69740)

This commit is contained in:
Benjamin Dauvergne 2022-10-03 11:17:37 +02:00
parent 86d3cad3b8
commit e98308d45c
2 changed files with 33 additions and 7 deletions

View File

@ -612,13 +612,13 @@ login = transaction.non_atomic_requests(csrf_exempt(LoginView.as_view()))
class LogoutView(ProfileMixin, LogMixin, View):
def get(self, request, *args, **kwargs):
def get(self, request, *args, logout_next_url='/', **kwargs):
if 'SAMLRequest' in request.GET:
return self.idp_logout(request, request.META['QUERY_STRING'], 'redirect')
elif 'SAMLResponse' in request.GET:
return self.sp_logout_response(request)
return self.sp_logout_response(request, logout_next_url=logout_next_url)
else:
return self.sp_logout_request(request)
return self.sp_logout_request(request, logout_next_url=logout_next_url)
def post(self, request, *args, **kwargs):
return self.idp_logout(request, force_str(request.body), 'soap')
@ -721,14 +721,14 @@ class LogoutView(ProfileMixin, LogMixin, View):
else:
return HttpResponseRedirect(logout.msgUrl)
def sp_logout_request(self, request):
def sp_logout_request(self, request, logout_next_url=None):
'''Launch a logout request to the identity provider'''
referer = request.headers.get('Referer')
field_next_url = request.GET.get(REDIRECT_FIELD_NAME)
next_url = None
if field_next_url and utils.same_origin(request.build_absolute_uri(), field_next_url):
next_url = field_next_url
next_url = next_url or '/'
next_url = next_url or logout_next_url
if not referer or utils.same_origin(request.build_absolute_uri(), referer):
if hasattr(request, 'user') and request.user.is_authenticated:
logout = None
@ -763,7 +763,7 @@ class LogoutView(ProfileMixin, LogMixin, View):
self.log.warning('logout refused referer %r is not of the same origin', referer)
return HttpResponseRedirect(next_url)
def sp_logout_response(self, request):
def sp_logout_response(self, request, logout_next_url='/'):
'''Launch a logout request to the identity provider'''
self.profile = logout = utils.create_logout(request)
logout.msgRelayState = request.GET.get('RelayState')
@ -778,7 +778,7 @@ class LogoutView(ProfileMixin, LogMixin, View):
self.log.warning('partial logout')
except lasso.Error as e:
self.log.warning('unable to process a logout response: %s', e)
return HttpResponseRedirect(self.get_next_url() or '/')
return HttpResponseRedirect(self.get_next_url() or logout_next_url)
logout = csrf_exempt(LogoutView.as_view())

View File

@ -270,6 +270,32 @@ def test_sso_slo_next(db, app, idp, caplog, sp_settings):
assert response.location == '/some/path/'
def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf):
from mellon.views import logout
response = app.get(reverse('mellon_login'))
url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
request = rf.get('/logout/')
request.session = app.session
request.user = mock.Mock()
request.user.is_authenticated = True
response = logout(request, logout_next_url='/other/path/')
assert list(request.session.values()) == ['/other/path/']
response = app.get(reverse('mellon_login'))
url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
request = rf.get('/logout/?next=/some/path/')
request.session = app.session
request.user = mock.Mock()
request.user.is_authenticated = True
response = logout(request, logout_next_url='/other/path/')
assert list(request.session.values()) == ['/some/path/']
def test_sso_idp_slo(db, app, idp, caplog, sp_settings):
assert Session.objects.count() == 0
assert User.objects.count() == 0