def testGetSPMetadataWithx509certNew(self): """ Tests the getSPMetadata method of the OneLogin_Saml2_Settings Case with x509certNew """ settings_info = self.loadSettingsJSON('settings7.json') settings_info['security']['wantNameIdEncrypted'] = False settings_info['security']['wantAssertionsEncrypted'] = False settings = OneLogin_Saml2_Settings(settings_info) metadata = compat.to_string(settings.get_sp_metadata()) self.assertNotEqual(len(metadata), 0) self.assertIn('<md:SPSSODescriptor', metadata) self.assertEqual(2, metadata.count('<md:KeyDescriptor')) self.assertEqual(2, metadata.count('<md:KeyDescriptor use="signing"')) self.assertEqual(0, metadata.count('<md:KeyDescriptor use="encryption"')) settings_info['security']['wantNameIdEncrypted'] = True settings_info['security']['wantAssertionsEncrypted'] = False settings = OneLogin_Saml2_Settings(settings_info) metadata = compat.to_string(settings.get_sp_metadata()) self.assertEqual(4, metadata.count('<md:KeyDescriptor')) self.assertEqual(2, metadata.count('<md:KeyDescriptor use="signing"')) self.assertEqual(2, metadata.count('<md:KeyDescriptor use="encryption"'))
def testProcessResponseInvalidRequestId(self): """ Tests the process_response method of the OneLogin_Saml2_Auth class Case Invalid Response, Invalid requestID """ request_data = self.get_request() message = self.file_contents(join(self.data_path, 'responses', 'unsigned_response.xml.base64')) plain_message = compat.to_string(b64decode(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/acs.php', current_url) del request_data['get_data'] request_data['post_data'] = { 'SAMLResponse': compat.to_string(b64encode(compat.to_bytes(plain_message))) } auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) request_id = 'invalid' auth.process_response(request_id) self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason()) auth.set_strict(True) auth.process_response(request_id) self.assertEqual(auth.get_errors(), ['invalid_response']) self.assertEqual('The InResponseTo of the Response: _57bcbf70-7b1f-012e-c821-782bcb13bb38, does not match the ID of the AuthNRequest sent by the SP: invalid', auth.get_last_error_reason()) valid_request_id = '_57bcbf70-7b1f-012e-c821-782bcb13bb38' auth.process_response(valid_request_id) self.assertEqual('No Signature found. SAML Response rejected', auth.get_last_error_reason())
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() 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 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 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 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 testGetSPMetadata(self): """ Tests the getSPMetadata method of the OneLogin_Saml2_Settings Case unsigned metadata """ settings_info = self.loadSettingsJSON() settings_info['security']['wantNameIdEncrypted'] = False settings_info['security']['wantAssertionsEncrypted'] = False settings = OneLogin_Saml2_Settings(settings_info) metadata = compat.to_string(settings.get_sp_metadata()) self.assertNotEqual(len(metadata), 0) self.assertIn('<md:SPSSODescriptor', metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', metadata) self.assertIn('AuthnRequestsSigned="false"', metadata) self.assertIn('WantAssertionsSigned="false"', metadata) self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://stuff.com/endpoints/endpoints/acs.php" index="1"/>', metadata) self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://stuff.com/endpoints/endpoints/sls.php"/>', metadata) self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', metadata) self.assertEqual(1, metadata.count('<md:KeyDescriptor')) self.assertEqual(1, metadata.count('<md:KeyDescriptor use="signing"')) self.assertEqual(0, metadata.count('<md:KeyDescriptor use="encryption"')) settings_info['security']['wantNameIdEncrypted'] = False settings_info['security']['wantAssertionsEncrypted'] = True settings = OneLogin_Saml2_Settings(settings_info) metadata = compat.to_string(settings.get_sp_metadata()) self.assertEqual(2, metadata.count('<md:KeyDescriptor')) self.assertEqual(1, metadata.count('<md:KeyDescriptor use="signing"')) self.assertEqual(1, metadata.count('<md:KeyDescriptor use="encryption"'))
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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) 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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) 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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertIn('RequestedAuthnContext Comparison="minimun"', inflated)
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() 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('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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegex(inflated, '^<samlp:AuthnRequest') self.assertIn('RequestedAuthnContext Comparison="minimun"', 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._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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) 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() inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(authn_request_encoded)) self.assertRegexpMatches(inflated, '^<samlp:AuthnRequest') self.assertNotIn('ProviderName="SP test"', inflated)
def testLoginIsPassive(self): """ Tests the login method of the OneLogin_Saml2_Auth class Case Logout with no parameters. A AuthN Request is built with IsPassive and redirect executed """ settings_info = self.loadSettingsJSON() return_to = u'http://example.com/returnto' settings_info['idp']['singleSignOnService']['url'] auth = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url = auth.login(return_to) parsed_query = parse_qs(urlparse(target_url)[4]) sso_url = settings_info['idp']['singleSignOnService']['url'] self.assertIn(sso_url, target_url) self.assertIn('SAMLRequest', parsed_query) request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query['SAMLRequest'][0])) self.assertNotIn('IsPassive="true"', request) auth_2 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_2 = auth_2.login(return_to, False, False) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) self.assertIn(sso_url, target_url_2) self.assertIn('SAMLRequest', parsed_query_2) request_2 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_2['SAMLRequest'][0])) self.assertNotIn('IsPassive="true"', request_2) auth_3 = OneLogin_Saml2_Auth(self.get_request(), old_settings=settings_info) target_url_3 = auth_3.login(return_to, False, True) parsed_query_3 = parse_qs(urlparse(target_url_3)[4]) self.assertIn(sso_url, target_url_3) self.assertIn('SAMLRequest', parsed_query_3) request_3 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(parsed_query_3['SAMLRequest'][0])) self.assertIn('IsPassive="true"', request_3)
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 testProcessSLOResponseValidDeletingSession(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class Case Valid Logout Response, validating deleting the local session """ request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) # FIXME # if (!isset($_SESSION)) { # $_SESSION = array(); # } # $_SESSION['samltest'] = true; # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) request_data['get_data']['SAMLResponse'] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.set_strict(True) auth.process_slo(False) self.assertEqual(len(auth.get_errors()), 0)
def __init__(self, settings, response=None, method='redirect'): """ Constructs a Logout Response object (Initialize params from settings and if provided load the Logout Response. Arguments are: * (OneLogin_Saml2_Settings) settings. Setting data * (string) response. An UUEncoded SAML Logout response from the IdP. """ self.__settings = settings self.__error = None self.__is_post = method == 'post' self.id = None if response is not None: if method == 'redirect': self.__logout_response = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( response, ignore_zip=True)) elif method == 'post': self.__logout_response = OneLogin_Saml2_Utils.b64decode( response) else: raise ValueError("Wrong value %r for argument 'method'." % method) self.document = OneLogin_Saml2_XML.to_etree(self.__logout_response) self.id = self.document.get('ID', None) if method != "redirect" and method != "post": raise ValueError("Wrong value %r for argument 'method'." % method)
def testIsValid(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ request_data = { 'http_host': 'example.com', 'script_name': 'index.html', 'get_data': {} } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) response = OneLogin_Saml2_Logout_Response(settings, message) self.assertTrue(response.is_valid(request_data)) settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) try: valid = response_2.is_valid(request_data) self.assertFalse(valid) except Exception as e: self.assertIn('The LogoutRequest was received at', str(e)) plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) self.assertTrue(response_3.is_valid(request_data))
def generateAndCheckMetadata(self, settings): """ Helper method: Given some settings, generate metadata and validate it """ if not isinstance(settings, OneLogin_Saml2_Settings): settings = OneLogin_Saml2_Settings(settings) metadata = compat.to_string(settings.get_sp_metadata()) self.assertIn('<md:SPSSODescriptor', metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', metadata) self.assertIn('AuthnRequestsSigned="false"', metadata) self.assertIn('WantAssertionsSigned="false"', metadata) self.assertIn( '<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://stuff.com/endpoints/endpoints/acs.php" index="1"/>', metadata) self.assertIn( '<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://stuff.com/endpoints/endpoints/sls.php"/>', metadata) self.assertIn( '<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', metadata) self.assertIn( '<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', metadata) self.assertIn( '<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', metadata) self.assertIn('<ds:Reference', metadata) self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', metadata)
def testConstructorEncryptIdUsingX509certMulti(self): """ Tests the OneLogin_Saml2_LogoutRequest Constructor. Case: Able to generate encryptedID with MultiCert """ settings_info = self.loadSettingsJSON('settings8.json') settings_info['security']['nameIdEncrypted'] = True settings = OneLogin_Saml2_Settings(settings_info) logout_request = OneLogin_Saml2_Logout_Request(settings) parameters = {'SAMLRequest': logout_request.get_request()} logout_url = OneLogin_Saml2_Utils.redirect( 'http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegex( logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=' ) url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) payload = exploded['SAMLRequest'][0] inflated = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) self.assertRegex(inflated, '^<samlp:LogoutRequest') self.assertRegex(inflated, '<saml:EncryptedID>')
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.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') self.assertRegex(inflated, 'AssertionConsumerServiceURL="http://stuff.com/endpoints/endpoints/acs.php">') self.assertRegex(inflated, '<saml:Issuer>http://stuff.com/endpoints/metadata.php</saml:Issuer>') self.assertRegex(inflated, 'Format="urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"') self.assertRegex(inflated, 'ProviderName="SP prueba"')
def testIsValid(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ request_data = { 'http_host': 'example.com', 'script_name': 'index.html', 'get_data': {} } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents( join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) response = OneLogin_Saml2_Logout_Response(settings, message) self.assertTrue(response.is_valid(request_data)) settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) with self.assertRaisesRegexp(Exception, 'The LogoutResponse was received at'): response_2.is_valid(request_data, raise_exceptions=True) plain_message = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace( 'http://stuff.com/endpoints/endpoints/sls.php', current_url) message_3 = OneLogin_Saml2_Utils.deflate_and_base64_encode( plain_message) response_3 = OneLogin_Saml2_Logout_Response(settings, message_3) self.assertTrue(response_3.is_valid(request_data))
def testProcessSLORequestSignedResponse(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class Case Valid Logout Request, validating the relayState, a signed LogoutResponse is created and a redirection executed """ settings_info = self.loadSettingsJSON() settings_info['security']['logoutResponseSigned'] = True request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) request_data['get_data']['SAMLRequest'] = message request_data['get_data']['RelayState'] = 'http://relaystate.com' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) auth.set_strict(True) target_url = auth.process_slo(False) parsed_query = parse_qs(urlparse(target_url)[4]) slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertIn(slo_url, target_url) self.assertIn('SAMLResponse', parsed_query) self.assertIn('RelayState', parsed_query) self.assertIn('SigAlg', parsed_query) self.assertIn('Signature', parsed_query) self.assertIn('http://relaystate.com', parsed_query['RelayState']) self.assertIn(OneLogin_Saml2_Constants.RSA_SHA1, parsed_query['SigAlg'])
def testIsInValidIssuer(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse Case invalid Issuer """ request_data = { 'http_host': 'example.com', 'script_name': 'index.html', 'get_data': {} } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents( join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) plain_message = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace( 'http://stuff.com/endpoints/endpoints/sls.php', current_url) plain_message = plain_message.replace( 'http://idp.example.com/', 'http://invalid.issuer.example.com') message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) settings.set_strict(False) response = OneLogin_Saml2_Logout_Response(settings, message) self.assertTrue(response.is_valid(request_data)) settings.set_strict(True) response_2 = OneLogin_Saml2_Logout_Response(settings, message) try: valid = response_2.is_valid(request_data) self.assertFalse(valid) except Exception as e: self.assertIn('Invalid issuer in the Logout Request', str(e))
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] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) 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 testAddX509KeyDescriptors(self): """ Tests the addX509KeyDescriptors method of the OneLogin_Saml2_Metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) sp_data = settings.get_sp_data() metadata = OneLogin_Saml2_Metadata.builder(sp_data) self.assertNotIn('<md:KeyDescriptor use="signing"', metadata) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata) metadata_without_descriptors = OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, None) self.assertNotIn('<md:KeyDescriptor use="signing"', metadata_without_descriptors) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata_without_descriptors) metadata_without_descriptors = OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, '') self.assertNotIn('<md:KeyDescriptor use="signing"', metadata_without_descriptors) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata_without_descriptors) cert_path = settings.get_cert_path() cert = self.file_contents(join(cert_path, 'sp.crt')) metadata_with_descriptors = compat.to_string(OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, cert)) self.assertIn('<md:KeyDescriptor use="signing"', metadata_with_descriptors) self.assertIn('<md:KeyDescriptor use="encryption"', metadata_with_descriptors) self.assertRaisesRegexp(Exception, 'Error parsing metadata', OneLogin_Saml2_Metadata.add_x509_key_descriptors, '', cert) base_path = dirname(dirname(dirname(dirname(__file__)))) unparsed_metadata = self.file_contents(join(base_path, 'data', 'metadata', 'unparsed_metadata.xml')) self.assertRaisesRegexp(Exception, 'Error parsing metadata', OneLogin_Saml2_Metadata.add_x509_key_descriptors, unparsed_metadata, cert)
def generate_name_id(value, sp_nq, sp_format=None, cert=None, debug=False, nq=None): """ Generates a nameID. :param value: fingerprint :type: string :param sp_nq: SP Name Qualifier :type: string :param sp_format: SP Format :type: string :param cert: IdP Public Cert to encrypt the nameID :type: string :param debug: Activate the xmlsec debug :type: bool :returns: DOMElement | XMLSec nameID :rtype: string :param nq: IDP Name Qualifier :type: string """ root = OneLogin_Saml2_XML.make_root("{%s}container" % OneLogin_Saml2_Constants.NS_SAML) name_id = OneLogin_Saml2_XML.make_child(root, '{%s}NameID' % OneLogin_Saml2_Constants.NS_SAML) if sp_nq is not None: name_id.set('SPNameQualifier', sp_nq) if sp_format is not None: name_id.set('Format', sp_format) if nq is not None: name_id.set('NameQualifier', nq) name_id.text = value if cert is not None: xmlsec.enable_debug_trace(debug) # Load the public cert manager = xmlsec.KeysManager() manager.add_key(xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None)) # Prepare for encryption enc_data = xmlsec.template.encrypted_data_create( root, xmlsec.Transform.AES128, type=xmlsec.EncryptionType.ELEMENT, ns="xenc") xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) key_info = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") enc_key = xmlsec.template.add_encrypted_key(key_info, xmlsec.Transform.RSA_OAEP) xmlsec.template.encrypted_data_ensure_cipher_value(enc_key) # Encrypt! enc_ctx = xmlsec.EncryptionContext(manager) enc_ctx.key = xmlsec.Key.generate(xmlsec.KeyData.AES, 128, xmlsec.KeyDataType.SESSION) enc_data = enc_ctx.encrypt_xml(enc_data, name_id) return '<saml:EncryptedID>' + compat.to_string(OneLogin_Saml2_XML.to_string(enc_data)) + '</saml:EncryptedID>' else: return OneLogin_Saml2_XML.extract_tag_text(root, "saml:NameID")
def testConstructor(self): """ Tests the OneLogin_Saml2_LogoutResponse Constructor. """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) response = OneLogin_Saml2_Logout_Response(settings, message) self.assertRegex(compat.to_string(OneLogin_Saml2_XML.to_string(response.document)), '<samlp:LogoutResponse')
def testSignMetadata(self): """ Tests the signMetadata method of the OneLogin_Saml2_Metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) sp_data = settings.get_sp_data() security = settings.get_security_data() metadata = OneLogin_Saml2_Metadata.builder( sp_data, security['authnRequestsSigned'], security['wantAssertionsSigned']) self.assertIsNotNone(metadata) cert_path = settings.get_cert_path() key = self.file_contents(join(cert_path, 'sp.key')) cert = self.file_contents(join(cert_path, 'sp.crt')) signed_metadata = compat.to_string( OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert)) self.assertIn('<md:SPSSODescriptor', signed_metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', signed_metadata) self.assertIn('AuthnRequestsSigned="false"', signed_metadata) self.assertIn('WantAssertionsSigned="false"', signed_metadata) self.assertIn( '<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"', signed_metadata) self.assertIn( 'Location="http://stuff.com/endpoints/endpoints/acs.php"', signed_metadata) self.assertIn( '<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"', signed_metadata) self.assertIn( ' Location="http://stuff.com/endpoints/endpoints/sls.php"/>', signed_metadata) self.assertIn( '<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', signed_metadata) self.assertIn( '<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', signed_metadata) self.assertIn( '<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', signed_metadata) self.assertIn('<ds:Reference', signed_metadata) self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', signed_metadata) with self.assertRaises(Exception) as context: OneLogin_Saml2_Metadata.sign_metadata('', key, cert) exception = context.exception self.assertIn("Empty string supplied as input", str(exception))
def generate_name_id(value, sp_nq, sp_format, cert=None, debug=False, nq=None): """ Generates a nameID. :param value: fingerprint :type: string :param sp_nq: SP Name Qualifier :type: string :param sp_format: SP Format :type: string :param cert: IdP Public Cert to encrypt the nameID :type: string :param debug: Activate the xmlsec debug :type: bool :returns: DOMElement | XMLSec nameID :rtype: string :param nq: IDP Name Qualifier :type: string """ root = OneLogin_Saml2_XML.make_root("{%s}container" % OneLogin_Saml2_Constants.NS_SAML) name_id = OneLogin_Saml2_XML.make_child(root, '{%s}NameID' % OneLogin_Saml2_Constants.NS_SAML) if sp_nq is not None: name_id.set('SPNameQualifier', sp_nq) name_id.set('Format', sp_format) if nq is not None: name_id.set('NameQualifier', nq) name_id.text = value if cert is not None: xmlsec.enable_debug_trace(debug) # Load the public cert manager = xmlsec.KeysManager() manager.add_key(xmlsec.Key.from_memory(cert, xmlsec.KeyFormat.CERT_PEM, None)) # Prepare for encryption enc_data = xmlsec.template.encrypted_data_create( root, xmlsec.Transform.AES128, type=xmlsec.EncryptionType.ELEMENT, ns="xenc") xmlsec.template.encrypted_data_ensure_cipher_value(enc_data) key_info = xmlsec.template.encrypted_data_ensure_key_info(enc_data, ns="dsig") enc_key = xmlsec.template.add_encrypted_key(key_info, xmlsec.Transform.RSA_OAEP) xmlsec.template.encrypted_data_ensure_cipher_value(enc_key) # Encrypt! enc_ctx = xmlsec.EncryptionContext(manager) enc_ctx.key = xmlsec.Key.generate(xmlsec.KeyData.AES, 128, xmlsec.KeyDataType.SESSION) enc_data = enc_ctx.encrypt_xml(enc_data, name_id) return '<saml:EncryptedID>' + compat.to_string(OneLogin_Saml2_XML.to_string(enc_data)) + '</saml:EncryptedID>' else: return OneLogin_Saml2_XML.extract_tag_text(root, "saml:NameID")
def testAddSignCheckAlg(self): """ Tests the add_sign method of the OneLogin_Saml2_Utils Case: Review signature & digest algorithm """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) key = settings.get_sp_key() cert = settings.get_sp_cert() xml_authn = b64decode( self.file_contents( join(self.data_path, 'requests', 'authn_request.xml.base64'))) xml_authn_signed = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed) self.assertIn( '<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', xml_authn_signed) self.assertIn( '<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>', xml_authn_signed) xml_authn_signed_2 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA256, OneLogin_Saml2_Constants.SHA384)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_2) self.assertIn( '<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#sha384"/>', xml_authn_signed_2) self.assertIn( '<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>', xml_authn_signed_2) xml_authn_signed_3 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert, False, OneLogin_Saml2_Constants.RSA_SHA384, OneLogin_Saml2_Constants.SHA512)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_3) self.assertIn( '<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"/>', xml_authn_signed_3) self.assertIn( '<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha384"/>', xml_authn_signed_3)
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 extract_tag_text(xml, tagname): open_tag = compat.to_bytes("<%s" % tagname) close_tag = compat.to_bytes("</%s>" % tagname) xml = OneLogin_Saml2_XML.to_string(xml) start = xml.find(open_tag) assert start != -1 end = xml.find(close_tag, start) + len(close_tag) assert end != -1 return compat.to_string(xml[start:end])
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 extract_tag_text(xml, tagname): open_tag = compat.to_bytes("<%s" % tagname) close_tag = compat.to_bytes("</%s>" % tagname) xml = OneLogin_Saml2_XML.to_string(xml) start = xml.find(open_tag) assert start != -1 end = xml.find(close_tag, start) + len(close_tag) assert end != -1 return compat.to_string(xml[start:end])
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 testConstructor(self): """ Tests the OneLogin_Saml2_LogoutResponse Constructor. """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) message = self.file_contents( join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) response = OneLogin_Saml2_Logout_Response(settings, message) self.assertRegex( compat.to_string(OneLogin_Saml2_XML.to_string(response.document)), '<samlp:LogoutResponse')
def testAddX509KeyDescriptors(self): """ Tests the addX509KeyDescriptors method of the OneLogin_Saml2_Metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) sp_data = settings.get_sp_data() metadata = OneLogin_Saml2_Metadata.builder(sp_data) self.assertNotIn('<md:KeyDescriptor use="signing"', metadata) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata) metadata_without_descriptors = OneLogin_Saml2_Metadata.add_x509_key_descriptors( metadata, None) self.assertNotIn('<md:KeyDescriptor use="signing"', metadata_without_descriptors) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata_without_descriptors) metadata_without_descriptors = OneLogin_Saml2_Metadata.add_x509_key_descriptors( metadata, '') self.assertNotIn('<md:KeyDescriptor use="signing"', metadata_without_descriptors) self.assertNotIn('<md:KeyDescriptor use="encryption"', metadata_without_descriptors) cert_path = settings.get_cert_path() cert = self.file_contents(join(cert_path, 'sp.crt')) metadata_with_descriptors = compat.to_string( OneLogin_Saml2_Metadata.add_x509_key_descriptors(metadata, cert)) self.assertIn('<md:KeyDescriptor use="signing"', metadata_with_descriptors) self.assertIn('<md:KeyDescriptor use="encryption"', metadata_with_descriptors) with self.assertRaises(Exception) as context: OneLogin_Saml2_Metadata.add_x509_key_descriptors('', cert) exception = context.exception self.assertIn("Error parsing metadata", str(exception)) base_path = dirname(dirname(dirname(dirname(__file__)))) unparsed_metadata = self.file_contents( join(base_path, 'data', 'metadata', 'unparsed_metadata.xml')) with self.assertRaises(Exception) as context: OneLogin_Saml2_Metadata.add_x509_key_descriptors( unparsed_metadata, cert) exception = context.exception self.assertIn("Error parsing metadata", str(exception))
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 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 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.assertRegexpMatches(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.assertRegexpMatches(inflated, '^<samlp:AuthnRequest')
def testGetSPMetadata(self): """ Tests the getSPMetadata method of the OneLogin_Saml2_Settings Case unsigned metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) metadata = compat.to_string(settings.get_sp_metadata()) self.assertNotEqual(len(metadata), 0) self.assertIn('<md:SPSSODescriptor', metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', metadata) self.assertIn('AuthnRequestsSigned="false"', metadata) self.assertIn('WantAssertionsSigned="false"', metadata) self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://stuff.com/endpoints/endpoints/acs.php" index="1"/>', metadata) self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://stuff.com/endpoints/endpoints/sls.php"/>', metadata) self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified</md:NameIDFormat>', metadata)
def testCreateDeflatedSAMLLogoutRequestURLParameter(self): """ Tests the OneLogin_Saml2_LogoutRequest Constructor. The creation of a deflated SAML Logout Request """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_request = OneLogin_Saml2_Logout_Request(settings) parameters = {'SAMLRequest': logout_request.get_request()} logout_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegex(logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=') url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) payload = exploded['SAMLRequest'][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) self.assertRegex(inflated, '^<samlp:LogoutRequest')
def __init__(self, settings, response=None): """ Constructs a Logout Response object (Initialize params from settings and if provided load the Logout Response. Arguments are: * (OneLogin_Saml2_Settings) settings. Setting data * (string) response. An UUEncoded SAML Logout response from the IdP. """ self.__settings = settings self.__error = None if response is not None: self.__logout_response = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(response)) self.document = OneLogin_Saml2_XML.to_etree(self.__logout_response)
def testCreateDeflatedSAMLLogoutRequestURLParameter(self): """ Tests the OneLogin_Saml2_LogoutRequest Constructor. The creation of a deflated SAML Logout Request """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) logout_request = OneLogin_Saml2_Logout_Request(settings) parameters = {'SAMLRequest': logout_request.get_request()} logout_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegexpMatches(logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=') url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) payload = exploded['SAMLRequest'][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) self.assertRegexpMatches(inflated, '^<samlp:LogoutRequest')
def testCreateDeflatedSAMLLogoutResponseURLParameter(self): """ Tests the OneLogin_Saml2_LogoutResponse Constructor. The creation of a deflated SAML Logout Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) in_response_to = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e' response_builder = OneLogin_Saml2_Logout_Response(settings) response_builder.build(in_response_to) parameters = {'SAMLResponse': response_builder.get_response()} logout_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegex(logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLResponse=') url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(exploded['SAMLResponse'][0])) self.assertRegex(inflated, '^<samlp:LogoutResponse')
def testProcessSLORequestNotOnOrAfterFailed(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class Case Logout Request NotOnOrAfter failed """ request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_requests', 'invalids', 'not_after_failed.xml.base64')) # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) request_data['get_data']['SAMLRequest'] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) auth.set_strict(True) auth.process_slo(True) self.assertEqual(auth.get_errors(), ['invalid_logout_request'])
def testConstructor(self): """ Tests the OneLogin_Saml2_LogoutRequest Constructor. """ settings_info = self.loadSettingsJSON() settings_info['security']['nameIdEncrypted'] = True settings = OneLogin_Saml2_Settings(settings_info) logout_request = OneLogin_Saml2_Logout_Request(settings) parameters = {'SAMLRequest': logout_request.get_request()} logout_url = OneLogin_Saml2_Utils.redirect('http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegex(logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLRequest=') url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) payload = exploded['SAMLRequest'][0] inflated = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(payload)) self.assertRegex(inflated, '^<samlp:LogoutRequest')
def generateAndCheckMetadata(self, settings): """ Helper method: Given some settings, generate metadata and validate it """ if not isinstance(settings, OneLogin_Saml2_Settings): settings = OneLogin_Saml2_Settings(settings) metadata = compat.to_string(settings.get_sp_metadata()) self.assertIn('<md:SPSSODescriptor', metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', metadata) self.assertIn('AuthnRequestsSigned="false"', metadata) self.assertIn('WantAssertionsSigned="false"', metadata) self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://stuff.com/endpoints/endpoints/acs.php" index="1"/>', metadata) self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://stuff.com/endpoints/endpoints/sls.php"/>', metadata) self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified</md:NameIDFormat>', metadata) self.assertIn('<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', metadata) self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', metadata) self.assertIn('<ds:Reference', metadata) self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', metadata)
def testSignMetadata(self): """ Tests the signMetadata method of the OneLogin_Saml2_Metadata """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) sp_data = settings.get_sp_data() security = settings.get_security_data() metadata = OneLogin_Saml2_Metadata.builder( sp_data, security['authnRequestsSigned'], security['wantAssertionsSigned'] ) self.assertIsNotNone(metadata) cert_path = settings.get_cert_path() key = self.file_contents(join(cert_path, 'sp.key')) cert = self.file_contents(join(cert_path, 'sp.crt')) signed_metadata = compat.to_string(OneLogin_Saml2_Metadata.sign_metadata(metadata, key, cert)) self.assertIn('<md:SPSSODescriptor', signed_metadata) self.assertIn('entityID="http://stuff.com/endpoints/metadata.php"', signed_metadata) self.assertIn('AuthnRequestsSigned="false"', signed_metadata) self.assertIn('WantAssertionsSigned="false"', signed_metadata) self.assertIn('<md:AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"', signed_metadata) self.assertIn('Location="http://stuff.com/endpoints/endpoints/acs.php"', signed_metadata) self.assertIn('<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"', signed_metadata) self.assertIn(' Location="http://stuff.com/endpoints/endpoints/sls.php"/>', signed_metadata) self.assertIn('<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>', signed_metadata) self.assertIn('<ds:SignedInfo>\n<ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>', signed_metadata) self.assertIn('<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>', signed_metadata) self.assertIn('<ds:Reference', signed_metadata) self.assertIn('<ds:KeyInfo>\n<ds:X509Data>\n<ds:X509Certificate>', signed_metadata) with self.assertRaises(Exception) as context: OneLogin_Saml2_Metadata.sign_metadata('', key, cert) exception = context.exception self.assertIn("Empty string supplied as input", str(exception))
def testProcessSLORequestDeletingSession(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class Case Valid Logout Request, validating that the local session is deleted, a LogoutResponse is created and a redirection executed """ settings_info = self.loadSettingsJSON() request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_requests', 'logout_request_deflated.xml.base64')) # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) request_data['get_data']['SAMLRequest'] = message # FIXME # if (!isset($_SESSION)) { # $_SESSION = array(); # } # $_SESSION['samltest'] = true; auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) auth.set_strict(True) target_url = auth.process_slo(True) parsed_query = parse_qs(urlparse(target_url)[4]) slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertIn(slo_url, target_url) self.assertIn('SAMLResponse', parsed_query) #self.assertNotIn('RelayState', parsed_query) # FIXME // Session is not alive # $this->assertFalse(isset($_SESSION['samltest'])); # $_SESSION['samltest'] = true; auth.set_strict(True) target_url_2 = auth.process_slo(True) target_url_2 = auth.process_slo(True) parsed_query_2 = parse_qs(urlparse(target_url_2)[4]) slo_url = settings_info['idp']['singleLogoutService']['url'] self.assertIn(slo_url, target_url_2) self.assertIn('SAMLResponse', parsed_query_2)
def logout_view(request, *args, **kwargs): backend = request.backend logout(request) # Пользователя разлогинили, теперь нужно ответить провайдеру. # Для начала нужно узнать, от какого именно IdentityProvider'а пришёл запрос. # Как минимму OneLogin не присылает в запросе никаких опознавательных знаков кроме атрибута Issuer. # В Issuer у него находится путь к его метадате. В конфиге тот же путь надо указать в entity_id. # вынимаем значение Issuer request_str = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( request.GET['SAMLRequest'])) xml_doc = OneLogin_Saml2_XML.to_etree(request_str) issuer_nodes = OneLogin_Saml2_XML.query( xml_doc, '/samlp:LogoutRequest/saml:Issuer') if len(issuer_nodes) == 0: raise ValueError('no Issuer in logout request: ' + request_str) issuer = issuer_nodes[0].text # ищем имя IDP idp_configs = backend.setting('ENABLED_IDPS') idp_name = next((name for name, cfg in idp_configs.items() if cfg.get('entity_id') == issuer), None) if idp_name is None: raise ValueError( f"IDP not found for Issuer '{issuer}' in logout request: " + request_str) # формируем адрес редиректа idp = backend.get_idp(idp_name) if idp.slo_config == {}: # если конфига slo нет url = settings.LOGOUT_REDIRECT_URL else: url = backend._create_saml_auth( idp, remove_signature_from_get=True).process_slo() return HttpResponseRedirect(url)
def testProcessSLOResponseRequestId(self): """ Tests the process_slo method of the OneLogin_Saml2_Auth class Case Logout Response with valid and invalid Request ID """ request_data = self.get_request() message = self.file_contents(join(self.data_path, 'logout_responses', 'logout_response_deflated.xml.base64')) # In order to avoid the destination problem plain_message = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(message)) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message = plain_message.replace('http://stuff.com/endpoints/endpoints/sls.php', current_url) message = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message) request_data['get_data']['SAMLResponse'] = message auth = OneLogin_Saml2_Auth(request_data, old_settings=self.loadSettingsJSON()) request_id = 'wrongID' auth.set_strict(True) auth.process_slo(True, request_id) self.assertEqual(auth.get_errors(), ['invalid_logout_response']) request_id = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e' auth.process_slo(True, request_id) self.assertEqual(len(auth.get_errors()), 0)
def build(self, in_response_to): """ Creates a Logout Response object. :param in_response_to: InResponseTo value for the Logout Response. :type in_response_to: string """ sp_data = self.__settings.get_sp_data() idp_data = self.__settings.get_idp_data() uid = OneLogin_Saml2_Utils.generate_unique_id() issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) logout_response = OneLogin_Saml2_Templates.LOGOUT_RESPONSE % \ { 'id': uid, 'issue_instant': issue_instant, 'destination': idp_data['singleLogoutService']['url'], 'in_response_to': in_response_to, 'entity_id': sp_data['entityId'], 'status': "urn:oasis:names:tc:SAML:2.0:status:Success" } self.__logout_response = compat.to_string(logout_response)
def testCreateDeflatedSAMLLogoutResponseURLParameter(self): """ Tests the OneLogin_Saml2_LogoutResponse Constructor. The creation of a deflated SAML Logout Response """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) in_response_to = 'ONELOGIN_21584ccdfaca36a145ae990442dcd96bfe60151e' response_builder = OneLogin_Saml2_Logout_Response(settings) response_builder.build(in_response_to) parameters = {'SAMLResponse': response_builder.get_response()} logout_url = OneLogin_Saml2_Utils.redirect( 'http://idp.example.com/SingleLogoutService.php', parameters, True) self.assertRegex( logout_url, '^http://idp\.example\.com\/SingleLogoutService\.php\?SAMLResponse=' ) url_parts = urlparse(logout_url) exploded = parse_qs(url_parts.query) inflated = compat.to_string( OneLogin_Saml2_Utils.decode_base64_and_inflate( exploded['SAMLResponse'][0])) self.assertRegex(inflated, '^<samlp:LogoutResponse')
def b64encode(data): """base64 encode""" return compat.to_string(base64.b64encode(compat.to_bytes(data)))
def testAddSign(self): """ Tests the add_sign method of the OneLogin_Saml2_Utils """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) key = settings.get_sp_key() cert = settings.get_sp_cert() xml_authn = b64decode( self.file_contents( join(self.data_path, 'requests', 'authn_request.xml.base64'))) xml_authn_signed = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed) res = parseString(xml_authn_signed) ds_signature = res.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature.tagName) xml_authn_dom = parseString(xml_authn) xml_authn_signed_2 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn_dom.toxml(), key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_2) res_2 = parseString(xml_authn_signed_2) ds_signature_2 = res_2.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_2.tagName) xml_authn_signed_3 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn_dom.firstChild.toxml(), key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_3) res_3 = parseString(xml_authn_signed_3) ds_signature_3 = res_3.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_3.tagName) xml_authn_etree = etree.fromstring(xml_authn) xml_authn_signed_4 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_4) res_4 = parseString(xml_authn_signed_4) ds_signature_4 = res_4.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_4.tagName) xml_authn_signed_5 = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_5) res_5 = parseString(xml_authn_signed_5) ds_signature_5 = res_5.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_5.tagName) xml_logout_req = b64decode( self.file_contents( join(self.data_path, 'logout_requests', 'logout_request.xml.base64'))) xml_logout_req_signed = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_logout_req, key, cert)) self.assertIn('<ds:SignatureValue>', xml_logout_req_signed) res_6 = parseString(xml_logout_req_signed) ds_signature_6 = res_6.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_6.tagName) xml_logout_res = b64decode( self.file_contents( join(self.data_path, 'logout_responses', 'logout_response.xml.base64'))) xml_logout_res_signed = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_logout_res, key, cert)) self.assertIn('<ds:SignatureValue>', xml_logout_res_signed) res_7 = parseString(xml_logout_res_signed) ds_signature_7 = res_7.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_7.tagName) xml_metadata = self.file_contents( join(self.data_path, 'metadata', 'metadata_settings1.xml')) xml_metadata_signed = compat.to_string( OneLogin_Saml2_Utils.add_sign(xml_metadata, key, cert)) self.assertIn('<ds:SignatureValue>', xml_metadata_signed) res_8 = parseString(xml_metadata_signed) ds_signature_8 = res_8.firstChild.firstChild.nextSibling.firstChild.nextSibling self.assertIn('ds:Signature', ds_signature_8.tagName)
def testIsInValidLogoutResponseSign(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutResponse """ request_data = { 'http_host': 'example.com', 'script_name': 'index.html', 'get_data': {} } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) settings.set_strict(False) request_data['get_data'] = { 'SAMLResponse': 'fZJva8IwEMa/Ssl7TZrW/gnqGHMMwSlM8cXeyLU9NaxNQi9lfvxVZczB5ptwSe733MPdjQma2qmFPdjOvyE5awiDU1MbUpevCetaoyyQJmWgQVK+VOvH14WSQ6Fca70tbc1ukPsEEGHrtTUsmM8mbDfKUhnFci8gliGINI/yXIAAiYnsw6JIRgWWAKlkwRZb6skJ64V6nKjDuSEPxvdPIowHIhpIsQkTFaYqSt9ZMEPy2oC/UEfvHSnOnfZFV38MjR1oN7TtgRv8tAZre9CGV9jYkGtT4Wnoju6Bauprme/ebOyErZbPi9XLfLnDoohwhHGc5WVSVhjCKM6rBMpYQpWJrIizfZ4IZNPxuTPqYrmd/m+EdONqPOfy8yG5rhxv0EMFHs52xvxWaHyd3tqD7+j37clWGGyh7vD+POiSrdZdWSIR49NrhR9R/teGTL8A', 'RelayState': 'https://pitbulk.no-ip.org/newonelogin/demo1/index.php', 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', 'Signature': 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVfNKGA=' } auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) relay_state = request_data['get_data']['RelayState'] del request_data['get_data']['RelayState'] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn("invalid_logout_response_signature", auth.get_errors()) request_data['get_data']['RelayState'] = relay_state settings.set_strict(True) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response', auth.get_errors()) settings.set_strict(False) old_signature = request_data['get_data']['Signature'] request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response_signature', auth.get_errors()) request_data['get_data']['Signature'] = old_signature old_signature_algorithm = request_data['get_data']['SigAlg'] del request_data['get_data']['SigAlg'] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) request_data['get_data']['RelayState'] = 'http://example.com/relaystate' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response_signature', auth.get_errors()) settings.set_strict(True) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) plain_message_6 = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLResponse'])) plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url) plain_message_6 = plain_message_6.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/') request_data['get_data']['SAMLResponse'] = compat.to_string(OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6)) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response_signature', auth.get_errors()) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response_signature', auth.get_errors()) request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_response_signature', auth.get_errors()) settings_info = self.loadSettingsJSON() settings_info['strict'] = True settings_info['security']['wantMessagesSigned'] = True settings = OneLogin_Saml2_Settings(settings_info) request_data['get_data']['SigAlg'] = old_signature_algorithm old_signature = request_data['get_data']['Signature'] del request_data['get_data']['Signature'] request_data['get_data']['SAMLResponse'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(plain_message_6) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('Signature validation failed. Logout Response rejected', auth.get_errors()) request_data['get_data']['Signature'] = old_signature settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9' del settings_info['idp']['x509cert'] settings_2 = OneLogin_Saml2_Settings(settings_info) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_slo() self.assertIn('In order to validate the sign on the SAMLResponse, the x509cert of the IdP is required', auth.get_errors())
def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None, name_id_format=None, spnq=None): """ Constructs the Logout Request object. :param settings: Setting data :type settings: OneLogin_Saml2_Settings :param request: Optional. A LogoutRequest to be loaded instead build one. :type request: string :param name_id: The NameID that will be set in the LogoutRequest. :type name_id: string :param session_index: SessionIndex that identifies the session of the user. :type session_index: string :param nq: IDP Name Qualifier :type: string :param name_id_format: The NameID Format that will be set in the LogoutRequest. :type: string :param spnq: SP Name Qualifier :type: string """ self.__settings = settings self.__error = None self.id = None if request is None: sp_data = self.__settings.get_sp_data() idp_data = self.__settings.get_idp_data() security = self.__settings.get_security_data() uid = OneLogin_Saml2_Utils.generate_unique_id() self.id = uid issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) cert = None if security['nameIdEncrypted']: exists_multix509enc = 'x509certMulti' in idp_data and \ 'encryption' in idp_data['x509certMulti'] and \ idp_data['x509certMulti']['encryption'] if exists_multix509enc: cert = idp_data['x509certMulti']['encryption'][0] else: cert = idp_data['x509cert'] if name_id is not None: if not name_id_format and sp_data['NameIDFormat'] != OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: name_id_format = sp_data['NameIDFormat'] else: name_id = idp_data['entityId'] name_id_format = OneLogin_Saml2_Constants.NAMEID_ENTITY # From saml-core-2.0-os 8.3.6, when the entity Format is used: # "The NameQualifier, SPNameQualifier, and SPProvidedID attributes # MUST be omitted. if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_ENTITY: nq = None spnq = None # NameID Format UNSPECIFIED omitted if name_id_format and name_id_format == OneLogin_Saml2_Constants.NAMEID_UNSPECIFIED: name_id_format = None name_id_obj = OneLogin_Saml2_Utils.generate_name_id( name_id, spnq, name_id_format, cert, False, nq ) if session_index: session_index_str = '<samlp:SessionIndex>%s</samlp:SessionIndex>' % session_index else: session_index_str = '' logout_request = OneLogin_Saml2_Templates.LOGOUT_REQUEST % \ { 'id': uid, 'issue_instant': issue_instant, 'single_logout_url': idp_data['singleLogoutService']['url'], 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, } else: logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(request, ignore_zip=True) self.id = self.get_id(logout_request) self.__logout_request = compat.to_string(logout_request)
def testAddSign(self): """ Tests the add_sign method of the OneLogin_Saml2_Utils """ settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) key = settings.get_sp_key() cert = settings.get_sp_cert() xml_authn = b64decode(self.file_contents(join(self.data_path, 'requests', 'authn_request.xml.base64'))) xml_authn_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed) res = parseString(xml_authn_signed) ds_signature = res.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature.tagName) xml_authn_dom = parseString(xml_authn) xml_authn_signed_2 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_dom.toxml(), key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_2) res_2 = parseString(xml_authn_signed_2) ds_signature_2 = res_2.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_2.tagName) xml_authn_signed_3 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_dom.firstChild.toxml(), key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_3) res_3 = parseString(xml_authn_signed_3) ds_signature_3 = res_3.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_3.tagName) xml_authn_etree = etree.fromstring(xml_authn) xml_authn_signed_4 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_4) res_4 = parseString(xml_authn_signed_4) ds_signature_4 = res_4.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_4.tagName) xml_authn_signed_5 = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_authn_etree, key, cert)) self.assertIn('<ds:SignatureValue>', xml_authn_signed_5) res_5 = parseString(xml_authn_signed_5) ds_signature_5 = res_5.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_5.tagName) xml_logout_req = b64decode(self.file_contents(join(self.data_path, 'logout_requests', 'logout_request.xml.base64'))) xml_logout_req_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_logout_req, key, cert)) self.assertIn('<ds:SignatureValue>', xml_logout_req_signed) res_6 = parseString(xml_logout_req_signed) ds_signature_6 = res_6.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_6.tagName) xml_logout_res = b64decode(self.file_contents(join(self.data_path, 'logout_responses', 'logout_response.xml.base64'))) xml_logout_res_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_logout_res, key, cert)) self.assertIn('<ds:SignatureValue>', xml_logout_res_signed) res_7 = parseString(xml_logout_res_signed) ds_signature_7 = res_7.firstChild.firstChild.nextSibling.nextSibling self.assertIn('ds:Signature', ds_signature_7.tagName) xml_metadata = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) xml_metadata_signed = compat.to_string(OneLogin_Saml2_Utils.add_sign(xml_metadata, key, cert)) self.assertIn('<ds:SignatureValue>', xml_metadata_signed) res_8 = parseString(xml_metadata_signed) ds_signature_8 = res_8.firstChild.firstChild.nextSibling.firstChild.nextSibling self.assertIn('ds:Signature', ds_signature_8.tagName)
def testIsValidLogoutRequestSign(self): """ Tests the is_valid method of the OneLogin_Saml2_LogoutRequest """ request_data = { 'http_host': 'example.com', 'script_name': 'index.html', 'get_data': { 'SAMLRequest': 'lVLBitswEP0Vo7tjeWzJtki8LIRCYLvbNksPewmyPc6K2pJqyXQ/v1LSQlroQi/DMJr33rwZbZ2cJysezNms/gt+X9H55G2etBOXlx1ZFy2MdMoJLWd0wvfieP/xQcCGCrsYb3ozkRvI+wjpHC5eGU2Sw35HTg3lA8hqZFwWFcMKsStpxbEsxoLXeQN9OdY1VAgk+YqLC8gdCUQB7tyKB+281D6UaF6mtEiBPudcABcMXkiyD26Ulv6CevXeOpFlVvlunb5ttEmV3ZjlnGn8YTRO5qx0NuBs8kzpAd829tXeucmR5NH4J/203I8el6gFRUqbFPJnyEV51Wq30by4TLW0/9ZyarYTxt4sBsjUYLMZvRykl1Fxm90SXVkfwx4P++T4KSafVzmpUcVJ/sfSrQZJPphllv79W8WKGtLx0ir8IrVTqD1pT2MH3QAMSs4KTvui71jeFFiwirOmprwPkYW063+5uRq4urHiiC4e8hCX3J5wqAEGaPpw9XB5JmkBdeDqSlkz6CmUXdl0Qae5kv2F/1384wu3PwE=', 'RelayState': '_1037fbc88ec82ce8e770b2bed1119747bb812a07e6', 'SigAlg': 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', 'Signature': 'XCwCyI5cs7WhiJlB5ktSlWxSBxv+6q2xT3c8L7dLV6NQG9LHWhN7gf8qNsahSXfCzA0Ey9dp5BQ0EdRvAk2DIzKmJY6e3hvAIEp1zglHNjzkgcQmZCcrkK9Czi2Y1WkjOwR/WgUTUWsGJAVqVvlRZuS3zk3nxMrLH6f7toyvuJc=' } } settings = OneLogin_Saml2_Settings(self.loadSettingsJSON()) current_url = OneLogin_Saml2_Utils.get_self_url_no_query(request_data) request = compat.to_string(OneLogin_Saml2_Utils.decode_base64_and_inflate(request_data['get_data']['SAMLRequest'])) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) relay_state = request_data['get_data']['RelayState'] del request_data['get_data']['RelayState'] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request_signature', auth.get_errors()) request_data['get_data']['RelayState'] = relay_state settings.set_strict(True) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request', auth.get_errors()) settings.set_strict(False) old_signature = request_data['get_data']['Signature'] request_data['get_data']['Signature'] = 'vfWbbc47PkP3ejx4bjKsRX7lo9Ml1WRoE5J5owF/0mnyKHfSY6XbhO1wwjBV5vWdrUVX+xp6slHyAf4YoAsXFS0qhan6txDiZY4Oec6yE+l10iZbzvie06I4GPak4QrQ4gAyXOSzwCrRmJu4gnpeUxZ6IqKtdrKfAYRAcVf3333=' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request_signature', auth.get_errors()) request_data['get_data']['Signature'] = old_signature old_signature_algorithm = request_data['get_data']['SigAlg'] del request_data['get_data']['SigAlg'] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertEqual([], auth.get_errors()) settings.set_strict(True) request_2 = request.replace('https://pitbulk.no-ip.org/newonelogin/demo1/index.php?sls', current_url) request_2 = request_2.replace('https://pitbulk.no-ip.org/simplesaml/saml2/idp/metadata.php', 'http://idp.example.com/') request_data['get_data']['SAMLRequest'] = OneLogin_Saml2_Utils.deflate_and_base64_encode(request_2) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request_signature', auth.get_errors()) settings.set_strict(False) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request_signature', auth.get_errors()) request_data['get_data']['SigAlg'] = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1' auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('invalid_logout_request_signature', auth.get_errors()) settings_info = self.loadSettingsJSON() settings_info['strict'] = True settings_info['security']['wantMessagesSigned'] = True settings = OneLogin_Saml2_Settings(settings_info) request_data['get_data']['SigAlg'] = old_signature_algorithm old_signature = request_data['get_data']['Signature'] del request_data['get_data']['Signature'] auth = OneLogin_Saml2_Auth(request_data, old_settings=settings) auth.process_slo() self.assertIn('Signature validation failed. Logout Request rejected', auth.get_errors()) request_data['get_data']['Signature'] = old_signature settings_info['idp']['certFingerprint'] = 'afe71c28ef740bc87425be13a2263d37971da1f9' del settings_info['idp']['x509cert'] settings_2 = OneLogin_Saml2_Settings(settings_info) auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_2) auth.process_slo() self.assertIn('In order to validate the sign on the SAMLRequest, the x509cert of the IdP is required', auth.get_errors())
def b64encode(data): """base64 encode""" return compat.to_string(base64.b64encode(compat.to_bytes(data)))
def __init__(self, settings, request=None, name_id=None, session_index=None, nq=None): """ Constructs the Logout Request object. :param settings: Setting data :type settings: OneLogin_Saml2_Settings :param request: Optional. A LogoutRequest to be loaded instead build one. :type request: string :param name_id: The NameID that will be set in the LogoutRequest. :type name_id: string :param session_index: SessionIndex that identifies the session of the user. :type session_index: string :param nq: IDP Name Qualifier :type: string """ self.__settings = settings self.__error = None self.id = None if request is None: sp_data = self.__settings.get_sp_data() idp_data = self.__settings.get_idp_data() security = self.__settings.get_security_data() uid = OneLogin_Saml2_Utils.generate_unique_id() self.id = uid issue_instant = OneLogin_Saml2_Utils.parse_time_to_SAML(OneLogin_Saml2_Utils.now()) cert = None if security['nameIdEncrypted']: cert = idp_data['x509cert'] if name_id is not None: name_id_format = sp_data['NameIDFormat'] sp_name_qualifier = None else: name_id = idp_data['entityId'] name_id_format = OneLogin_Saml2_Constants.NAMEID_ENTITY sp_name_qualifier = sp_data['entityId'] name_id_obj = OneLogin_Saml2_Utils.generate_name_id( name_id, sp_name_qualifier, name_id_format, cert, nq=nq, ) if session_index: session_index_str = '<samlp:SessionIndex>%s</samlp:SessionIndex>' % session_index else: session_index_str = '' logout_request = OneLogin_Saml2_Templates.LOGOUT_REQUEST % \ { 'id': uid, 'issue_instant': issue_instant, 'single_logout_url': idp_data['singleLogoutService']['url'], 'entity_id': sp_data['entityId'], 'name_id': name_id_obj, 'session_index': session_index_str, } else: logout_request = OneLogin_Saml2_Utils.decode_base64_and_inflate(request, ignore_zip=True) self.id = self.get_id(logout_request) self.__logout_request = compat.to_string(logout_request)