Projet

Général

Profil

0002-views-improve-handling-of-next_url-for-sp-initiated-.patch

Benjamin Dauvergne, 30 septembre 2022 08:05

Télécharger (5,92 ko)

Voir les différences:

Subject: [PATCH 2/2] views: improve handling of next_url for sp initiated
 logout (#69740)

 mellon/views.py       | 20 +++++++++++---------
 tests/test_sso_slo.py | 32 ++++++++++++++++++++++++++++++--
 2 files changed, 41 insertions(+), 11 deletions(-)
mellon/views.py
612 612

  
613 613

  
614 614
class LogoutView(ProfileMixin, LogMixin, View):
615
    def get(self, request, *args, **kwargs):
615
    def get(self, request, *args, next_url=None, **kwargs):
616 616
        if 'SAMLRequest' in request.GET:
617 617
            return self.idp_logout(request, request.META['QUERY_STRING'], 'redirect')
618 618
        elif 'SAMLResponse' in request.GET:
619
            return self.sp_logout_response(request)
619
            return self.sp_logout_response(request, next_url=next_url)
620 620
        else:
621
            return self.sp_logout_request(request)
621
            return self.sp_logout_request(request, next_url=next_url)
622 622

  
623 623
    def post(self, request, *args, **kwargs):
624 624
        return self.idp_logout(request, force_str(request.body), 'soap')
......
721 721
        else:
722 722
            return HttpResponseRedirect(logout.msgUrl)
723 723

  
724
    def sp_logout_request(self, request):
724
    def sp_logout_request(self, request, next_url=None):
725 725
        '''Launch a logout request to the identity provider'''
726
        next_url = request.GET.get(REDIRECT_FIELD_NAME)
727 726
        referer = request.headers.get('Referer')
727
        field_next_url = request.GET.get(REDIRECT_FIELD_NAME)
728
        if field_next_url and utils.same_origin(request.build_absolute_uri(), field_next_url):
729
            next_url = field_next_url
730
        next_url = next_url or '/'
728 731
        if not referer or utils.same_origin(request.build_absolute_uri(), referer):
729 732
            if hasattr(request, 'user') and request.user.is_authenticated:
730 733
                logout = None
......
754 757
                    self.log.info('user logged out, SLO request sent to IdP')
755 758
            else:
756 759
                # anonymous user: if next_url is None redirect to referer
757
                return HttpResponseRedirect(next_url or referer)
760
                return HttpResponseRedirect(next_url)
758 761
        else:
759 762
            self.log.warning('logout refused referer %r is not of the same origin', referer)
760 763
        return HttpResponseRedirect(next_url)
761 764

  
762
    def sp_logout_response(self, request):
765
    def sp_logout_response(self, request, next_url='/'):
763 766
        '''Launch a logout request to the identity provider'''
764 767
        self.profile = logout = utils.create_logout(request)
765 768
        logout.msgRelayState = request.GET.get('RelayState')
......
775 778
        except lasso.Error as e:
776 779
            self.log.warning('unable to process a logout response: %s', e)
777 780
            return HttpResponseRedirect(resolve_url(settings.LOGIN_REDIRECT_URL))
778
        next_url = self.get_next_url(default=resolve_url(settings.LOGIN_REDIRECT_URL))
779
        return HttpResponseRedirect(next_url)
781
        return HttpResponseRedirect(self.get_next_url() or next_url)
780 782

  
781 783

  
782 784
logout = csrf_exempt(LogoutView.as_view())
tests/test_sso_slo.py
254 254

  
255 255
    # again, user is already logged out
256 256
    response = app.get(reverse('mellon_logout'), extra_environ={'HTTP_REFERER': '/some/path'})
257
    assert urlparse.urlparse(response['Location']).path == '/some/path'
257
    assert urlparse.urlparse(response['Location']).path == '/'
258 258

  
259 259

  
260 260
def test_sso_slo_next(db, app, idp, caplog, sp_settings):
261 261
    response = app.get(reverse('mellon_login'))
262 262
    url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
263 263
    response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
264
    response = app.get(reverse('mellon_logout') + '?next=/some/path/')
264
    response = app.get(
265
        reverse('mellon_logout') + '?next=/some/path/', extra_environ={'HTTP_REFERER': '/other/path'}
266
    )
265 267
    assert urlparse.urlparse(response['Location']).path == '/singleLogout'
266 268
    url = idp.process_logout_request_redirect(response.location)
267 269
    response = app.get(url)
268 270
    assert response.location == '/some/path/'
269 271

  
270 272

  
273
def test_sso_slo_default_next_url(db, app, idp, caplog, sp_settings, rf):
274
    from mellon.views import logout
275

  
276
    response = app.get(reverse('mellon_login'))
277
    url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
278
    response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
279

  
280
    request = rf.get('/logout/')
281
    request.session = app.session
282
    request.user = mock.Mock()
283
    request.user.is_authenticated = True
284
    response = logout(request, next_url='/other/path/')
285
    assert list(request.session.values()) == ['/other/path/']
286

  
287
    response = app.get(reverse('mellon_login'))
288
    url, body, relay_state = idp.process_authn_request_redirect(response['Location'])
289
    response = app.post(reverse('mellon_login'), params={'SAMLResponse': body, 'RelayState': relay_state})
290

  
291
    request = rf.get('/logout/?next=/some/path/')
292
    request.session = app.session
293
    request.user = mock.Mock()
294
    request.user.is_authenticated = True
295
    response = logout(request, next_url='/other/path/')
296
    assert list(request.session.values()) == ['/some/path/']
297

  
298

  
271 299
def test_sso_idp_slo(db, app, idp, caplog, sp_settings):
272 300
    assert Session.objects.count() == 0
273 301
    assert User.objects.count() == 0
274
-