0002-views-improve-handling-of-next_url-for-sp-initiated-.patch
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 |
- |