From 941d4f0cba59a73ba1f9da8ddb0a24a1d51c21ba Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Mon, 1 Jun 2015 03:24:19 +0000 Subject: [PATCH 01/11] Work in progress on the HTTP-POST binding for IdP's single sign on service. Before this we only do HTTP redirects to the IdP's sso URL with everything in a querystring (as per SAML HTTP-Redirect bindng). A lot of the work concerns creating an HTML form as well as enveloped signatures. --- README.md | 4 + src/onelogin/saml2/authn_request.py | 77 ++++++++++++++++++- tests/settings/settings4.json | 58 ++++++++++++++ .../saml2_tests/authn_request_test.py | 23 ++++++ 4 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 tests/settings/settings4.json diff --git a/README.md b/README.md index 2fe50fe0..4f1e850f 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,10 @@ The previous line will run the tests for the whole toolkit. You can also run the python setup.py test --test-suite tests.src.OneLogin.saml2_tests.auth_test.OneLogin_Saml2_Auth_Test ``` +``` +python setup.py test --test-suite tests.src.OneLogin.saml2_tests.authn_request_test.OneLogin_Saml2_Authn_Request_Test +``` + With the --test-suite parameter you can specify the module to test. You'll find all the module available and their class names at tests/src/OneLogin/saml2_tests/ ### How it works ### diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 8e8495e7..cbdd56a1 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -15,6 +15,8 @@ from onelogin.saml2.utils import OneLogin_Saml2_Utils from onelogin.saml2.constants import OneLogin_Saml2_Constants +import dm.xmlsec.binding as xmlsec +from dm.xmlsec.binding.tmpl import Signature class OneLogin_Saml2_Authn_Request(object): """ @@ -97,6 +99,8 @@ def __init__(self, settings, force_authn=False, is_passive=False): ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="%(assertion_url)s"> %(entity_id)s + + @@ -115,7 +119,78 @@ def __init__(self, settings, force_authn=False, is_passive=False): 'requested_authn_context_str': requested_authn_context_str, } - self.__authn_request = request + + # Only the urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding gets the enveloped signature + if settings.get_idp_data()['singleSignOnService'].get('binding', None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' and security['authnRequestsSigned'] == True: + + xmlsec.initialize() + xmlsec.set_error_callback(self.print_xmlsec_errors) + + + signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) + + from lxml.etree import parse, tostring, fromstring + doc = fromstring(request) + + # ID attributes different from xml:id must be made known by the application through a call + # to the addIds(node, ids) function defined by xmlsec. + xmlsec.addIDs(doc, ['ID']) + + + doc.insert(0, signature) + + ref = signature.addReference(xmlsec.TransformSha1,uri="#%s" % uid) + ref.addTransform(xmlsec.TransformEnveloped) + ref.addTransform(xmlsec.TransformExclC14N) + + key_info = signature.ensureKeyInfo() + key_info.addKeyName() + key_info.addX509Data() + + + # Load the key into the xmlsec context + key = settings.get_sp_key() + if not key: + raise OneLogin_Saml2_Error("Attempt to sign the AuthnRequest but unable to load the SP private key") + + dsig_ctx = xmlsec.DSigCtx() + + sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) + + + print key + print settings.get_sp_cert() + print tostring(doc) + + from tempfile import NamedTemporaryFile + cert_file = NamedTemporaryFile(delete=True) + cert_file.write(settings.get_sp_cert()) + cert_file.seek(0) + + sign_key.loadCert(cert_file.name, xmlsec.KeyDataFormatPem) + + + dsig_ctx.signKey = sign_key + + # Note: the assignment below effectively copies the key + dsig_ctx.sign(signature) + print tostring(doc) + + self.__authn_request = tostring(doc) + else: + self.__authn_request = request + + def print_xmlsec_errors(self, filename, line, func, errorObject, errorSubject, reason, msg): + # this would give complete but often not very usefull) information + print "%(filename)s:%(line)d(%(func)s) error %(reason)d obj=%(errorObject)s subject=%(errorSubject)s: %(msg)s" % locals() + # the following prints if we get something with relation to the application + + info = [] + if errorObject != "unknown": info.append("obj=" + errorObject) + if errorSubject != "unknown": info.append("subject=" + errorSubject) + if msg.strip(): info.append("msg=" + msg) + if info: + print "%s:%d(%s)" % (filename, line, func), " ".join(info) def get_request(self): """ diff --git a/tests/settings/settings4.json b/tests/settings/settings4.json new file mode 100644 index 00000000..226f94ac --- /dev/null +++ b/tests/settings/settings4.json @@ -0,0 +1,58 @@ +{ + "strict": false, + "debug": false, + "sp": { + "entityId": "sp.example.com", + "assertionConsumerService": { + "url": "https://sp.example.com/saml/?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://sp.example.com/saml?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", + "x509cert": "MIIETTCCA7agAwIBAgIJANaOuOCRgiz3MA0GCSqGSIb3DQEBBQUAMIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wHhcNMDUwNzEwMDIyOTAxWhcNMTUwNzA4MDIyOTAxWjCBvDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxHjAcBgNVBAsTFVRlc3QgUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABo4IBUzCCAU8wDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNpG6Wvmr9M9quUhS1LtymYo4P6FMIHxBgNVHSMEgekwgeaAFNpG6Wvmr9M9quUhS1LtymYo4P6FoYHCpIG/MIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb22CCQDWjrjgkYIs9zANBgkqhkiG9w0BAQUFAAOBgQBUXbdOTQwArcNrbxavzARp2JGOnzo6WzTm+OFSXC0F08YwT8jWbht97e8lNNVOBU4Y/38ReZqYC9OqFofG1/O9AdQ58WL/FWg8DgP5MJPTT9kRU3FU01jUiX2+kbdnghZAOJm0ziRNxfNPwIIWPKYXyXEKQQzrnxyFey1hP7cg6A==", + "privateKey": "MIICXQIBAAKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABAoGAKfyh43+yi3gG+QIh7UBtZ5Xm5/+8rRO02hC+mHh+t09X1bY/k8gUOy1sLveOUBhF2I8LtQoIIuxkmJJedDFmI0rbHwEMT/3uWzuqVbaPSd5GP9rfHvieF2d4DoZ+iHwyzsagSIgJLe9ZKNaZymdLqMJSaRIkLwTIwUZOpMHeMwkCQQD5VyfjihPCKU5CBHQ3JTAU42Sw8hiPSPUQWPc4F8lcxhqIkC87GgjY6qD9JlRUE4E5zy+hLCNTgggXa5uS0KMTAkEA4KGScqRoiKl1s9Ei1AQX32RSMQaw0Vh46S3ms44zIfjkCjcKON79MUTL1at/2IENoFDimFZUbYhY9D4QH+cf5QJAeQzYH76kMwospR5WcYNLYYi4FLOkOsP3vdUDSKc7qh+/N/eQBohwLSdTuzMFk7/YaAFvJTcxe1RQq1YhtFg4IwJBAI04xwtQFXAlqZv9FXpZgHCvb4TnAe77QjjG5M1pzvfCtAtAAysx9dgtukCA64U/zUNG1s6TJ80c9V/ITPbhpYkCQQCjQbKZgL5E1njCMFUMkYZF53LaGOUrxC7BzObMIECDaoMQppLWm3coXdEjDCl1ZbmZXIdzZ5QwGUdSj/v7nkne" + }, + "idp": { + "entityId": "idp.example.com", + "singleSignOnService": { + "url": "https://idp.example.com/login", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://idp.example.com/sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" + }, + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": true, + "logoutRequestSigned": true, + "logoutResponseSigned": true, + "signMetadata": true, + "wantMessagesSigned": false, + "wantAssertionsSigned": false, + "wantNameIdEncrypted": false, + "requestedAuthnContext": true + }, + "contactPerson": { + "technical": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + }, + "support": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + } + }, + "organization": { + "en-US": { + "name": "example.com", + "displayname": "example.com", + "url": "http://example.com" + } + } +} diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 2d5a0a2f..37116c51 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -224,3 +224,26 @@ def testCreateEncSAMLRequest(self): self.assertRegexpMatches(inflated, 'http://stuff.com/endpoints/metadata.php') self.assertRegexpMatches(inflated, 'Format="urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted"') self.assertRegexpMatches(inflated, 'ProviderName="SP prueba"') + + 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. + + """ + filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings4.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) + + # To verify the assertion is signed correct we can use the xmlsec1 command line tool + # xmlsec1 --verify --id-attr:ID AuthnRequest --store-references --trusted-pem tests/certs/aleksey-xmlsec/cacert.pem --verification-time "2007-07-04 12:12:12" authn_signed_assertion.xml \ No newline at end of file From 4dd89c31aba29ab6aa515c79e85499d67995685e Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 19:45:03 +0000 Subject: [PATCH 02/11] Added tests for AuthnRequest HTTP-POST binding. Also added an example self signed certificate with pub/private key to be used for testing. --- src/onelogin/saml2/authn_request.py | 16 ++- tests/certs/example.com/example.privatekey | 27 ++++ tests/certs/example.com/example.pubkey | 9 ++ .../example_settings_http_post_binding.json | 59 +++++++++ tests/settings/settings4.json | 116 +++++++++--------- .../saml2_tests/authn_request_test.py | 34 ++++- 6 files changed, 189 insertions(+), 72 deletions(-) create mode 100644 tests/certs/example.com/example.privatekey create mode 100644 tests/certs/example.com/example.pubkey create mode 100644 tests/settings/example_settings_http_post_binding.json diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index cbdd56a1..4e05214c 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -8,6 +8,7 @@ AuthNRequest class of OneLogin's Python Toolkit. """ +import logging from base64 import b64encode from zlib import compress @@ -18,6 +19,8 @@ import dm.xmlsec.binding as xmlsec from dm.xmlsec.binding.tmpl import Signature +log = logging.getLogger(__name__) + class OneLogin_Saml2_Authn_Request(object): """ @@ -123,10 +126,11 @@ def __init__(self, settings, force_authn=False, is_passive=False): # Only the urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding gets the enveloped signature if settings.get_idp_data()['singleSignOnService'].get('binding', None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' and security['authnRequestsSigned'] == True: + log.debug("Generating AuthnRequest using urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding") + xmlsec.initialize() xmlsec.set_error_callback(self.print_xmlsec_errors) - signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) from lxml.etree import parse, tostring, fromstring @@ -136,7 +140,6 @@ def __init__(self, settings, force_authn=False, is_passive=False): # to the addIds(node, ids) function defined by xmlsec. xmlsec.addIDs(doc, ['ID']) - doc.insert(0, signature) ref = signature.addReference(xmlsec.TransformSha1,uri="#%s" % uid) @@ -157,11 +160,6 @@ def __init__(self, settings, force_authn=False, is_passive=False): sign_key = xmlsec.Key.loadMemory(key, xmlsec.KeyDataFormatPem, None) - - print key - print settings.get_sp_cert() - print tostring(doc) - from tempfile import NamedTemporaryFile cert_file = NamedTemporaryFile(delete=True) cert_file.write(settings.get_sp_cert()) @@ -169,14 +167,14 @@ def __init__(self, settings, force_authn=False, is_passive=False): sign_key.loadCert(cert_file.name, xmlsec.KeyDataFormatPem) - dsig_ctx.signKey = sign_key # Note: the assignment below effectively copies the key dsig_ctx.sign(signature) - print tostring(doc) self.__authn_request = tostring(doc) + log.debug("Generated AuthnRequest: {}".format(self.__authn_request)) + else: self.__authn_request = request diff --git a/tests/certs/example.com/example.privatekey b/tests/certs/example.com/example.privatekey new file mode 100644 index 00000000..42af7266 --- /dev/null +++ b/tests/certs/example.com/example.privatekey @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpgIBAAKCAQEA65E2/lZg54UVMrgjvoun1iSHsvYpcCzMPqP+00jPKqgeTwmW +1Z3j5nfez24WpFUaqGBXzaWzGw9UIxV1yApKJVVUtrZkRrBpRPZ9fz/XoJ1Zq3wc +ka1VYiXeBBBtxWKipqXxmj11ISRqrGF13b+2Yt2XAvqSg6zI4kH7/zJWbB3GdRQW +zpOSknv2/Eac5LWxeAKoY4jv146bJFCFTJVHMqg5U5C2MVNbjSFPUSRGNP/Sm5v1 +U90jBY4aTE21NeA5hgES17C2Yno7P3OjumL89QiX/KQs1AUfErFA9BVGIJt0dTQa +gIDTzCXjQ1AMeqpbvfEWIxJ8gVcik/9E4dAYLQIDAQABAoIBAQDGz40ZRK+OVkxY +vP4138nrunLogEbizHwoVeJIUYe+mZrS2+X4LcRdC0f5yxDC6qyP9JfGERXDPcGl +xoPcK4r+TTEs72xcGKEPufSaw7fpb0NxrlKyRBbuucTRq0fpseBSQ3VP1pSXPxPk +nnCKkTWN5TSBKBclmFsGUegrLkGwBiakiIYy7TrNSzQ7tFI/jOHP58b1oN4sWQMr +qyBIADQgR1G+r+iRjrKiMQ0HFrn3cBIh/pOb9e/R/PzuzjluKR21cbUL9U2VBmID +mku/JdoVef8vadWO8wF+AsMkHbT6EpWqJnmzm9Yc1d7OnGdFYiqJOfx5jwe0hO4I +WEV1Mz1BAoGBAPd7wjNAr69/BE2zo4gUYJiELQsvF+cD00kr4wjkYuuwlJkfrGX5 +ywxkpE0o2wzc2FirviHQizDmv0L9NfkEO+Qy9XfOYL1kJ2oV9Yw/Mp1dcDyap4bg ++NTcx65PQJJTB0fr3DGBWAWFko2hTKOTbgF+4DO8lO41H2GqpkSoUQSdAoGBAPOs +epUxnMAKnQKCjJ/pMgf+vtMYg8VkAtvLrd8bRCmHWKhrBHObojorQx2fGic22Sb/ +B8jFzK6Y61Q9LiBJDLoU3acgkq76x0C71e6YwvGVXSgw6FRuTIsmt10PzqOFfUFn +Bfq2t92PYnJXJyx8RtFRl0Z1aiSqmxzpvon/9mTRAoGBAMOYGEARm8iEBo6yr0hZ +co6XyFHSgn2eVFq8SM86UcQc5xSuJ77g0U2WLRSeeaGM2aAa/EYVYCzh8b+sCAAr +DHqqm754aZTFlzEM8ehJ+mLM+murf0PmgkMZyudE06/R1ytMidbGdx7GFrHBDaUq +XALql5/MJ5ise4ThLk+NB5sxAoGBALMoOESjYoWMCB7FT5FvSjq4oSLh3lhuDO// +lAn6qSYDfjrt3CsH3cH49vK7fOYiHIzga5/BVpl0k2mvRc+1Bed22fU8LLz8Yy2E +LWms5X/r+r9HHjqdkiepQp3otlxiFFLW5X2NhCgheRdqXsIFaagS3i+OuojU6xDa +Bx69lDJRAoGBAJFMcGCk280m3s3IqoiXOsyOdcvQ6EKOltJrA1PIyI6S8KeWW1su +66hnxa2ffAw34uiKA0WscyzFCOdXpohOxCnWx0eANpWpQieP41izNYmUURWa5+r1 +qr0BfgdzOPAn2Wa0bUBHBGM7g5XrwLtvaUkFdy6USHXXzN08oPT8Wx1A +-----END RSA PRIVATE KEY----- diff --git a/tests/certs/example.com/example.pubkey b/tests/certs/example.com/example.pubkey new file mode 100644 index 00000000..ee782d87 --- /dev/null +++ b/tests/certs/example.com/example.pubkey @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA65E2/lZg54UVMrgjvoun +1iSHsvYpcCzMPqP+00jPKqgeTwmW1Z3j5nfez24WpFUaqGBXzaWzGw9UIxV1yApK +JVVUtrZkRrBpRPZ9fz/XoJ1Zq3wcka1VYiXeBBBtxWKipqXxmj11ISRqrGF13b+2 +Yt2XAvqSg6zI4kH7/zJWbB3GdRQWzpOSknv2/Eac5LWxeAKoY4jv146bJFCFTJVH +Mqg5U5C2MVNbjSFPUSRGNP/Sm5v1U90jBY4aTE21NeA5hgES17C2Yno7P3OjumL8 +9QiX/KQs1AUfErFA9BVGIJt0dTQagIDTzCXjQ1AMeqpbvfEWIxJ8gVcik/9E4dAY +LQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/settings/example_settings_http_post_binding.json b/tests/settings/example_settings_http_post_binding.json new file mode 100644 index 00000000..13eb47ec --- /dev/null +++ b/tests/settings/example_settings_http_post_binding.json @@ -0,0 +1,59 @@ +{ + "comment": "This settings file uses the HTTP-POST binding for the assertionConsumerService.", + "strict": false, + "debug": false, + "sp": { + "entityId": "sp.example.com", + "assertionConsumerService": { + "url": "https://sp.example.com/saml/?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://sp.example.com/saml?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", + "x509cert": "MIIDuTCCAqGgAwIBAgIJALO8tfVURFsvMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE1MDgwODE4NDQ1OVoXDTI1MDgwNTE4NDQ1OVowczELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrkTb+VmDnhRUyuCO+i6fWJIey9ilwLMw+o/7TSM8qqB5PCZbVnePmd97PbhakVRqoYFfNpbMbD1QjFXXICkolVVS2tmRGsGlE9n1/P9egnVmrfByRrVViJd4EEG3FYqKmpfGaPXUhJGqsYXXdv7Zi3ZcC+pKDrMjiQfv/MlZsHcZ1FBbOk5KSe/b8RpzktbF4AqhjiO/XjpskUIVMlUcyqDlTkLYxU1uNIU9RJEY0/9Kbm/VT3SMFjhpMTbU14DmGARLXsLZiejs/c6O6Yvz1CJf8pCzUBR8SsUD0FUYgm3R1NBqAgNPMJeNDUAx6qlu98RYjEnyBVyKT/0Th0BgtAgMBAAGjUDBOMB0GA1UdDgQWBBRghqUeLjDqMaJikgHWgxYQnQm1azAfBgNVHSMEGDAWgBRghqUeLjDqMaJikgHWgxYQnQm1azAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAs0h0MFMDme/07Gj5TJkaxQLWiacNhqmqEa3Mt1C3k2Wva6OwAAqMMWcAQMdmACg8Qk0xjLBizs7go7QvkmV+RNbHDBdD0p00GRrBj1XTPR4VJiJJvOmY2G7A084lx0M9nOGHtQRLgs116TNOTMHz3rPDeN0SdQJien1AAKa4JhcWtuC0yBlPXtx9nQ4TSrCTgOEOnYfYjh+WKD/ZGUeYzmNlT9Sl6aPjjbpSBMzZzckhxDAdQrveTKb75z8BFr+60X3f76qCO7xsoCCUJfybsgiUZvp6s7qcAkJNnWeIKWao5XEkAOvr6Kry/oncaUNm6Q1LRKgXmB8FiIx3i3xdP", + "privateKey": "MIIEpgIBAAKCAQEA65E2/lZg54UVMrgjvoun1iSHsvYpcCzMPqP+00jPKqgeTwmW1Z3j5nfez24WpFUaqGBXzaWzGw9UIxV1yApKJVVUtrZkRrBpRPZ9fz/XoJ1Zq3wcka1VYiXeBBBtxWKipqXxmj11ISRqrGF13b+2Yt2XAvqSg6zI4kH7/zJWbB3GdRQWzpOSknv2/Eac5LWxeAKoY4jv146bJFCFTJVHMqg5U5C2MVNbjSFPUSRGNP/Sm5v1U90jBY4aTE21NeA5hgES17C2Yno7P3OjumL89QiX/KQs1AUfErFA9BVGIJt0dTQagIDTzCXjQ1AMeqpbvfEWIxJ8gVcik/9E4dAYLQIDAQABAoIBAQDGz40ZRK+OVkxYvP4138nrunLogEbizHwoVeJIUYe+mZrS2+X4LcRdC0f5yxDC6qyP9JfGERXDPcGlxoPcK4r+TTEs72xcGKEPufSaw7fpb0NxrlKyRBbuucTRq0fpseBSQ3VP1pSXPxPknnCKkTWN5TSBKBclmFsGUegrLkGwBiakiIYy7TrNSzQ7tFI/jOHP58b1oN4sWQMrqyBIADQgR1G+r+iRjrKiMQ0HFrn3cBIh/pOb9e/R/PzuzjluKR21cbUL9U2VBmIDmku/JdoVef8vadWO8wF+AsMkHbT6EpWqJnmzm9Yc1d7OnGdFYiqJOfx5jwe0hO4IWEV1Mz1BAoGBAPd7wjNAr69/BE2zo4gUYJiELQsvF+cD00kr4wjkYuuwlJkfrGX5ywxkpE0o2wzc2FirviHQizDmv0L9NfkEO+Qy9XfOYL1kJ2oV9Yw/Mp1dcDyap4bg+NTcx65PQJJTB0fr3DGBWAWFko2hTKOTbgF+4DO8lO41H2GqpkSoUQSdAoGBAPOsepUxnMAKnQKCjJ/pMgf+vtMYg8VkAtvLrd8bRCmHWKhrBHObojorQx2fGic22Sb/B8jFzK6Y61Q9LiBJDLoU3acgkq76x0C71e6YwvGVXSgw6FRuTIsmt10PzqOFfUFnBfq2t92PYnJXJyx8RtFRl0Z1aiSqmxzpvon/9mTRAoGBAMOYGEARm8iEBo6yr0hZco6XyFHSgn2eVFq8SM86UcQc5xSuJ77g0U2WLRSeeaGM2aAa/EYVYCzh8b+sCAArDHqqm754aZTFlzEM8ehJ+mLM+murf0PmgkMZyudE06/R1ytMidbGdx7GFrHBDaUqXALql5/MJ5ise4ThLk+NB5sxAoGBALMoOESjYoWMCB7FT5FvSjq4oSLh3lhuDO//lAn6qSYDfjrt3CsH3cH49vK7fOYiHIzga5/BVpl0k2mvRc+1Bed22fU8LLz8Yy2ELWms5X/r+r9HHjqdkiepQp3otlxiFFLW5X2NhCgheRdqXsIFaagS3i+OuojU6xDaBx69lDJRAoGBAJFMcGCk280m3s3IqoiXOsyOdcvQ6EKOltJrA1PIyI6S8KeWW1su66hnxa2ffAw34uiKA0WscyzFCOdXpohOxCnWx0eANpWpQieP41izNYmUURWa5+r1qr0BfgdzOPAn2Wa0bUBHBGM7g5XrwLtvaUkFdy6USHXXzN08oPT8Wx1A" + }, + "idp": { + "entityId": "idp.example.com", + "singleSignOnService": { + "url": "https://idp.example.com/login", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://idp.example.com/sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "MIIDuTCCAqGgAwIBAgIJALO8tfVURFsvMA0GCSqGSIb3DQEBCwUAMHMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxFDASBgNVBAMMC2V4YW1wbGUuY29tMB4XDTE1MDgwODE4NDQ1OVoXDTI1MDgwNTE4NDQ1OVowczELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDrkTb+VmDnhRUyuCO+i6fWJIey9ilwLMw+o/7TSM8qqB5PCZbVnePmd97PbhakVRqoYFfNpbMbD1QjFXXICkolVVS2tmRGsGlE9n1/P9egnVmrfByRrVViJd4EEG3FYqKmpfGaPXUhJGqsYXXdv7Zi3ZcC+pKDrMjiQfv/MlZsHcZ1FBbOk5KSe/b8RpzktbF4AqhjiO/XjpskUIVMlUcyqDlTkLYxU1uNIU9RJEY0/9Kbm/VT3SMFjhpMTbU14DmGARLXsLZiejs/c6O6Yvz1CJf8pCzUBR8SsUD0FUYgm3R1NBqAgNPMJeNDUAx6qlu98RYjEnyBVyKT/0Th0BgtAgMBAAGjUDBOMB0GA1UdDgQWBBRghqUeLjDqMaJikgHWgxYQnQm1azAfBgNVHSMEGDAWgBRghqUeLjDqMaJikgHWgxYQnQm1azAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAs0h0MFMDme/07Gj5TJkaxQLWiacNhqmqEa3Mt1C3k2Wva6OwAAqMMWcAQMdmACg8Qk0xjLBizs7go7QvkmV+RNbHDBdD0p00GRrBj1XTPR4VJiJJvOmY2G7A084lx0M9nOGHtQRLgs116TNOTMHz3rPDeN0SdQJien1AAKa4JhcWtuC0yBlPXtx9nQ4TSrCTgOEOnYfYjh+WKD/ZGUeYzmNlT9Sl6aPjjbpSBMzZzckhxDAdQrveTKb75z8BFr+60X3f76qCO7xsoCCUJfybsgiUZvp6s7qcAkJNnWeIKWao5XEkAOvr6Kry/oncaUNm6Q1LRKgXmB8FiIx3i3xdP" + }, + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": true, + "logoutRequestSigned": true, + "logoutResponseSigned": true, + "signMetadata": true, + "wantMessagesSigned": false, + "wantAssertionsSigned": false, + "wantNameIdEncrypted": false, + "requestedAuthnContext": true + }, + "contactPerson": { + "technical": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + }, + "support": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + } + }, + "organization": { + "en-US": { + "name": "example.com", + "displayname": "example.com", + "url": "http://example.com" + } + } +} diff --git a/tests/settings/settings4.json b/tests/settings/settings4.json index 226f94ac..9ee3eea0 100644 --- a/tests/settings/settings4.json +++ b/tests/settings/settings4.json @@ -1,58 +1,58 @@ -{ - "strict": false, - "debug": false, - "sp": { - "entityId": "sp.example.com", - "assertionConsumerService": { - "url": "https://sp.example.com/saml/?acs", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "singleLogoutService": { - "url": "https://sp.example.com/saml?sls", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", - "x509cert": "MIIETTCCA7agAwIBAgIJANaOuOCRgiz3MA0GCSqGSIb3DQEBBQUAMIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wHhcNMDUwNzEwMDIyOTAxWhcNMTUwNzA4MDIyOTAxWjCBvDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxHjAcBgNVBAsTFVRlc3QgUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABo4IBUzCCAU8wDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNpG6Wvmr9M9quUhS1LtymYo4P6FMIHxBgNVHSMEgekwgeaAFNpG6Wvmr9M9quUhS1LtymYo4P6FoYHCpIG/MIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb22CCQDWjrjgkYIs9zANBgkqhkiG9w0BAQUFAAOBgQBUXbdOTQwArcNrbxavzARp2JGOnzo6WzTm+OFSXC0F08YwT8jWbht97e8lNNVOBU4Y/38ReZqYC9OqFofG1/O9AdQ58WL/FWg8DgP5MJPTT9kRU3FU01jUiX2+kbdnghZAOJm0ziRNxfNPwIIWPKYXyXEKQQzrnxyFey1hP7cg6A==", - "privateKey": "MIICXQIBAAKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABAoGAKfyh43+yi3gG+QIh7UBtZ5Xm5/+8rRO02hC+mHh+t09X1bY/k8gUOy1sLveOUBhF2I8LtQoIIuxkmJJedDFmI0rbHwEMT/3uWzuqVbaPSd5GP9rfHvieF2d4DoZ+iHwyzsagSIgJLe9ZKNaZymdLqMJSaRIkLwTIwUZOpMHeMwkCQQD5VyfjihPCKU5CBHQ3JTAU42Sw8hiPSPUQWPc4F8lcxhqIkC87GgjY6qD9JlRUE4E5zy+hLCNTgggXa5uS0KMTAkEA4KGScqRoiKl1s9Ei1AQX32RSMQaw0Vh46S3ms44zIfjkCjcKON79MUTL1at/2IENoFDimFZUbYhY9D4QH+cf5QJAeQzYH76kMwospR5WcYNLYYi4FLOkOsP3vdUDSKc7qh+/N/eQBohwLSdTuzMFk7/YaAFvJTcxe1RQq1YhtFg4IwJBAI04xwtQFXAlqZv9FXpZgHCvb4TnAe77QjjG5M1pzvfCtAtAAysx9dgtukCA64U/zUNG1s6TJ80c9V/ITPbhpYkCQQCjQbKZgL5E1njCMFUMkYZF53LaGOUrxC7BzObMIECDaoMQppLWm3coXdEjDCl1ZbmZXIdzZ5QwGUdSj/v7nkne" - }, - "idp": { - "entityId": "idp.example.com", - "singleSignOnService": { - "url": "https://idp.example.com/login", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" - }, - "singleLogoutService": { - "url": "https://idp.example.com/sls", - "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" - }, - "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" - }, - "security": { - "nameIdEncrypted": false, - "authnRequestsSigned": true, - "logoutRequestSigned": true, - "logoutResponseSigned": true, - "signMetadata": true, - "wantMessagesSigned": false, - "wantAssertionsSigned": false, - "wantNameIdEncrypted": false, - "requestedAuthnContext": true - }, - "contactPerson": { - "technical": { - "givenName": "Example Admin", - "emailAddress": "admin@example.com" - }, - "support": { - "givenName": "Example Admin", - "emailAddress": "admin@example.com" - } - }, - "organization": { - "en-US": { - "name": "example.com", - "displayname": "example.com", - "url": "http://example.com" - } - } -} +{ + "strict": false, + "debug": false, + "sp": { + "entityId": "sp.example.com", + "assertionConsumerService": { + "url": "https://sp.example.com/saml/?acs", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://sp.example.com/saml?sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified", + "x509cert": "MIIETTCCA7agAwIBAgIJANaOuOCRgiz3MA0GCSqGSIb3DQEBBQUAMIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb20wHhcNMDUwNzEwMDIyOTAxWhcNMTUwNzA4MDIyOTAxWjCBvDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExPTA7BgNVBAoTNFhNTCBTZWN1cml0eSBMaWJyYXJ5IChodHRwOi8vd3d3LmFsZWtzZXkuY29tL3htbHNlYykxHjAcBgNVBAsTFVRlc3QgUm9vdCBDZXJ0aWZpY2F0ZTEWMBQGA1UEAxMNQWxla3NleSBTYW5pbjEhMB8GCSqGSIb3DQEJARYSeG1sc2VjQGFsZWtzZXkuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABo4IBUzCCAU8wDAYDVR0TBAUwAwEB/zAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFNpG6Wvmr9M9quUhS1LtymYo4P6FMIHxBgNVHSMEgekwgeaAFNpG6Wvmr9M9quUhS1LtymYo4P6FoYHCpIG/MIG8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTE9MDsGA1UEChM0WE1MIFNlY3VyaXR5IExpYnJhcnkgKGh0dHA6Ly93d3cuYWxla3NleS5jb20veG1sc2VjKTEeMBwGA1UECxMVVGVzdCBSb290IENlcnRpZmljYXRlMRYwFAYDVQQDEw1BbGVrc2V5IFNhbmluMSEwHwYJKoZIhvcNAQkBFhJ4bWxzZWNAYWxla3NleS5jb22CCQDWjrjgkYIs9zANBgkqhkiG9w0BAQUFAAOBgQBUXbdOTQwArcNrbxavzARp2JGOnzo6WzTm+OFSXC0F08YwT8jWbht97e8lNNVOBU4Y/38ReZqYC9OqFofG1/O9AdQ58WL/FWg8DgP5MJPTT9kRU3FU01jUiX2+kbdnghZAOJm0ziRNxfNPwIIWPKYXyXEKQQzrnxyFey1hP7cg6A==", + "privateKey": "MIICXQIBAAKBgQDayaFajJxOdVU+8EjwO31S2XqNmYxxbHfiUJO3w2h57OPUkKAcKe5Gvt9hJbPTb3C4blPScOke2RexKnXS7pAXXbxFlgUlZ0QK0K2pdl559OSmrtH3mPP9BJvvDMlxkcNj9/EeD+yGd8GN/yT6PTDh8G/4lszOXL+tyKIkC4Ys/wIDAQABAoGAKfyh43+yi3gG+QIh7UBtZ5Xm5/+8rRO02hC+mHh+t09X1bY/k8gUOy1sLveOUBhF2I8LtQoIIuxkmJJedDFmI0rbHwEMT/3uWzuqVbaPSd5GP9rfHvieF2d4DoZ+iHwyzsagSIgJLe9ZKNaZymdLqMJSaRIkLwTIwUZOpMHeMwkCQQD5VyfjihPCKU5CBHQ3JTAU42Sw8hiPSPUQWPc4F8lcxhqIkC87GgjY6qD9JlRUE4E5zy+hLCNTgggXa5uS0KMTAkEA4KGScqRoiKl1s9Ei1AQX32RSMQaw0Vh46S3ms44zIfjkCjcKON79MUTL1at/2IENoFDimFZUbYhY9D4QH+cf5QJAeQzYH76kMwospR5WcYNLYYi4FLOkOsP3vdUDSKc7qh+/N/eQBohwLSdTuzMFk7/YaAFvJTcxe1RQq1YhtFg4IwJBAI04xwtQFXAlqZv9FXpZgHCvb4TnAe77QjjG5M1pzvfCtAtAAysx9dgtukCA64U/zUNG1s6TJ80c9V/ITPbhpYkCQQCjQbKZgL5E1njCMFUMkYZF53LaGOUrxC7BzObMIECDaoMQppLWm3coXdEjDCl1ZbmZXIdzZ5QwGUdSj/v7nkne" + }, + "idp": { + "entityId": "idp.example.com", + "singleSignOnService": { + "url": "https://idp.example.com/login", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" + }, + "singleLogoutService": { + "url": "https://idp.example.com/sls", + "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" + }, + "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" + }, + "security": { + "nameIdEncrypted": false, + "authnRequestsSigned": true, + "logoutRequestSigned": true, + "logoutResponseSigned": true, + "signMetadata": true, + "wantMessagesSigned": false, + "wantAssertionsSigned": false, + "wantNameIdEncrypted": false, + "requestedAuthnContext": true + }, + "contactPerson": { + "technical": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + }, + "support": { + "givenName": "Example Admin", + "emailAddress": "admin@example.com" + } + }, + "organization": { + "en-US": { + "name": "example.com", + "displayname": "example.com", + "url": "http://example.com" + } + } +} diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 37116c51..2125167c 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -15,6 +15,8 @@ from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils +from lxml.etree import parse, tostring, fromstring +import dm.xmlsec.binding as xmlsec class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): def loadSettingsJSON(self): @@ -229,21 +231,43 @@ 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', 'settings4.json') + 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 = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) - # To verify the assertion is signed correct we can use the xmlsec1 command line tool - # xmlsec1 --verify --id-attr:ID AuthnRequest --store-references --trusted-pem tests/certs/aleksey-xmlsec/cacert.pem --verification-time "2007-07-04 12:12:12" authn_signed_assertion.xml \ No newline at end of file + # 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) From 5bd4b5f5377c95ddc0c9a2847c95468f918f39f2 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 19:58:40 +0000 Subject: [PATCH 03/11] Fixing some TravisCI errors with imports --- .gitignore | 3 +++ src/onelogin/saml2/authn_request.py | 4 +++- tests/src/OneLogin/saml2_tests/authn_request_test.py | 5 ++++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index ad382d79..80fc9494 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,6 @@ __pycache_ settings.py advanced_settings.py + +# Any test files / output that is generated by tests +tests/sample_output diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 4e05214c..795a0598 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -15,10 +15,13 @@ from onelogin.saml2.utils import OneLogin_Saml2_Utils from onelogin.saml2.constants import OneLogin_Saml2_Constants +from onelogin.saml2.errors import OneLogin_Saml2_Error import dm.xmlsec.binding as xmlsec from dm.xmlsec.binding.tmpl import Signature +from lxml.etree import tostring, fromstring + log = logging.getLogger(__name__) class OneLogin_Saml2_Authn_Request(object): @@ -133,7 +136,6 @@ def __init__(self, settings, force_authn=False, is_passive=False): signature = Signature(xmlsec.TransformExclC14N, xmlsec.TransformRsaSha1) - from lxml.etree import parse, tostring, fromstring doc = fromstring(request) # ID attributes different from xml:id must be made known by the application through a call diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 2125167c..38a3fe9a 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -15,7 +15,7 @@ from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.utils import OneLogin_Saml2_Utils -from lxml.etree import parse, tostring, fromstring +from lxml.etree import fromstring import dm.xmlsec.binding as xmlsec class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): @@ -250,6 +250,9 @@ def testSignedHttpPostBinding(self): decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) + 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) From 70c8510f9360edd67c10673d3ebbd4014f9fa97b Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 20:13:31 +0000 Subject: [PATCH 04/11] Fixing lots of small issues with PEP8 --- src/onelogin/saml2/authn_request.py | 20 ++++++++++++------- .../saml2_tests/authn_request_test.py | 3 ++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 795a0598..cd26849f 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -25,6 +25,7 @@ log = logging.getLogger(__name__) class OneLogin_Saml2_Authn_Request(object): + """ This class handles an AuthNRequest. It builds an @@ -125,9 +126,8 @@ def __init__(self, settings, force_authn=False, is_passive=False): 'requested_authn_context_str': requested_authn_context_str, } - # Only the urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding gets the enveloped signature - if settings.get_idp_data()['singleSignOnService'].get('binding', None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' and security['authnRequestsSigned'] == True: + if settings.get_idp_data()['singleSignOnService'].get('binding', None) == 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST' and security['authnRequestsSigned'] is True: log.debug("Generating AuthnRequest using urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST binding") @@ -144,7 +144,7 @@ def __init__(self, settings, force_authn=False, is_passive=False): doc.insert(0, signature) - ref = signature.addReference(xmlsec.TransformSha1,uri="#%s" % uid) + ref = signature.addReference(xmlsec.TransformSha1, uri="#%s" % uid) ref.addTransform(xmlsec.TransformEnveloped) ref.addTransform(xmlsec.TransformExclC14N) @@ -152,7 +152,6 @@ def __init__(self, settings, force_authn=False, is_passive=False): key_info.addKeyName() key_info.addX509Data() - # Load the key into the xmlsec context key = settings.get_sp_key() if not key: @@ -186,9 +185,16 @@ def print_xmlsec_errors(self, filename, line, func, errorObject, errorSubject, r # the following prints if we get something with relation to the application info = [] - if errorObject != "unknown": info.append("obj=" + errorObject) - if errorSubject != "unknown": info.append("subject=" + errorSubject) - if msg.strip(): info.append("msg=" + msg) + + if errorObject != "unknown": + info.append("obj=" + errorObject) + + if errorSubject != "unknown": + info.append("subject=" + errorSubject) + + if msg.strip(): + info.append("msg=" + msg) + if info: print "%s:%d(%s)" % (filename, line, func), " ".join(info) diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 38a3fe9a..e4fd01a1 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -19,6 +19,7 @@ import dm.xmlsec.binding as xmlsec class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): + def loadSettingsJSON(self): filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings1.json') if exists(filename): @@ -234,7 +235,7 @@ def testSignedHttpPostBinding(self): 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 """ From 14c73470f3e4f402d695c1613bbcf8c1cef10476 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 20:20:41 +0000 Subject: [PATCH 05/11] Attempting to fix the blank lines issue with PEP8 --- src/onelogin/saml2/authn_request.py | 1 + tests/src/OneLogin/saml2_tests/authn_request_test.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index cd26849f..71dbf551 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -26,6 +26,7 @@ class OneLogin_Saml2_Authn_Request(object): + """ This class handles an AuthNRequest. It builds an diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index e4fd01a1..df2db5cf 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -20,6 +20,7 @@ class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): + def loadSettingsJSON(self): filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings1.json') if exists(filename): @@ -246,7 +247,7 @@ def testSignedHttpPostBinding(self): settings = OneLogin_Saml2_Settings(settings) - authn_request = OneLogin_Saml2_Authn_Request(settings) + authn_request = OneLogin_Saml2_Authn_Request(settings) authn_request_encoded = authn_request.get_request() decoded = b64decode(authn_request_encoded) inflated = decompress(decoded, -15) From 80a3aa6ea226a65bd68b5631669b72f41166fc37 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 20:22:42 +0000 Subject: [PATCH 06/11] Make sure sample output directory is made before writing to it --- tests/src/OneLogin/saml2_tests/authn_request_test.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index df2db5cf..fd45f7b8 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -252,6 +252,10 @@ def testSignedHttpPostBinding(self): 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) From 965093f1331701978b6fa590ee77f50ca4f7420e Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 20:32:08 +0000 Subject: [PATCH 07/11] Forgot an import and trying to fix more blank lines --- tests/src/OneLogin/saml2_tests/authn_request_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index fd45f7b8..865540d3 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -3,6 +3,7 @@ # Copyright (c) 2014, OneLogin, Inc. # All rights reserved. +import os from base64 import b64decode import json from os.path import dirname, join, exists @@ -20,7 +21,6 @@ class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): - def loadSettingsJSON(self): filename = join(dirname(__file__), '..', '..', '..', 'settings', 'settings1.json') if exists(filename): From 2bfdf7174d5f10514c95fb569d8a30303c097ec9 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sat, 8 Aug 2015 20:38:32 +0000 Subject: [PATCH 08/11] More pep8 fixes --- src/onelogin/saml2/authn_request.py | 2 +- tests/src/OneLogin/saml2_tests/authn_request_test.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onelogin/saml2/authn_request.py b/src/onelogin/saml2/authn_request.py index 71dbf551..ccd468ab 100644 --- a/src/onelogin/saml2/authn_request.py +++ b/src/onelogin/saml2/authn_request.py @@ -24,8 +24,8 @@ log = logging.getLogger(__name__) -class OneLogin_Saml2_Authn_Request(object): +class OneLogin_Saml2_Authn_Request(object): """ diff --git a/tests/src/OneLogin/saml2_tests/authn_request_test.py b/tests/src/OneLogin/saml2_tests/authn_request_test.py index 865540d3..5a08c026 100644 --- a/tests/src/OneLogin/saml2_tests/authn_request_test.py +++ b/tests/src/OneLogin/saml2_tests/authn_request_test.py @@ -19,6 +19,7 @@ from lxml.etree import fromstring import dm.xmlsec.binding as xmlsec + class OneLogin_Saml2_Authn_Request_Test(unittest.TestCase): def loadSettingsJSON(self): From 49ee5bde52b5eddd8989d0125b8719b342bd1ef0 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sun, 9 Aug 2015 17:57:15 +0000 Subject: [PATCH 09/11] HTML form generation for AuthnRequest using Jinja2 library. --- setup.py | 1 + src/onelogin/saml2/auth.py | 25 +++++++++++++++++++ .../saml2/templates/authn_request.html | 12 +++++++++ tests/src/OneLogin/saml2_tests/auth_test.py | 22 ++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 src/onelogin/saml2/templates/authn_request.html diff --git a/setup.py b/setup.py index faf8612b..073f87f9 100644 --- a/setup.py +++ b/setup.py @@ -36,6 +36,7 @@ 'dm.xmlsec.binding==1.3.2', 'isodate==0.5.0', 'defusedxml==0.4.1', + 'Jinja2==2.7.3', ], extras_require={ 'test': ( diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 49cbfbce..3d64bd36 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -11,8 +11,11 @@ """ +import logging from base64 import b64encode from urllib import quote_plus +from os.path import dirname, join +from jinja2 import Template import dm.xmlsec.binding as xmlsec @@ -25,6 +28,7 @@ from onelogin.saml2.logout_request import OneLogin_Saml2_Logout_Request from onelogin.saml2.authn_request import OneLogin_Saml2_Authn_Request +log = logging.getLogger(__name__) class OneLogin_Saml2_Auth(object): """ @@ -276,6 +280,27 @@ def login(self, return_to=None, force_authn=False, is_passive=False): 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) def logout(self, return_to=None, name_id=None, session_index=None): diff --git a/src/onelogin/saml2/templates/authn_request.html b/src/onelogin/saml2/templates/authn_request.html new file mode 100644 index 00000000..5fd0c375 --- /dev/null +++ b/src/onelogin/saml2/templates/authn_request.html @@ -0,0 +1,12 @@ + + +
+ + + +
+ + + \ No newline at end of file diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index ea0b9fd0..45bf5cca 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -543,6 +543,28 @@ def testLogin(self): hostname = OneLogin_Saml2_Utils.get_self_host(request_data) self.assertIn(u'http://%s/index.html' % hostname, parsed_query['RelayState']) + def testLoginHttpPostBinding(self): + """ + Tests the login method of the OneLogin_Saml2_Auth class + Case Login with no parameters. An AuthnRequest is built into a form. + """ + + filename = join(dirname(__file__), '..', '..', '..', 'settings', 'example_settings_http_post_binding.json') + stream = open(filename, 'r') + settings_info = json.load(stream) + stream.close() + + request_data = self.get_request() + auth = OneLogin_Saml2_Auth(request_data, old_settings=settings_info) + + html = auth.login() + + print html + + sso_url = settings_info['idp']['singleSignOnService']['url'] + self.assertIn(sso_url, html) + + def testLoginWithRelayState(self): """ Tests the login method of the OneLogin_Saml2_Auth class From 75342f5e1416126cdc3a85c55c0cf854f8504685 Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Sun, 9 Aug 2015 18:01:45 +0000 Subject: [PATCH 10/11] Removing print statement and some blank lines --- tests/src/OneLogin/saml2_tests/auth_test.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/src/OneLogin/saml2_tests/auth_test.py b/tests/src/OneLogin/saml2_tests/auth_test.py index 45bf5cca..77317fd9 100644 --- a/tests/src/OneLogin/saml2_tests/auth_test.py +++ b/tests/src/OneLogin/saml2_tests/auth_test.py @@ -559,12 +559,9 @@ def testLoginHttpPostBinding(self): html = auth.login() - print html - sso_url = settings_info['idp']['singleSignOnService']['url'] self.assertIn(sso_url, html) - def testLoginWithRelayState(self): """ Tests the login method of the OneLogin_Saml2_Auth class From cfef0955a64e8b8be301ac20e7cdb126320490ea Mon Sep 17 00:00:00 2001 From: Jeff Tchang Date: Mon, 10 Aug 2015 17:39:31 +0000 Subject: [PATCH 11/11] Fixing PEP8 whitespace --- src/onelogin/saml2/auth.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/onelogin/saml2/auth.py b/src/onelogin/saml2/auth.py index 3d64bd36..319427c6 100644 --- a/src/onelogin/saml2/auth.py +++ b/src/onelogin/saml2/auth.py @@ -30,6 +30,7 @@ log = logging.getLogger(__name__) + class OneLogin_Saml2_Auth(object): """ @@ -291,9 +292,9 @@ def login(self, return_to=None, force_authn=False, is_passive=False): template = Template(template_text) context = { - 'sso_url' : self.get_sso_url(), - 'saml_request' : saml_request, - 'relay_state' : parameters['RelayState'] + 'sso_url': self.get_sso_url(), + 'saml_request': saml_request, + 'relay_state': parameters['RelayState'] } html = template.render(context)