def testCreateRequestAuthContextComparision(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request with defined AuthnContextComparison """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD, inflated) self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated) saml_settings['security']['requestedAuthnContext'] = True settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn('RequestedAuthnContext Comparison="exact"', inflated) saml_settings['security']['requestedAuthnContextComparison'] = 'minimun' settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn('RequestedAuthnContext Comparison="minimun"', inflated)
def testCreateRequestIsPassive(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request with IsPassive="true" """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( authn_request_encoded)) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertNotIn('IsPassive="true"', inflated) authn_request_2 = OneLogin_Saml2_Authn_Request(settings, False, False) authn_request_encoded_2 = authn_request_2.get_request() inflated_2 = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( authn_request_encoded_2)) self.assertRegexpMatches(inflated_2, '^<samlp:AuthnRequest') self.assertNotIn('IsPassive="true"', inflated_2) authn_request_3 = OneLogin_Saml2_Authn_Request(settings, False, True) authn_request_encoded_3 = authn_request_3.get_request() inflated_3 = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( authn_request_encoded_3)) self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest') self.assertIn('IsPassive="true"', inflated_3)
def testCreateRequestSetNameIDPolicy(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request with and without NameIDPolicy """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn('<samlp:NameIDPolicy', inflated) authn_request_2 = OneLogin_Saml2_Authn_Request(settings, False, False, True) authn_request_encoded_2 = authn_request_2.get_request() decoded_2 = b64decode(authn_request_encoded_2) inflated_2 = decompress(decoded_2, -15) self.assertRegexpMatches(inflated_2, '^<samlp:AuthnRequest') self.assertIn('<samlp:NameIDPolicy', inflated_2) authn_request_3 = OneLogin_Saml2_Authn_Request(settings, False, False, False) authn_request_encoded_3 = authn_request_3.get_request() decoded_3 = b64decode(authn_request_encoded_3) inflated_3 = decompress(decoded_3, -15) self.assertRegexpMatches(inflated_3, '^<samlp:AuthnRequest') self.assertNotIn('<samlp:NameIDPolicy', inflated_3)
def testAttributeConsumingService(self): """ Tests that the attributeConsumingServiceIndex is present as an attribute """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( authn_request_encoded)) self.assertNotIn('AttributeConsumingServiceIndex="1"', inflated) saml_settings = self.loadSettingsJSON('settings4.json') settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( authn_request_encoded)) self.assertRegex(inflated, 'AttributeConsumingServiceIndex="1"')
def testCreateRequest(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) settings._OneLogin_Saml2_Settings__organization = { u'en-US': { u'url': u'http://sp.example.com', u'name': u'sp_test' } } authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated) saml_settings['organization'] = {} settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated)
def testCreateRequest(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) settings._organization = { u'en-US': { u'url': u'http://sp.example.com', u'name': u'sp_test' } } authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated) saml_settings['organization'] = {} settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated)
def testCreateRequestSubject(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request with and without Subject """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertNotIn('<saml:Subject>', inflated) authn_request_2 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req='*****@*****.**') authn_request_encoded_2 = authn_request_2.get_request() inflated_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_2)) self.assertRegex(inflated_2, '^<samlp:AuthnRequest') self.assertIn('<saml:Subject>', inflated_2) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">[email protected]</saml:NameID>', inflated_2) self.assertIn('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', inflated_2) saml_settings['sp']['NameIDFormat'] = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' settings = OneLogin_Saml2_Settings(saml_settings) authn_request_3 = OneLogin_Saml2_Authn_Request(settings, name_id_value_req='*****@*****.**') authn_request_encoded_3 = authn_request_3.get_request() inflated_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded_3)) self.assertRegex(inflated_3, '^<samlp:AuthnRequest') self.assertIn('<saml:Subject>', inflated_3) self.assertIn('Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">[email protected]</saml:NameID>', inflated_3) self.assertIn('<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">', inflated_3)
def testGetXML(self): """ Tests that we can get the request XML directly without going through intermediate steps """ saml_settings = self.loadSettingsJSON() saml_settings['organization'] = { u'en-US': { u'url': u'http://sp.example.com', u'name': u'sp_test', u'displayname': u'SP test', } } settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) inflated = authn_request.get_xml() self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn('ProviderName="SP test"', inflated) saml_settings['organization'] = {} settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) inflated = authn_request.get_xml() self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated)
def testIdGeneration(self): saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) anId = authn_request.get_id() self.assertTrue(anId.startswith('ONELOGIN_'), anId) with mocked_generate_unique_id(lambda: 'Yes'): authn_request = OneLogin_Saml2_Authn_Request(settings) anId = authn_request.get_id() self.assertEquals('Yes', anId)
def testCreateRequestAuthContext(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request with defined AuthContext """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD, inflated) self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated) saml_settings['security']['requestedAuthnContext'] = True settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated) self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated) del saml_settings['security']['requestedAuthnContext'] settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated) self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated) saml_settings['security']['requestedAuthnContext'] = False settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertNotIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated) self.assertNotIn(OneLogin_Saml2_Constants.AC_X509, inflated) saml_settings['security']['requestedAuthnContext'] = (OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, OneLogin_Saml2_Constants.AC_X509) settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertIn(OneLogin_Saml2_Constants.AC_PASSWORD_PROTECTED, inflated) self.assertIn(OneLogin_Saml2_Constants.AC_X509, inflated)
def testCreateEncSAMLRequest(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request """ settings = self.loadSettingsJSON() settings['organization'] = { 'es': { 'name': 'sp_prueba', 'displayname': 'SP prueba', 'url': 'http://sp.example.com' } } settings['security']['wantNameIdEncrypted'] = True settings = OneLogin_Saml2_Settings(settings) authn_request = OneLogin_Saml2_Authn_Request(settings) parameters = { 'SAMLRequest': authn_request.get_request() } auth_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SSOService.php', parameters, True) self.assertRegexpMatches(auth_url, '^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=') exploded = urlparse(auth_url) exploded = parse_qs(exploded[4]) payload = exploded['SAMLRequest'][0] decoded = b64decode(payload) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertRegexpMatches(inflated, 'AssertionConsumerServiceURL="http://stuff.com/endpoints/endpoints/acs.php"') self.assertRegexpMatches(inflated, '<saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>') self.assertRegexpMatches(inflated, 'Format="urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"') self.assertRegexpMatches(inflated, 'ProviderName="SP prueba"')
def login(self, return_to=None, force_authn=False, is_passive=False): """ Initiates the SSO process. :param return_to: Optional argument. The target URL the user should be redirected to after login. :type return_to: string :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'. :type force_authn: string :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'. :type is_passive: string :returns: Redirection url """ authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive) saml_request = authn_request.get_request() parameters = {'SAMLRequest': saml_request} if return_to is not None: parameters['RelayState'] = return_to else: parameters[ 'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query( self.__request_data) security = self.__settings.get_security_data() if security.get('authnRequestsSigned', False): parameters['SigAlg'] = OneLogin_Saml2_Constants.RSA_SHA1 parameters['Signature'] = self.build_request_signature( saml_request, parameters['RelayState']) return self.redirect_to(self.get_sso_url(), parameters)
def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True): """ Initiates the SSO process. :param return_to: Optional argument. The target URL the user should be redirected to after login. :type return_to: string :param force_authn: Optional argument. When true the AuthNRequest will set the ForceAuthn='true'. :type force_authn: bool :param is_passive: Optional argument. When true the AuthNRequest will set the Ispassive='true'. :type is_passive: bool :param set_nameid_policy: Optional argument. When true the AuthNRequest will set a nameIdPolicy element. :type set_nameid_policy: bool :returns: Redirection URL :rtype: string """ authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy) self.__last_request = authn_request.get_xml() self.__last_request_id = authn_request.get_id() saml_request = authn_request.get_request() parameters = {'SAMLRequest': saml_request} if return_to is not None: parameters['RelayState'] = return_to else: parameters['RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query(self.__request_data) security = self.__settings.get_security_data() if security.get('authnRequestsSigned', False): parameters['SigAlg'] = security['signatureAlgorithm'] parameters['Signature'] = self.build_request_signature(saml_request, parameters['RelayState'], security['signatureAlgorithm']) return self.redirect_to(self.get_sso_url(), parameters)
def testGetID(self): """ Tests the get_id method of the OneLogin_Saml2_Authn_Request. """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) document = OneLogin_Saml2_XML.to_etree(inflated) self.assertEqual(authn_request.get_id(), document.get('ID', None))
def testSignedHttpPostBinding(self): """ Test to use the binding: urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST To sign a samlp:AuthnRequest you need to have a private key set for the service provider. To verify the assertion is signed correct you can also use the xmlsec1 command line tool: xmlsec1 --verify --id-attr:ID AuthnRequest --trusted-pem tests/certs/example.com/example.crt authn_signed_assertion.xml """ filename = join(dirname(__file__), '..', '..', '..', 'settings', 'example_settings_http_post_binding.json') stream = open(filename, 'r') settings = json.load(stream) stream.close() settings = OneLogin_Saml2_Settings(settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) sample_output_directory = join(dirname(__file__), '..', '..', '..', 'sample_output') if not os.path.exists(sample_output_directory): os.makedirs(sample_output_directory) with open( join(dirname(__file__), '..', '..', '..', 'sample_output/authn_signed_assertion.xml'), 'wb') as f: f.write(inflated) # Turn the inflated xml (which is just a string) into a in memory XML document doc = fromstring(inflated) # Verification of enveloped signature node = doc.find(".//{%s}Signature" % xmlsec.DSigNs) key_file = join(dirname(__file__), '..', '..', '..', 'certs/example.com', 'example.pubkey') dsigCtx = xmlsec.DSigCtx() signKey = xmlsec.Key.load(key_file, xmlsec.KeyDataFormatPem, None) signKey.name = 'example.pubkey' # Note: the assignment below effectively copies the key dsigCtx.signKey = signKey # Add ID attributes different from xml:id # See the Notes on https://pypi.python.org/pypi/dm.xmlsec.binding/1.3.2 xmlsec.addIDs(doc, ["ID"]) # This raises an exception if the document does not verify dsigCtx.verify(node)
def testAttributeConsumingService(self): """ Tests that the attributeConsumingServiceIndex is present as an attribute """ saml_settings = self.loadSettingsJSON() settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertNotIn('AttributeConsumingServiceIndex="1"', inflated) saml_settings = self.loadSettingsJSON('settings4.json') settings = OneLogin_Saml2_Settings(saml_settings) authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) self.assertRegexpMatches(inflated, 'AttributeConsumingServiceIndex="1"')
def get_context_data(self, **kwargs): complete_url = reverse('social:complete', args=("saml", )) saml_backend = load_backend( load_strategy(self.request), "saml", redirect_uri=complete_url, ) idp = saml_backend.get_idp('pzgovpl') config = saml_backend.generate_saml_config(idp) saml_settings = OneLogin_Saml2_Settings(config, sp_validation_only=True) log.debug("saml_settings: %s", saml_settings) # saml_settings = OneLogin_Saml2_Settings(custom_base_path=settings.SAML_FOLDER) kwargs['SAMLRequest'] = OneLogin_Saml2_Authn_Request( saml_settings).get_request() return super().get_context_data(**kwargs)
def testCreateDeflatedSAMLRequestURLParameter(self): """ Tests the OneLogin_Saml2_Authn_Request Constructor. The creation of a deflated SAML Request """ authn_request = OneLogin_Saml2_Authn_Request(self.__settings) parameters = { 'SAMLRequest': authn_request.get_request() } auth_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SSOService.php', parameters, True) self.assertRegex(auth_url, '^http://idp\.example\.com\/SSOService\.php\?SAMLRequest=') exploded = urlparse(auth_url) exploded = parse_qs(exploded[4]) payload = exploded['SAMLRequest'][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) self.assertRegex(inflated, '^<samlp:AuthnRequest')
def _get_authn_request(self): if not self._is_enabled(): return None return OneLogin_Saml2_Authn_Request(saml2_settings())
def login(self, return_to=None, force_authn=False, is_passive=False, set_nameid_policy=True): """ Initiates the SSO process. :param return_to: Optional argument. The target URL the user should be redirected to after login. :type return_to: string :param force_authn: Optional argument. When true the AuthNReuqest will set the ForceAuthn='true'. :type force_authn: bool :param is_passive: Optional argument. When true the AuthNReuqest will set the Ispassive='true'. :type is_passive: bool :param set_nameid_policy: Optional argument. When true the AuthNReuqest will set a nameIdPolicy element. :type set_nameid_policy: bool :returns: Redirection url :rtype: string """ authn_request = OneLogin_Saml2_Authn_Request(self.__settings, force_authn, is_passive, set_nameid_policy) self.__last_request_id = authn_request.get_id() saml_request = authn_request.get_request() parameters = {'SAMLRequest': saml_request} if return_to is not None: parameters['RelayState'] = return_to else: parameters[ 'RelayState'] = OneLogin_Saml2_Utils.get_self_url_no_query( self.__request_data) security = self.__settings.get_security_data() if security.get('authnRequestsSigned', False): parameters['SigAlg'] = security['signatureAlgorithm'] parameters['Signature'] = self.build_request_signature( saml_request, parameters['RelayState'], security['signatureAlgorithm']) # HTTP-POST binding requires generation of a form if self.get_settings().get_idp_data()['singleSignOnService'].get( 'binding', None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST': log.debug("Generating AuthnRequest HTTP-POST binding form") # Return HTML form template_file = open( join(dirname(__file__), 'templates/authn_request.html')) template_text = template_file.read() template = Template(template_text) context = { 'sso_url': self.get_sso_url(), 'saml_request': saml_request, 'relay_state': parameters['RelayState'] } html = template.render(context) log.debug("Generated HTML: %s" % html) return html return self.redirect_to(self.get_sso_url(), parameters)