def logout(self, request, data, binding): conf = get_config(request=request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # Logout started by us client.parse_logout_request_response(data['SAMLResponse'], binding) http_response = logout_completed() else: # Logout started by IdP subject_id = _get_subject_id(request.session) if subject_id is None: http_response = logout_completed() else: http_info = client.handle_logout_request(data['SAMLRequest'], subject_id, binding, relay_state=data.get('RelayState', '')) http_response = HttpResponseRedirect(get_location(http_info)) state.sync() user = request.user if user.is_anonymous: return http_response Token.objects.get(user=user).delete() auth.logout(request) event_logger.saml2_auth.info( 'User {user_username} with full name {user_full_name} logged out successfully with SAML2.', event_type='auth_logged_out_with_saml2', event_context={'user': user} ) return http_response
def logout(request, config_loader_path=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug('Logout process started') state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), ) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contains the subject id for user %s' % request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) resp = client.global_logout(subject_id) entity_ids = client.users.issuers_of_info(subject_id) state.sync() logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(resp[entity_ids[0]][1]['headers'][0][1])
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s', request.user) sign_requests = getattr(conf, '_sp_logout_requests_signed', False) sign_alg = getattr(conf, 'signing_algorithm', SIG_RSA_SHA1) digest_alg = getattr(conf, 'digest_algorithm', DIGEST_SHA1) result = client.global_logout(subject_id, sign=sign_requests, sign_alg=sign_alg, digest_alg=digest_alg) state.sync() if not result: logger.error("Looks like the user %s is not logged in any IdP/AA", subject_id) return HttpResponseBadRequest("You are not logged in any IdP/AA") if len(result) > 1: logger.error( 'Sorry, I do not know how to logout from several sources. I will logout just from the first one' ) for entityid, logout_info in result.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: logger.debug( 'Returning form to the IdP to continue the logout process') body = ''.join(http_info['data']) return HttpResponse(body) elif binding == BINDING_HTTP_REDIRECT: logger.debug( 'Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(get_location(http_info)) else: logger.error('Unknown binding: %s', binding) return HttpResponseServerError('Failed to log out') else: # We must have had a soap logout return finish_logout(request, logout_info) logger.error( 'Could not logout because there only the HTTP_REDIRECT is supported') return HttpResponseServerError('Logout Binding not supported')
def logout_ls_saml2_view(request, config_loader_path=None, next_page=None): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') """Turn off saml2 """ _set_saml2_auth_used(request.session, False) conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.logout_response(request.GET['SAMLResponse'], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == '200 Ok': return logout_view(request) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return HttpResponse('Error during logout') else: response, success = client.logout_request(request.GET, subject_id) state.sync() if success: logout_view(request) assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout_service( request, config_loader_path=None, next_page=None, logout_error_template="djangosaml2/logout_error.html" ): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug("Logout service started") conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if "SAMLResponse" in request.GET: # we started the logout logger.debug("Receiving a logout response from the IdP") response = client.logout_response(request.GET["SAMLResponse"], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == "200 Ok": if next_page is None and hasattr(settings, "LOGOUT_REDIRECT_URL"): next_page = settings.LOGOUT_REDIRECT_URL logger.debug("Performing django_logout with a next_page of %s" % next_page) return django_logout(request, next_page=next_page) else: logger.error("Unknown error during the logout") return HttpResponse("Error during logout") elif "SAMLRequest" in request.GET: # logout started by the IdP logger.debug("Receiving a logout request from the IdP") subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( "The session does not contain the subject id for user %s. " + "Performing local logout" % request.user ) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: response, success = client.logout_request(request.GET, subject_id) state.sync() if success: auth.logout(request) assert response[0][0] == "Location" url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == "Location" url = response[0][1] return HttpResponseRedirect(url) else: logger.error("Unknown error during the logout") return HttpResponse("Error during logout") else: logger.error("No SAMLResponse or SAMLRequest parameter found") raise Http404("No SAMLResponse or SAMLRequest parameter found")
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ initiate_global_logout = get_custom_setting('DJANGOSAML_INITIATE_GLOBAL_LOGOUT', default=True) # Handle SP that should NOT initiate a global logout if not initiate_global_logout: logger.debug('SP cannot initiate a global logout. Doing local logout for %s and redirecting to %s' % (request.user, settings.LOGOUT_REDIRECT_URL)) auth.logout(request) return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s', request.user) result = client.global_logout(subject_id) state.sync() if not result: logger.error("Looks like the user %s is not logged in any IdP/AA", subject_id) return HttpResponseBadRequest("You are not logged in any IdP/AA") if len(result) > 1: logger.error('Sorry, I do not know how to logout from several sources. I will logout just from the first one') for entityid, logout_info in result.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: logger.debug('Returning form to the IdP to continue the logout process') body = ''.join(http_info['data']) return HttpResponse(body) elif binding == BINDING_HTTP_REDIRECT: logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(get_location(http_info)) else: logger.error('Unknown binding: %s', binding) return HttpResponseServerError('Failed to log out') else: # We must have had a soap logout return finish_logout(request, logout_info) logger.error('Could not logout because there only the HTTP_REDIRECT is supported') return HttpResponseServerError('Logout Binding not supported')
def logout_service(request, config_loader_path=None, next_page=None): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.logout_response(request.GET['SAMLResponse'], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == '200 Ok': if next_page is None and hasattr(settings, 'LOGOUT_REDIRECT_URL'): next_page = settings.LOGOUT_REDIRECT_URL logger.debug('Performing django_logout with a next_page of %s' % next_page) return django_logout(request, next_page=next_page) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) response, success = client.logout_request(request.GET, subject_id) state.sync() if success: auth.logout(request) assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug('Logout process started') state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contains the subject id for user %s', request.user) result = client.global_logout(subject_id) state.sync() if not result: logger.error("Looks like the user %s is not logged in any IdP/AA", subject_id) return HttpResponseBadRequest("You are not logged in any IdP/AA") if len(result) > 1: logger.error('Sorry, I do not know how to logout from several sources. I will logout just from the first one') for entityid, logout_info in result.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: logger.debug('Returning form to the IdP to continue the logout process') body = ''.join(http_info['data']) return HttpResponse(body) elif binding == BINDING_HTTP_REDIRECT: logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(get_location(http_info)) else: logger.error('Unknown binding: %s', binding) return HttpResponseServerError('Failed to log out') else: # We must have had a soap logout return finish_logout(request, logout_info) logger.error('Could not logout because there only the HTTP_REDIRECT is supported') return HttpResponseServerError('Logout Binding not supported')
def do_logout_service(request, data, binding, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """ SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # we started the logout logger.debug('Receiving a logout response from the IdP') try: response = client.parse_logout_request_response(data['SAMLResponse'], binding) state.sync() except: traceback_info = '\n'.join(traceback.format_exception(*(sys.exc_info()))) logger.warning('Encountered the following error when calling Saml2Client.global_logout(): %s' % traceback_info) auth.logout(request) return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) else: return finish_logout(request, response, next_page=next_page) elif 'SAMLRequest' in data: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning('The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) else: http_info = client.handle_logout_request(data['SAMLRequest'], subject_id, binding) state.sync() auth.logout(request) redirect_location = settings.LOGOUT_REDIRECT_URL try: redirect_location = get_location(http_info) except: traceback_info = '\n'.join(traceback.format_exception(*(sys.exc_info()))) logger.error('Encountered the following error calling get_location(http_info) with an http_info value of %s: %s' % (http_info, traceback_info)) return HttpResponseRedirect(redirect_location) else: logger.error('No SAMLResponse or SAMLRequest parameter found') return failure_redirect("No SAMLResponse or SAMLRequest parameter found.")
def do_logout_service(request, data, binding, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.parse_logout_request_response(data['SAMLResponse'], binding) state.sync() return finish_logout(request, response, next_page=next_page) elif 'SAMLRequest' in data: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout', request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: http_info = client.handle_logout_request( data['SAMLRequest'], subject_id, binding) state.sync() auth.logout(request) return HttpResponseRedirect(get_location(http_info)) else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug("Logout process started") state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning("The session does not contains the subject id for user %s" % request.user) session_id, code, head, body = client.global_logout(subject_id) headers = dict(head) state.sync() logger.debug("Redirecting to the IdP to continue the logout process") return HttpResponseRedirect(headers["Location"])
def logout(request, config_loader_path=None): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug('Logout process started') state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) subject_id = _get_subject_id(request.session) session_id, code, head, body = client.global_logout(subject_id) headers = dict(head) state.sync() logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(headers['Location'])
def get(self, request): state = StateCache(request.session) conf = get_config(request=request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: return logout_failed(_('You cannot be logged out.')) try: result = client.global_logout(subject_id) except KeyError: return logout_failed(_('You are not logged in any IdP/AA.')) state.sync() if not result: return logout_failed(_('You are not logged in any IdP/AA.')) # Logout is supported only from 1 IdP binding, http_info = result.values()[0] return HttpResponseRedirect(get_location(http_info))
def do_logout_service(request, data, binding, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.parse_logout_request_response( data['SAMLResponse'], binding) state.sync() return finish_logout(request, response, next_page=next_page) elif 'SAMLRequest' in data: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout', request.user) auth.logout(request) return render(request, logout_error_template, status=403) else: http_info = client.handle_logout_request(data['SAMLRequest'], subject_id, binding, relay_state=data.get( 'RelayState', '')) state.sync() auth.logout(request) if (http_info.get('method', 'GET') == 'POST' and 'data' in http_info and ('Content-type', 'text/html') in http_info.get('headers', [])): # need to send back to the IDP a signed POST response with user session # return HTML form content to browser with auto form validation # to finally send request to the IDP return HttpResponse(http_info['data']) else: return HttpResponseRedirect(get_location(http_info)) else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def echo_attributes(request, config_loader_path=None, template='djangosaml2/echo_attributes.html'): """Example view that echo the SAML attributes of an user""" state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) identity = client.users.get_identity(subject_id, check_not_on_or_after=False) return render(request, template, {'attributes': identity[0]})
def echo_attributes(request, config_loader_path=None, template='djangosaml2/echo_attributes.html'): """Example view that echo the SAML attributes of an user""" state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) try: identity = client.users.get_identity(subject_id, check_not_on_or_after=False) except AttributeError: return HttpResponse("No active SAML identity found. Are you sure you have logged in via SAML?") return render(request, template, {'attributes': identity[0]})
def logout_service(request, config_loader_path=None, next_page=None): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.logout_response(request.GET['SAMLResponse'], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == '200 Ok': return django_logout(request, next_page=next_page) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) response, success = client.logout_request(request.GET, subject_id) state.sync() if success: auth.logout(request) assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def do_logout_service(request, data, binding, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) if 'SAMLResponse' in data: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.parse_logout_request_response( data['SAMLResponse'], binding) state.sync() return finish_logout(request, response, next_page=next_page) elif 'SAMLRequest' in data: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: http_info = client.handle_logout_request(data['SAMLRequest'], subject_id, binding) state.sync() auth.logout(request) return HttpResponseRedirect(get_location(http_info)) else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout(request, config_loader_path=None): """ SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ logger.debug('Logout process started') initiate_global_logout = get_custom_setting('DJANGOSAML_INITIATE_GLOBAL_LOGOUT', default=True) # Handle SP that should NOT initiate a global logout if not initiate_global_logout: logger.debug('SP cannot initiate a global logout. Doing local logout for %s and redirecting to %s' % (request.user, settings.LOGOUT_REDIRECT_URL)) auth.logout(request) return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) # Handle SP that should initiate a global logout if not request.user.is_authenticated(): logger.debug('User is not authenticated. Redirecting to settings.LOGOUT_REDIRECT_URL') return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning('The session does not contains the subject id for user %s' % request.user) try: result = client.global_logout(subject_id) except Exception as e: traceback_info = '\n'.join(traceback.format_exception(*(sys.exc_info()))) logger.warning('Encountered the following error when calling Saml2Client.global_logout(): %s' % traceback_info) return failure_redirect('Encountered the following error when trying to do a global logout: %s.' % e) state.sync() if not result: logger.error("Looks like the user %s is not logged in any IdP/AA" % subject_id) return failure_redirect("You are not logged into any IdP/AA") if len(result) > 1: logger.error('Sorry, I do not know how to logout from several sources. I will logout just from the first one') for entityid, logout_info in result.items(): if isinstance(logout_info, tuple): binding, http_info = logout_info if binding == BINDING_HTTP_POST: logger.debug('Returning form to the IdP to continue the logout process') body = ''.join(http_info['data']) logger.debug("Body = %s" % body) return HttpResponse(body) elif binding == BINDING_HTTP_REDIRECT: logger.debug('Redirecting to the IdP to continue the logout process') return HttpResponseRedirect(get_location(http_info)) else: logger.error('Unknown binding: %s', binding) return failure_redirect("Failed to log out.") else: # We must have had a soap logout return finish_logout(request, logout_info) logger.error('Could not logout because there only the HTTP_REDIRECT is supported') return failure_redirect("Logout Binding not supported")
def spid_logout(request, config_loader_path=None, **kwargs): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ if not request.user.is_authenticated: return HttpResponseRedirect(settings.LOGOUT_REDIRECT_URL) state = StateCache(request.saml_session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.saml_session)) # whatever happens, however, the user will be logged out of this sp auth.logout(request) state.sync() subject_id = djangosaml2_views._get_subject_id(request.saml_session) if subject_id is None: logger.warning( f"The session does not contain the subject id for user {request.user}" ) logger.error( f"Looks like the user {subject_id} is not logged in any IdP/AA") return HttpResponseBadRequest("You are not logged in any IdP/AA") slo_req = saml2.samlp.LogoutRequest() slo_req.destination = subject_id.name_qualifier # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...) slo_req.attribute_consuming_service_index = "0" issuer = saml2.saml.Issuer() issuer.name_qualifier = client.config.entityid issuer.text = client.config.entityid issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity" slo_req.issuer = issuer # message id slo_req.id = saml2.s_utils.sid() slo_req.version = saml2.VERSION # "2.0" slo_req.issue_instant = saml2.time_util.instant() # oggetto slo_req.name_id = subject_id try: session_info = client.users.get_info_from(slo_req.name_id, subject_id.name_qualifier, False) except KeyError as e: logger.error(f"SPID Logout error: {e}") return HttpResponseRedirect("/") session_indexes = [session_info["session_index"]] # aggiungere session index if session_indexes: sis = [] for si in session_indexes: if isinstance(si, saml2.samlp.SessionIndex): sis.append(si) else: sis.append(saml2.samlp.SessionIndex(text=si)) slo_req.session_index = sis slo_req.protocol_binding = SAML2_DEFAULT_BINDING assertion_consumer_service_url = client.config._sp_endpoints[ "assertion_consumer_service"][0][0] slo_req.assertion_consumer_service_url = assertion_consumer_service_url slo_req_signed = client.sign( slo_req, sign_prepare=False, sign_alg=settings.SPID_SIG_ALG, digest_alg=settings.SPID_DIG_ALG, ) _req_str = slo_req_signed logger.debug( f"LogoutRequest to {subject_id.name_qualifier}: {repr_saml_request(_req_str)}" ) slo_location = client.metadata.single_logout_service( subject_id.name_qualifier, SAML2_DEFAULT_BINDING, "idpsso")[0]["location"] if not slo_location: error_message = f"Unable to know SLO endpoint in {subject_id.name_qualifier}" logger.error(error_message) return HttpResponse(error_message) http_info = client.apply_binding( SAML2_DEFAULT_BINDING, _req_str, slo_location, sign=True, sigalg=settings.SPID_SIG_ALG, ) state.sync() return HttpResponse(http_info["data"])
def spid_logout(request, config_loader_path=None, **kwargs): """SAML Logout Request initiator This view initiates the SAML2 Logout request using the pysaml2 library to create the LogoutRequest. """ state = StateCache(request.session) conf = get_config(config_loader_path, request) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session)) subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s', request.user) logger.error("Looks like the user %s is not logged in any IdP/AA", subject_id) return HttpResponseBadRequest("You are not logged in any IdP/AA") slo_req = saml2.samlp.LogoutRequest() binding = settings.SPID_DEFAULT_BINDING location_fixed = subject_id.name_qualifier location = location_fixed slo_req.destination = location_fixed # spid-testenv2 preleva l'attribute consumer service dalla authnRequest (anche se questo sta già nei metadati...) slo_req.attribute_consuming_service_index = "0" # import pdb; pdb.set_trace() issuer = saml2.saml.Issuer() issuer.name_qualifier = client.config.entityid issuer.text = client.config.entityid issuer.format = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity" slo_req.issuer = issuer # message id slo_req.id = saml2.s_utils.sid() slo_req.version = saml2.VERSION # "2.0" slo_req.issue_instant = saml2.time_util.instant() # oggetto slo_req.name_id = subject_id session_info = client.users.get_info_from(slo_req.name_id, subject_id.name_qualifier, False) session_indexes = [session_info['session_index']] # aggiungere session index if session_indexes: sis = [] for si in session_indexes: if isinstance(si, saml2.samlp.SessionIndex): sis.append(si) else: sis.append(saml2.samlp.SessionIndex(text=si)) slo_req.session_index = sis slo_req.protocol_binding = binding assertion_consumer_service_url = client.config._sp_endpoints['assertion_consumer_service'][0][0] slo_req.assertion_consumer_service_url = assertion_consumer_service_url slo_req_signed = client.sign(slo_req, sign_prepare=False, sign_alg=settings.SPID_ENC_ALG, digest_alg=settings.SPID_DIG_ALG) session_id = slo_req.id _req_str = slo_req_signed logger.debug('LogoutRequest to {}: {}'.format(subject_id.name_qualifier, repr_saml(_req_str))) # get slo from metadata slo_location = None # for k,v in client.metadata.metadata.items(): # idp_nq = v.entity.get(subject_id.name_qualifier) # if idp_nq: # slo_location = idp_nq['idpsso_descriptor'][0]['single_logout_service'][0]['location'] slo_location = client.metadata.single_logout_service(subject_id.name_qualifier, binding, "idpsso")[0]['location'] if not slo_location: logger.error('Unable to know SLO endpoint in {}'.format(subject_id.name_qualifier)) return HttpResponse(text_type(e)) http_info = client.apply_binding(binding, _req_str, slo_location, sign=True, sigalg=settings.SPID_ENC_ALG) state.sync() return HttpResponse(http_info['data'])
def test_valid_form(self): url, data, session_id = self._get_sp_authn_request() response = self.client.post(url, data, follow=True) # csrf_regexp = '<input type="hidden" name="csrfmiddlewaretoken" value="(?P<value>[a-zA-Z0-9+=]*)">' # login_data['csrfmiddlewaretoken'] = re.findall(csrf_regexp, response.content.decode())[0] self.login_data['password'] = '******' login_response = self.client.post(login_url, data=self.login_data, follow=True) # is there a SAML response? saml_resp = re.findall(samlresponse_form_regexp, login_response.content.decode()) assert saml_resp # test agreement screens self.sp.agreement_screen = 1 self.sp.save() login_response = self.client.post(login_url, data=self.login_data, follow=True) assert 'has requested the following informations' in login_response.content.decode( ) # don't show again agr_data = dict(dont_show_again=1, confirm=1) agr_url = reverse('uniauth:saml_user_agreement') agr_response = self.client.post(agr_url, data=agr_data, follow=True) # login again, agreement screen should not be displayed anymore # purge persistent_id from storage self.user.persistentid_set.all().delete() login_response = self.client.post(login_url, data=self.login_data, follow=True) saml_resp = re.findall(samlresponse_form_regexp, login_response.content.decode()) assert saml_resp # transient name_id format, remove persistent_id sp_conf = copy.deepcopy(SAML_SP_CONFIG) del (sp_conf['service']['sp']['name_id_format'][0]) self.sp_conf.load(sp_conf) url, data, session_id = self._get_sp_authn_request() response = self.client.post(url, data, follow=True) login_response = self.client.post(login_url, data=self.login_data, follow=True) # test logout session = self.client.session state = StateCache(session) identity_cache = IdentityCache(session) oq_cache = OutstandingQueriesCache(session) oq_cache.set(session_id, '/') outstanding_queries = oq_cache.outstanding_queries() client = Saml2Client(self.sp_conf, state_cache=state, identity_cache=IdentityCache(session)) response = client.parse_authn_request_response(saml_resp[0], BINDING_HTTP_POST, outstanding_queries) # this should take name_id dict # result = client.global_logout(session['SAML']['subject_id']) logout_result = client.global_logout(response.name_id) # is there a SAML response? saml_req_logout = re.findall(samlrequest_form_regexp, logout_result[idp_eid][1]['data']) assert saml_req_logout logout_response = self.client.post( logout_url, data={'SAMLRequest': saml_req_logout}, follow=True)
def logout_service(request, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session), logger=logger) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.logout_response(request.GET['SAMLResponse'], binding=BINDING_HTTP_REDIRECT) state.sync() if response and response[1] == '200 Ok': if next_page is None and hasattr(settings, 'LOGOUT_REDIRECT_URL'): next_page = settings.LOGOUT_REDIRECT_URL logger.debug('Performing django_logout with a next_page of %s' % next_page) return django_logout(request, next_page=next_page) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: response, success = client.logout_request(request.GET, subject_id) state.sync() if success: auth.logout(request) assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) elif response is not None: assert response[0][0] == 'Location' url = response[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')
def logout_service(request, config_loader_path=None, next_page=None, logout_error_template='djangosaml2/logout_error.html'): """SAML Logout Response endpoint The IdP will send the logout response to this view, which will process it with pysaml2 help and log the user out. Note that the IdP can request a logout even when we didn't initiate the process as a single logout request started by another SP. """ logger.debug('Logout service started') conf = get_config(config_loader_path, request) state = StateCache(request.session) client = Saml2Client(conf, state_cache=state, identity_cache=IdentityCache(request.session) ) if 'SAMLResponse' in request.GET: # we started the logout logger.debug('Receiving a logout response from the IdP') response = client.parse_logout_request_response(request.GET["SAMLResponse"], binding=BINDING_HTTP_REDIRECT) action = client.handle_logout_response(response) state.sync() if action and action[1] == '200 Ok': if next_page is None and hasattr(settings, 'LOGOUT_REDIRECT_URL'): next_page = settings.LOGOUT_REDIRECT_URL logger.debug('Performing django_logout with a next_page of %s' % next_page) return django_logout(request, next_page=next_page) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') elif 'SAMLRequest' in request.GET: # logout started by the IdP logger.debug('Receiving a logout request from the IdP') subject_id = _get_subject_id(request.session) if subject_id is None: logger.warning( 'The session does not contain the subject id for user %s. Performing local logout' % request.user) auth.logout(request) return render_to_response(logout_error_template, {}, context_instance=RequestContext(request)) else: response = client.handle_logout_request(request.GET["SAMLRequest"], subject_id, binding=BINDING_HTTP_REDIRECT) state.sync() if response['headers']: headers = response['headers'] auth.logout(request) assert headers[0][0] == 'Location' url = headers[0][1] return HttpResponseRedirect(url) else: logger.error('Unknown error during the logout') return HttpResponse('Error during logout') else: logger.error('No SAMLResponse or SAMLRequest parameter found') raise Http404('No SAMLResponse or SAMLRequest parameter found')