def assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting("SAML_ATTRIBUTE_MAPPING", {"uid": ("username",)}) create_unknown_user = create_unknown_user or get_custom_setting("SAML_CREATE_UNKNOWN_USER", True) logger.debug("Assertion Consumer Service started") conf = get_config(config_loader_path, request) if "SAMLResponse" not in request.POST: return HttpResponseBadRequest('Couldn\'t find "SAMLResponse" in POST data.') post = {"SAMLResponse": request.POST["SAMLResponse"]} client = Saml2Client(conf, identity_cache=IdentityCache(request.session), logger=logger) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() # process the authentication response response = client.response(post, outstanding_queries) if response is None: logger.error("SAML response is None") return HttpResponseBadRequest("SAML response has errors. Please check the logs") session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug("Trying to authenticate the user") user = auth.authenticate( session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user ) if user is None: logger.error("The user is None") return HttpResponseForbidden("Permission denied") auth.login(request, user) _set_subject_id(request.session, session_info["name_id"]) logger.debug("Sending the post_authenticated signal") post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from relay_state = request.POST.get("RelayState", "/") if not relay_state: logger.warning("The RelayState parameter exists but is empty") relay_state = settings.LOGIN_REDIRECT_URL logger.debug("Redirecting to the RelayState: " + relay_state) return HttpResponseRedirect(relay_state)
def custom_validation(self, response): conf = get_config(None, self.request) # Spid and SAML2 additional tests accepted_time_diff = conf.accepted_time_diff recipient = conf._sp_endpoints["assertion_consumer_service"][0][0] authn_context_classref = settings.SPID_AUTH_CONTEXT oq_cache = OutstandingQueriesCache(self.request.saml_session) in_response_to = oq_cache.outstanding_queries() logger.debug("in_response_to=%r", in_response_to) validator = Saml2ResponseValidator( authn_response=response.xmlstr, recipient=recipient, accepted_time_diff=accepted_time_diff, in_response_to=in_response_to, authn_context_class_ref=authn_context_classref, return_addrs=response.return_addrs, ) validator.run()
def post(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) attribute_mapping = get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = get_custom_setting( 'SAML_CREATE_UNKNOWN_USER', True) conf = get_config(request=request) client = Saml2Client(conf, identity_cache=IdentityCache(request.session)) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() xmlstr = serializer.validated_data['SAMLResponse'] # process the authentication response try: response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries) except Exception as e: if isinstance(e, StatusRequestDenied): return login_failed(_('Authentication request has been denied by identity provider. ' 'Please check your credentials.')) logger.error('SAML response parsing failed %s' % e) return login_failed(_('SAML2 response has errors.')) if response is None: logger.error('SAML response is None') return login_failed(_('SAML response has errors. Please check the logs')) if response.assertion is None: logger.error('SAML response assertion is None') return login_failed(_('SAML response has errors. Please check the logs')) session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() user = auth.authenticate( session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user, ) if user is None: return login_failed(_('SAML2 authentication failed.')) registration_method = settings.WALDUR_AUTH_SAML2.get('name', 'saml2') if user.registration_method != registration_method: user.registration_method = registration_method user.save(update_fields=['registration_method']) # required for validating SAML2 logout requests auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug('User %s authenticated via SSO.', user) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) token = self.refresh_token(user) logger.info('Authenticated with SAML token. Returning token for successful login of user %s', user) event_logger.saml2_auth.info( 'User {user_username} with full name {user_full_name} logged in successfully with SAML2.', event_type='auth_logged_in_with_saml2', event_context={'user': user} ) return login_completed(token.key, 'saml2')
def assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user or get_custom_setting( 'SAML_CREATE_UNKNOWN_USER', True) logger.debug('Assertion Consumer Service started') conf = get_config(config_loader_path, request) if 'SAMLResponse' not in request.POST: return HttpResponseBadRequest( 'Couldn\'t find "SAMLResponse" in POST data.') xmlstr = request.POST['SAMLResponse'] client = Saml2Client(conf, identity_cache=IdentityCache(request.session)) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() try: response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries) except StatusError: return render(request, 'djangosaml2/login_error.html', status=403) except MissingKey: logger.error('MissingKey error in ACS') return HttpResponseForbidden( "The Identity Provider is not configured correctly: " "the certificate key is missing") if response is None: logger.error('SAML response is None') return HttpResponseBadRequest( "SAML response has errors. Please check the logs") session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user') user = auth.authenticate(session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) if user is None: logger.error('The user is None') return render(request, 'djangosaml2/permission_denied.html', status=403) auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from default_relay_state = get_custom_setting('ACS_DEFAULT_REDIRECT_URL', settings.LOGIN_REDIRECT_URL) relay_state = request.POST.get('RelayState', default_relay_state) if not relay_state: logger.warning('The RelayState parameter exists but is empty') relay_state = default_relay_state if not is_safe_url(url=relay_state, host=request.get_host()): came_from = settings.LOGIN_REDIRECT_URL logger.debug('Redirecting to the RelayState: %s', relay_state) return HttpResponseRedirect(relay_state)
def assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user or get_custom_setting( 'SAML_CREATE_UNKNOWN_USER', True) logger.debug('Assertion Consumer Service started') conf = get_config(config_loader_path, request) if 'SAMLResponse' not in request.POST: return HttpResponseBadRequest( 'Couldn\'t find "SAMLResponse" in POST data.') xmlstr = request.POST['SAMLResponse'] client = Saml2Client(conf, identity_cache=IdentityCache(request.session)) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() # process the authentication response response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries) if response is None: logger.error('SAML response is None') return HttpResponseBadRequest( "SAML response has errors. Please check the logs") session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user') user = auth.authenticate(session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) if user is None: logger.error('The user is None') return HttpResponseForbidden("Permission denied") auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from default_relay_state = get_custom_setting('ACS_DEFAULT_REDIRECT_URL', settings.LOGIN_REDIRECT_URL) relay_state = request.POST.get('RelayState', default_relay_state) if not relay_state: logger.warning('The RelayState parameter exists but is empty') relay_state = default_relay_state logger.debug('Redirecting to the RelayState: %s', relay_state) return HttpResponseRedirect(relay_state)
def assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting('SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user if create_unknown_user is not None else \ get_custom_setting('SAML_CREATE_UNKNOWN_USER', True) conf = get_config(config_loader_path, request) try: xmlstr = request.POST['SAMLResponse'] except KeyError: logger.warning('Missing "SAMLResponse" parameter in POST data.') raise SuspiciousOperation client = Saml2Client(conf, identity_cache=IdentityCache(request.session)) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() try: response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries) except (StatusError, ToEarly): logger.exception("Error processing SAML Assertion.") return fail_acs_response(request) except ResponseLifetimeExceed: logger.info("SAML Assertion is no longer valid. Possibly caused by network delay or replay attack.", exc_info=True) return fail_acs_response(request) except SignatureError: logger.info("Invalid or malformed SAML Assertion.", exc_info=True) return fail_acs_response(request) except StatusAuthnFailed: logger.info("Authentication denied for user by IdP.", exc_info=True) return fail_acs_response(request) except StatusRequestDenied: logger.warning("Authentication interrupted at IdP.", exc_info=True) return fail_acs_response(request) except StatusNoAuthnContext: logger.warning("Missing Authentication Context from IdP.", exc_info=True) return fail_acs_response(request) except MissingKey: logger.exception("SAML Identity Provider is not configured correctly: certificate key is missing!") return fail_acs_response(request) except UnsolicitedResponse: logger.exception("Received SAMLResponse when no request has been made.") return fail_acs_response(request) if response is None: logger.warning("Invalid SAML Assertion received (unknown error).") return fail_acs_response(request, status=400, exc_class=SuspiciousOperation) session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user. Session info: %s', session_info) user = auth.authenticate(request=request, session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) if user is None: logger.warning("Could not authenticate user received in SAML Assertion. Session info: %s", session_info) raise PermissionDenied auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug("User %s authenticated via SSO.", user) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from default_relay_state = get_custom_setting('ACS_DEFAULT_REDIRECT_URL', settings.LOGIN_REDIRECT_URL) relay_state = request.POST.get('RelayState', default_relay_state) if not relay_state: logger.warning('The RelayState parameter exists but is empty') relay_state = default_relay_state if not is_safe_url_compat(url=relay_state, allowed_hosts={request.get_host()}): relay_state = settings.LOGIN_REDIRECT_URL logger.debug('Redirecting to the RelayState: %s', relay_state) return HttpResponseRedirect(relay_state)
def assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user if create_unknown_user is not None else \ get_custom_setting('SAML_CREATE_UNKNOWN_USER', True) conf = get_config(config_loader_path, request) try: xmlstr = request.POST['SAMLResponse'] except KeyError: logger.warning('Missing "SAMLResponse" parameter in POST data.') raise SuspiciousOperation client = Saml2Client(conf, identity_cache=IdentityCache(request.session)) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() try: response = client.parse_authn_request_response(xmlstr, BINDING_HTTP_POST, outstanding_queries) except (StatusError, ToEarly): logger.exception("Error processing SAML Assertion.") return fail_acs_response(request) except ResponseLifetimeExceed: logger.info( "SAML Assertion is no longer valid. Possibly caused by network delay or replay attack.", exc_info=True) return fail_acs_response(request) except SignatureError: logger.info("Invalid or malformed SAML Assertion.", exc_info=True) return fail_acs_response(request) except StatusAuthnFailed: logger.info("Authentication denied for user by IdP.", exc_info=True) return fail_acs_response(request) except StatusRequestDenied: logger.warning("Authentication interrupted at IdP.", exc_info=True) return fail_acs_response(request) except MissingKey: logger.exception( "SAML Identity Provider is not configured correctly: certificate key is missing!" ) return fail_acs_response(request) except UnsolicitedResponse: logger.exception( "Received SAMLResponse when no request has been made.") return fail_acs_response(request) if response is None: logger.warning("Invalid SAML Assertion received (unknown error).") return fail_acs_response(request, status=400, exc_class=SuspiciousOperation) session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user. Session info: %s', session_info) user = auth.authenticate(request=request, session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) if user is None: logger.warning( "Could not authenticate user received in SAML Assertion. Session info: %s", session_info) raise PermissionDenied auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug("User %s authenticated via SSO.", user) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from default_relay_state = get_custom_setting('ACS_DEFAULT_REDIRECT_URL', settings.LOGIN_REDIRECT_URL) relay_state = request.POST.get('RelayState', default_relay_state) if not relay_state: logger.warning('The RelayState parameter exists but is empty') relay_state = default_relay_state if not is_safe_url_compat(url=relay_state, allowed_hosts={request.get_host()}): relay_state = settings.LOGIN_REDIRECT_URL logger.debug('Redirecting to the RelayState: %s', relay_state) return HttpResponseRedirect(relay_state)
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 assertion_consumer_service(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ attribute_mapping = attribute_mapping or get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user or get_custom_setting( 'SAML_CREATE_UNKNOWN_USER', True) logger.debug('Assertion Consumer Service started') conf = get_config(config_loader_path, request) if 'SAMLResponse' not in request.POST: return HttpResponseBadRequest( 'Couldn\'t find "SAMLResponse" in POST data.') post = {'SAMLResponse': request.POST['SAMLResponse']} client = Saml2Client(conf, identity_cache=IdentityCache(request.session), logger=logger) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() # process the authentication response response = client.response(post, outstanding_queries) if response is None: logger.error('SAML response is None') return HttpResponseBadRequest( "SAML response has errors. Please check the logs") session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user') user = auth.authenticate(session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) if user is None: logger.error('The user is None') return HttpResponseForbidden("Permission denied") auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from relay_state = request.POST.get('RelayState', '/') if not relay_state: logger.warning('The RelayState parameter exists but is empty') relay_state = settings.LOGIN_REDIRECT_URL logger.debug('Redirecting to the RelayState: ' + relay_state) return HttpResponseRedirect(relay_state)
def assertion_consumer_service_view(request, config_loader_path=None, attribute_mapping=None, create_unknown_user=None): """SAML Authorization Response endpoint The IdP will send its response to this view, which will process it with pysaml2 help and log the user in using the custom Authorization backend djangosaml2.backends.Saml2Backend that should be enabled in the settings.py """ logger.debug('Assertion Consumer Service started') attribute_mapping = attribute_mapping or get_custom_setting( 'SAML_ATTRIBUTE_MAPPING', {'uid': ('username', )}) create_unknown_user = create_unknown_user or get_custom_setting( 'SAML_CREATE_UNKNOWN_USER', True) logger.debug('Assertion Consumer Service started') conf = get_config(config_loader_path, request) if 'SAMLResponse' not in request.POST: return HttpResponseBadRequest( 'Couldn\'t find "SAMLResponse" in POST data.') post = {'SAMLResponse': request.POST['SAMLResponse']} client = Saml2Client(conf, identity_cache=IdentityCache(request.session), logger=logger) oq_cache = OutstandingQueriesCache(request.session) outstanding_queries = oq_cache.outstanding_queries() # process the authentication response try: response = client.response(post, outstanding_queries) except Exception as e: logger.error('Error while authenticating. %s' % e) return HttpResponseRedirect('/saml2/login_error') if response is None: logger.error('SAML response is None') return HttpResponse("SAML response has errors. Please check the logs") session_id = response.session_id() oq_cache.delete(session_id) # authenticate the remote user session_info = response.session_info() if callable(attribute_mapping): attribute_mapping = attribute_mapping() if callable(create_unknown_user): create_unknown_user = create_unknown_user() logger.debug('Trying to authenticate the user') try: user = auth.authenticate(session_info=session_info, attribute_mapping=attribute_mapping, create_unknown_user=create_unknown_user) except Exception as e: logger.error('Error while authenticating. %s' % e) return HttpResponseRedirect('/saml2/login_error') if user is None: logger.error('The user is None') return HttpResponseRedirect('/saml2/login_error') #return HttpResponse("There were problems trying to authenticate the user") auth.login(request, user) _set_subject_id(request.session, session_info['name_id']) _set_saml2_auth_used(request.session, True) logger.debug('Sending the post_authenticated signal') post_authenticated.send_robust(sender=user, session_info=session_info) # redirect the user to the view where he came from #relay_state = request.POST.get('RelayState', '/login') relay_state = '/login' logger.debug('Redirecting to the RelayState: ' + relay_state) return HttpResponseRedirect(relay_state)