def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://idp.testshib.org/idp/shibboleth') except URLError: xml = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "x509cert": "MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==", "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xml = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml) self.assertTrue(data is not None and data is not {}) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def test_parse_testshib_required_binding_sso_post(self): """ Test with testshib metadata. Especially test extracting SSO with POST binding. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/POST/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" } } } """ try: xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( 'https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xmldoc = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) # Parse, require POST binding. settings = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST ) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings)
def testParse(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(XMLSyntaxError): data = OneLogin_Saml2_IdPMetadataParser.parse('') xml_sp_metadata = self.file_contents(join(self.data_path, 'metadata', 'metadata_settings1.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_sp_metadata) self.assertEqual({}, data) xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) # W/o further specification, expect to get the redirect binding SSO # URL extracted. expected_settings_json = """ { "idp": { "singleSignOnService": { "url": "https://app.onelogin.com/trust/saml2/http-post/sso/383123", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509certs": ["MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzET\\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYD\\nVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2\\nMDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI\\nDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9u\\nZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0B\\nAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z\\n0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sT\\ngf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0m\\nTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SF\\nzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJ\\nUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwG\\nA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNV\\nHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJV\\nUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREw\\nDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAO\\nBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHu\\nAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcV\\ngG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJ\\nsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClP\\nTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWu\\nQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh78\\n1sE="], "entityId": "https://app.onelogin.com/saml/metadata/383123" }, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testGetMetadata(self): """ Tests the get_metadata method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.get_metadata('http://google.es') data = OneLogin_Saml2_IdPMetadataParser.get_metadata('https://www.testshib.org/metadata/testshib-providers.xml') self.assertTrue(data is not None and data is not {})
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote('http://google.es') data = OneLogin_Saml2_IdPMetadataParser.parse_remote('https://www.testshib.org/metadata/testshib-providers.xml') self.assertTrue(data is not None and data is not {}) expected_data = {'sp': {'NameIDFormat': 'urn:mace:shibboleth:1.0:nameIdentifier'}, 'idp': {'singleLogoutService': {'url': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO'}, 'entityId': 'https://idp.testshib.org/idp/shibboleth'}} self.assertEqual(expected_data, data)
def test_parse_multi_same_signing_and_encrypt_cert(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: IdP metadata contains multiple signature cert and encrypt cert that is the same """ xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_same_sign_and_encrypt_cert.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" }, "idp": { "x509cert": "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=", "entityId": "https://app.onelogin.com/saml/metadata/383123", "singleSignOnService": { "url": "https://app.onelogin.com/trust/saml2/http-post/sso/383123", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data) xml_idp_metadata_2 = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_different_sign_and_encrypt_cert.xml')) data_2 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata_2) expected_settings_json_2 = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" }, "idp": { "x509certMulti": { "encryption": [ "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==" ], "signing": [ "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=" ] }, "entityId": "https://app.onelogin.com/saml/metadata/383123", "singleSignOnService": { "url": "https://app.onelogin.com/trust/saml2/http-post/sso/383123", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings_2 = json.loads(expected_settings_json_2) self.assertEqual(expected_settings_2, data_2)
def testGetMetadata(self): """ Tests the get_metadata method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.get_metadata('http://google.es') try: data = OneLogin_Saml2_IdPMetadataParser.get_metadata('https://www.testshib.org/metadata/testshib-providers.xml') except URLError: data = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) self.assertTrue(data is not None and data is not {})
def test_parse_multi_singing_certs(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: IdP metadata contains multiple signing certs and no encryption certs """ xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_multi_signing_certs.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" }, "idp": { "singleLogoutService": { "url": "https://idp.examle.com/saml/slo", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509certMulti": { "signing": [ "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==", "MIICZDCCAc2gAwIBAgIBADANBgkqhkiG9w0BAQ0FADBPMQswCQYDVQQGEwJ1czEUMBIGA1UECAwLZXhhbXBsZS5jb20xFDASBgNVBAoMC2V4YW1wbGUuY29tMRQwEgYDVQQDDAtleGFtcGxlLmNvbTAeFw0xNzA0MTUxNjMzMThaFw0xODA0MTUxNjMzMThaME8xCzAJBgNVBAYTAnVzMRQwEgYDVQQIDAtleGFtcGxlLmNvbTEUMBIGA1UECgwLZXhhbXBsZS5jb20xFDASBgNVBAMMC2V4YW1wbGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6GLkl5lDUZdHNDAojp5i24OoPlqrt5TGXJIPqAZYT1hQvJW5nv17MFDHrjmtEnmW4ACKEy0fAX80QWIcHunZSkbEGHb+NG/6oTi5RipXMvmHnfFnPJJ0AdtiLiPE478CV856gXekV4Xx5u3KrylcOgkpYsp0GMIQBDzleMUXlYQIDAQABo1AwTjAdBgNVHQ4EFgQUnP8vlYPGPL2n6ZzDYij2kMDC8wMwHwYDVR0jBBgwFoAUnP8vlYPGPL2n6ZzDYij2kMDC8wMwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOBgQAlQGAl+b8Cpot1g+65lLLjVoY7APJPWLW0klKQNlMU0s4MU+71Y3ExUEOXDAZgKcFoavb1fEOGMwEf38NaJAy1e/l6VNuixXShffq20ymqHQxOG0q8ujeNkgZF9k6XDfn/QZ3AD0o/IrCT7UMc/0QsfgIjWYxwCvp2syApc5CYfQ==", "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==" ] }, "entityId": "https://idp.examle.com/saml/metadata", "singleSignOnService": { "url": "https://idp.examle.com/saml/sso", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def testMergeSettings(self): """ Tests the merge_settings method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(AttributeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings(None, {}) with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings({}, None) xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) settings = self.loadSettingsJSON() settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, data) expected_data = {u'sp': {'NameIDFormat': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'}, u'idp': {'singleLogoutService': {'url': 'https://app.onelogin.com/trust/saml2/http-post/sso/383123'}, 'entityId': 'https://app.onelogin.com/saml/metadata/383123', 'x509cert': 'MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYD\nVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2\nMDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI\nDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9u\nZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z\n0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sT\ngf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0m\nTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SF\nzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJ\nUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwG\nA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNV\nHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREw\nDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAO\nBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHu\nAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcV\ngG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJ\nsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClP\nTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWu\nQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh78\n1sE='}, u'strict': False, u'contactPerson': {u'technical': {u'givenName': u'technical_name', u'emailAddress': u'*****@*****.**'}, u'support': {u'givenName': u'support_name', u'emailAddress': u'*****@*****.**'}}, u'debug': False, u'organization': {u'en-US': {u'url': u'http://sp.example.com', u'displayname': u'SP test', u'name': u'sp_test'}}, u'security': {u'signMetadata': False, u'wantAssertionsSigned': False, u'authnRequestsSigned': False}, u'custom_base_path': u'../../../tests/data/customPath/'} self.assertEqual(expected_data, settings_result) expected_data2 = {'sp': {u'singleLogoutService': {u'url': u'http://stuff.com/endpoints/endpoints/sls.php'}, u'assertionConsumerService': {u'url': u'http://stuff.com/endpoints/endpoints/acs.php'}, u'entityId': u'http://stuff.com/endpoints/metadata.php', u'NameIDFormat': u'urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified'}, 'idp': {u'singleLogoutService': {u'url': u'http://idp.example.com/SingleLogoutService.php'}, u'entityId': u'http://idp.example.com/', u'x509cert': u'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo', u'singleSignOnService': {u'url': u'http://idp.example.com/SSOService.php'}}, u'strict': False, u'contactPerson': {u'technical': {u'givenName': u'technical_name', u'emailAddress': u'*****@*****.**'}, u'support': {u'givenName': u'support_name', u'emailAddress': u'*****@*****.**'}}, u'debug': False, u'organization': {u'en-US': {u'url': u'http://sp.example.com', u'displayname': u'SP test', u'name': u'sp_test'}}, u'security': {u'signMetadata': False, u'wantAssertionsSigned': False, u'authnRequestsSigned': False}, u'custom_base_path': u'../../../tests/data/customPath/'} settings_result2 = OneLogin_Saml2_IdPMetadataParser.merge_settings(data, settings) self.assertEqual(expected_data2, settings_result2)
def test_parse_testshib_required_binding_sso_redirect(self): """ Test with testshib metadata. Especially test extracting SSO with REDIRECT binding. Note that the testshib metadata does not contain an SLO specification in the first <IDPSSODescriptor> tag. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "x509cert": "MIIDAzCCAeugAwIBAgIVAPX0G6LuoXnKS0Muei006mVSBXbvMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNVBAMMEGlkcC50ZXN0c2hpYi5vcmcwHhcNMTYwODIzMjEyMDU0WhcNMzYwODIzMjEyMDU0WjAbMRkwFwYDVQQDDBBpZHAudGVzdHNoaWIub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAg9C4J2DiRTEhJAWzPt1S3ryhm3M2P3hPpwJwvt2q948vdTUxhhvNMuc3M3S4WNh6JYBs53R+YmjqJAII4ShMGNEmlGnSVfHorex7IxikpuDPKV3SNf28mCAZbQrX+hWA+ann/uifVzqXktOjs6DdzdBnxoVhniXgC8WCJwKcx6JO/hHsH1rG/0DSDeZFpTTcZHj4S9MlLNUtt5JxRzV/MmmB3ObaX0CMqsSWUOQeE4nylSlp5RWHCnx70cs9kwz5WrflnbnzCeHU2sdbNotBEeTHot6a2cj/pXlRJIgPsrL/4VSicPZcGYMJMPoLTJ8mdy6mpR6nbCmP7dVbCIm/DQIDAQABoz4wPDAdBgNVHQ4EFgQUUfaDa2mPi24x09yWp1OFXmZ2GPswGwYDVR0RBBQwEoIQaWRwLnRlc3RzaGliLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEASKKgqTxhqBzROZ1eVy++si+eTTUQZU4+8UywSKLia2RattaAPMAcXUjO+3cYOQXLVASdlJtt+8QPdRkfp8SiJemHPXC8BES83pogJPYEGJsKo19l4XFJHPnPy+Dsn3mlJyOfAa8RyWBS80u5lrvAcr2TJXt9fXgkYs7BOCigxtZoR8flceGRlAZ4p5FPPxQR6NDYb645jtOTMVr3zgfjP6Wh2dt+2p04LG7ENJn8/gEwtXVuXCsPoSCDx9Y0QmyXTJNdV1aB0AhORkWPlFYwp+zOyOIR+3m1+pqWFpn0eT/HrxpdKa74FA3R2kq4R7dXe4G0kUgXTdqXMLRKhDgdmA==", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ try: xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( 'https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xmldoc = self.file_contents( join(self.data_path, 'metadata', 'testshib-providers.xml')) # Parse, require SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings1) self.assertEqual(expected_settings, settings2)
def _set_saml_settings(cmd, sp_x_509_cert, sp_private_key, has_sp_cert): ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr( cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) idp_settings = _parse_saml_settings(idp_metadata, idp_entity_id) url_prefix = prepare_url_prefix( mgr.get_module_option('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [ { "name": idp_username_attribute, "isRequired": True } ] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, # Not all Identity Providers support this. "wantNameIdEncrypted": False, "metadataValidUntil": '', "wantAttributeStatement": False } } settings = Saml2Parser.merge_settings(settings, idp_settings) mgr.SSO_DB.saml2.onelogin_settings = settings mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save()
def test_parse_testshib_required_binding_sso_redirect(self): """ Test with testshib metadata. Especially test extracting SSO with REDIRECT binding. Note that the testshib metadata does not contain an SLO specification in the first <IDPSSODescriptor> tag. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ try: xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( 'https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xmldoc = self.file_contents( join(self.data_path, 'metadata', 'testshib-providers.xml')) # Parse, require SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings1) self.assertEqual(expected_settings, settings2)
def initialize(self, *args, **kwargs): super().initialize(*args, **kwargs) self.log.info("Initializing AuthHub") self.parse_command_line(*args, **kwargs) if self.generate_config: return self.log.info("Loading config") self.load_config_file(self.config_file) if self.auto_IdP_metadata: self.log.info("Getting the IdP metadata.") idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( self.auto_IdP_metadata) self.saml_settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( self.saml_settings, idp_data) self.init_logging() self.init_db() self.init_secrets() self.init_handlers() self.init_tornado_settings() self.init_tornado()
def _get_saml_settings(): # load settings from environment settings = current_app.config.get('SAML_SETTINGS') # if settings doesn't exist and a file is provided, load the file settings_file = current_app.config.get('SAML_SETTINGS_FILE') if not settings and settings_file: with open(settings_file, 'r') as json_data_file: settings = json.load(json_data_file) # if saml metadata url is provided, load idp settings via metadata idp_metadata_url = current_app.config.get('SAML_METADATA_URL') idp_metadata_entity_id = current_app.config.get('SAML_METADATA_ENTITY_ID', None) if idp_metadata_url: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url, entity_id=idp_metadata_entity_id) settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, idp_settings) return settings
def test_parse_testshib_required_binding_sso_redirect(self): """ Test with testshib metadata. Especially test extracting SSO with REDIRECT binding. Note that the testshib metadata does not contain an SLO specification in the first <IDPSSODescriptor> tag. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:mace:shibboleth:1.0:nameIdentifier" }, "idp": { "entityId": "https://idp.testshib.org/idp/shibboleth", "singleSignOnService": { "url": "https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ try: xmldoc = OneLogin_Saml2_IdPMetadataParser.get_metadata( 'https://www.testshib.org/metadata/testshib-providers.xml') except URLError: xmldoc = self.file_contents(join(self.data_path, 'metadata', 'testshib-providers.xml')) # Parse, require SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings1) self.assertEqual(expected_settings, settings2)
def _get_saml_settings(): # load settings from environment settings = current_app.config.get('SAML_SETTINGS') # if settings doesn't exist and a file is provided, load the file settings_file = current_app.config.get('SAML_SETTINGS_FILE') if not settings and settings_file: with open(settings_file, 'r') as json_data_file: settings = json.load(json_data_file) # if saml metadata url is provided, load idp settings via metadata idp_metadata_url = current_app.config.get('SAML_METADATA_URL') idp_metadata_entity_id = current_app.config.get('SAML_METADATA_ENTITY_ID', None) if idp_metadata_url: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url, entity_id=idp_metadata_entity_id ) settings = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, idp_settings) return settings
async def init_saml_auth(request): saml_config = copy.deepcopy( config.get("get_user_by_saml_settings.saml_settings", {})) idp_metadata_url = config.get("get_user_by_saml_settings.idp_metadata_url") if idp_metadata_url: idp_metadata = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata_url) saml_config = dict_merge(saml_config, idp_metadata) auth = await sync_to_async(OneLogin_Saml2_Auth)( request, saml_config, custom_base_path=config.get("get_user_by_saml_settings.saml_path"), ) return auth
def test_parse_with_entity_id(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: Provide entity_id to identify the desired IdPDescriptor from EntitiesDescriptor """ xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_multiple_descriptors.xml')) # should find first descriptor data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) self.assertEqual("https://foo.example.com/access/saml/idp.xml", data["idp"]["entityId"]) # should find desired descriptor data2 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata, entity_id="https://bar.example.com/access/saml/idp.xml") self.assertEqual("https://bar.example.com/access/saml/idp.xml", data2["idp"]["entityId"]) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, "idp": { "singleLogoutService": { "url": "https://hello.example.com/access/saml/logout", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "entityId": "https://bar.example.com/access/saml/idp.xml", "x509cert": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURxekNDQXhTZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBRENCaGpFTE1Ba0dBMVVFQmhNQ1FWVXgKRERBS0JnTlZCQWdUQTA1VFZ6RVBNQTBHQTFVRUJ4TUdVM2xrYm1WNU1Rd3dDZ1lEVlFRS0RBTlFTVlF4Q1RBSApCZ05WQkFzTUFERVlNQllHQTFVRUF3d1BiR0YzY21WdVkyVndhWFF1WTI5dE1TVXdJd1lKS29aSWh2Y05BUWtCCkRCWnNZWGR5Wlc1alpTNXdhWFJBWjIxaGFXd3VZMjl0TUI0WERURXlNRFF4T1RJeU5UUXhPRm9YRFRNeU1EUXgKTkRJeU5UUXhPRm93Z1lZeEN6QUpCZ05WQkFZVEFrRlZNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVApCbE41Wkc1bGVURU1NQW9HQTFVRUNnd0RVRWxVTVFrd0J3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psCmJtTmxjR2wwTG1OdmJURWxNQ01HQ1NxR1NJYjNEUUVKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnYKYlRDQm56QU5CZ2txaGtpRzl3MEJBUUVGQUFPQmpRQXdnWWtDZ1lFQXFqaWUzUjJvaStwRGFldndJeXMvbWJVVApubkdsa3h0ZGlrcnExMXZleHd4SmlQTmhtaHFSVzNtVXVKRXpsbElkVkw2RW14R1lUcXBxZjkzSGxoa3NhZUowCjhVZ2pQOVVtTVlyaFZKdTFqY0ZXVjdmei9yKzIxL2F3VG5EVjlzTVlRcXVJUllZeTdiRzByMU9iaXdkb3ZudGsKN2dGSTA2WjB2WmFjREU1Ym9xVUNBd0VBQWFPQ0FTVXdnZ0VoTUFrR0ExVWRFd1FDTUFBd0N3WURWUjBQQkFRRApBZ1VnTUIwR0ExVWREZ1FXQkJTUk9OOEdKOG8rOGpnRnRqa3R3WmRxeDZCUnlUQVRCZ05WSFNVRUREQUtCZ2dyCkJnRUZCUWNEQVRBZEJnbGdoa2dCaHZoQ0FRMEVFQllPVkdWemRDQllOVEE1SUdObGNuUXdnYk1HQTFVZEl3U0IKcXpDQnFJQVVrVGpmQmlmS1B2STRCYlk1TGNHWGFzZWdVY21oZ1l5a2dZa3dnWVl4Q3pBSkJnTlZCQVlUQWtGVgpNUXd3Q2dZRFZRUUlFd05PVTFjeER6QU5CZ05WQkFjVEJsTjVaRzVsZVRFTU1Bb0dBMVVFQ2d3RFVFbFVNUWt3CkJ3WURWUVFMREFBeEdEQVdCZ05WQkFNTUQyeGhkM0psYm1ObGNHbDBMbU52YlRFbE1DTUdDU3FHU0liM0RRRUoKQVF3V2JHRjNjbVZ1WTJVdWNHbDBRR2R0WVdsc0xtTnZiWUlCQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9CZ1FDRQpUQWVKVERTQVc2ejFVRlRWN1FyZWg0VUxGT1JhajkrZUN1RjNLV0RIYyswSVFDajlyZG5ERzRRL3dmNy9yYVEwCkpuUFFDU0NkclBMSmV5b1BIN1FhVHdvYUY3ZHpWdzRMQ3N5TkpURld4NGNNNTBWdzZSNWZET2dpQzhic2ZmUzgKQkptb3VscnJaRE5OVmpHOG1XNmNMeHJZdlZRT3JSVmVjQ0ZJZ3NzQ2JBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=", "singleSignOnService": { "url": "https://hello.example.com/access/saml/login", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data2)
def testParseRemote(self): """ Tests the parse_remote method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(Exception): data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'http://google.es') data = OneLogin_Saml2_IdPMetadataParser.parse_remote( 'https://www.testshib.org/metadata/testshib-providers.xml') self.assertTrue(data is not None and data is not {}) expected_data = { 'sp': { 'NameIDFormat': 'urn:mace:shibboleth:1.0:nameIdentifier' }, 'idp': { 'singleLogoutService': { 'url': 'https://idp.testshib.org/idp/profile/SAML2/Redirect/SSO' }, 'entityId': 'https://idp.testshib.org/idp/shibboleth' } } self.assertEqual(expected_data, data)
def remote_metadata(self): """Load the IdP metadata from the remote server and cache it for future accesses""" cache_key = '%s-idp-metadata' % self.name cached_metadata = cache.get(cache_key) if cached_metadata: idp_config = json.loads(cached_metadata) else: idp_config = OneLogin_Saml2_IdPMetadataParser.parse_remote( self.metadata_url) idp = idp_config['idp'] cert = self.find_valid_certificate(idp) out = { 'entity_id': idp['entityId'], 'url': idp['singleSignOnService']['url'], 'x509cert': cert, } cache.set(cache_key, json.dumps(idp_config), timeout=24 * 3600) return out
def _settings_from_url(self, metadata_url): idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( metadata_url)["idp"] sp_settings = { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified", "assertionConsumerService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTPS-POST", "url": "%s/saml/acs" % self.domain }, "x509cert": self.x509cert, "privateKey": self.private_key, "entityId": "%s/saml/metadata" % self.domain, "singleLogoutService": { "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", "url": "%s/saml/sls" % self.domain } } return {"strict": True, "sp": sp_settings, "idp": idp_settings}
def test_parse_multi_certs(self): """ Tests the parse method of the OneLogin_Saml2_IdPMetadataParser Case: IdP metadata contains multiple certs """ xml_idp_metadata = self.file_contents( join(self.data_path, 'metadata', 'idp_metadata_multi_certs.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" }, "idp": { "singleLogoutService": { "url": "https://idp.examle.com/saml/slo", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509certMulti": { "encryption": [ "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==" ], "signing": [ "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" ] }, "entityId": "https://idp.examle.com/saml/metadata", "singleSignOnService": { "url": "https://idp.examle.com/saml/sso", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, data)
def load_idp_metadata(self, url=None): """Load IdP metadata in an XML format from the specified url. :param url: URL of a metadata service :type url: Optional[string] :return: XML string containing InCommon Metadata :rtype: string :raise: MetadataLoadError """ self._logger.info( "Started loading IdP XML metadata from {0}".format(url)) try: xml_metadata = OneLogin_Saml2_IdPMetadataParser.get_metadata(url) except Exception as exception: raise SAMLMetadataLoadingError(inner_exception=exception) self._logger.info( "Finished loading IdP XML metadata from {0}".format(url)) return xml_metadata
def handle(self, request, helper): missing_values = error_value = error_url = False id_or_metadata_url = url = '' if 'action_save' in request.POST: id_or_metadata_url = request.POST['id_or_metadata_url'] # Get metadata url if an app_id was provided if id_or_metadata_url and id_or_metadata_url.isdigit(): id_or_metadata_url = ONELOGIN_METADATA_URL_PREFIX + id_or_metadata_url validate_url = URLValidator() try: validate_url(id_or_metadata_url) url = id_or_metadata_url try: data = OneLogin_Saml2_IdPMetadataParser.parse_remote(url) if data and 'idp' in data: idp_data = SAML2Provider.extract_idp_data_from_parsed_data(data) form2 = OneLoginSAMLForm(idp_data) if form2.is_valid(): helper.bind_state('idp', idp_data) helper.bind_state('contact', request.user.email) return helper.next_step() else: missing_values = form2.errors.keys except Exception: error_url = True except ValidationError: error_value = True return self.respond('sentry_auth_onelogin/select-idp.html', { 'error_value': error_value, 'error_url': error_url, 'missing_values': missing_values, 'id_or_metadata_url': id_or_metadata_url })
def testMergeSettings(self): """ Tests the merge_settings method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(AttributeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( None, {}) with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( {}, None) xml_idp_metadata = self.file_contents( join(self.data_path, 'metadata', 'idp_metadata.xml')) data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) settings = self.loadSettingsJSON() settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, data) expected_data = { u'sp': { 'NameIDFormat': 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress' }, u'idp': { 'singleLogoutService': { 'url': 'https://app.onelogin.com/trust/saml2/http-post/sso/383123' }, 'entityId': 'https://app.onelogin.com/saml/metadata/383123', 'x509cert': 'MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYD\nVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2\nMDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQI\nDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9u\nZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z\n0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sT\ngf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0m\nTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SF\nzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJ\nUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwG\nA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNV\nHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJV\nUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREw\nDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAO\nBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHu\nAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcV\ngG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJ\nsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClP\nTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWu\nQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh78\n1sE=' }, u'strict': False, u'contactPerson': { u'technical': { u'givenName': u'technical_name', u'emailAddress': u'*****@*****.**' }, u'support': { u'givenName': u'support_name', u'emailAddress': u'*****@*****.**' } }, u'debug': False, u'organization': { u'en-US': { u'url': u'http://sp.example.com', u'displayname': u'SP test', u'name': u'sp_test' } }, u'security': { u'signMetadata': False, u'wantAssertionsSigned': False, u'authnRequestsSigned': False }, u'custom_base_path': u'../../../tests/data/customPath/' } self.assertEqual(expected_data, settings_result) expected_data2 = { 'sp': { u'singleLogoutService': { u'url': u'http://stuff.com/endpoints/endpoints/sls.php' }, u'assertionConsumerService': { u'url': u'http://stuff.com/endpoints/endpoints/acs.php' }, u'entityId': u'http://stuff.com/endpoints/metadata.php', u'NameIDFormat': u'urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified' }, 'idp': { u'singleLogoutService': { u'url': u'http://idp.example.com/SingleLogoutService.php' }, u'entityId': u'http://idp.example.com/', u'x509cert': u'MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo', u'singleSignOnService': { u'url': u'http://idp.example.com/SSOService.php' } }, u'strict': False, u'contactPerson': { u'technical': { u'givenName': u'technical_name', u'emailAddress': u'*****@*****.**' }, u'support': { u'givenName': u'support_name', u'emailAddress': u'*****@*****.**' } }, u'debug': False, u'organization': { u'en-US': { u'url': u'http://sp.example.com', u'displayname': u'SP test', u'name': u'sp_test' } }, u'security': { u'signMetadata': False, u'wantAssertionsSigned': False, u'authnRequestsSigned': False }, u'custom_base_path': u'../../../tests/data/customPath/' } settings_result2 = OneLogin_Saml2_IdPMetadataParser.merge_settings( data, settings) self.assertEqual(expected_data2, settings_result2)
def _build_configuration(self, idp): """Update default config with the ones read from configuration.""" def update(d, u): for k, v in u.items(): if isinstance(v, collections.Mapping): d[k] = update(d.get(k, {}), v) else: d[k] = v return d def make_handler(handler, default=None): handler = handler if handler else default return (import_string(handler) if handler and isinstance(handler, string_types) else handler) config = _default_config(idp) update(config, self.app.config["SSO_SAML_IDPS"][idp]) # Read IdP config from file or URL if any if config["settings_url"]: external_conf = OneLogin_Saml2_IdPMetadataParser.parse_remote( config["settings_url"]) config["settings"]["idp"].update(external_conf.get("idp")) if config["settings_file_path"]: with open(config["settings_file_path"], "r") as idp: file = config["settings_file_path"] # xml format if file.endswith(".xml"): external_conf = OneLogin_Saml2_IdPMetadataParser.parse( idp.read()) # json format elif file.endswith(".json"): external_conf = json.loads(idp.read()) config["settings"]["idp"].update(external_conf.get("idp")) # Load certificate and key if config["sp_cert_file"]: with open(config["sp_cert_file"], "r") as cf: cert = cf.read() config["settings"]["sp"]["x509cert"] = cert if config["sp_key_file"]: with open(config["sp_key_file"], "r") as cf: cert = cf.read() config["settings"]["sp"]["privateKey"] = cert # Import handlers is present config["settings_handler"] = make_handler( config["settings_handler"], self.app.config.get("SSO_SAML_DEFAUTL_SETTINGS_HANDLER"), ) config["login_handler"] = make_handler( config["login_handler"], self.app.config.get("SSO_SAML_DEFAUTL_LOGIN_HANDLER"), ) config["logout_handler"] = make_handler( config["logout_handler"], self.app.config.get("SSO_SAML_DEFAUTL_LOGOUT_HANDLER"), ) config["acs_handler"] = make_handler( config["acs_handler"], self.app.config.get("SSO_SAML_DEFAUTL_ACS_HANDLER")) config["sls_handler"] = make_handler( config["sls_handler"], self.app.config.get("SSO_SAML_DEFAUTL_SLS_HANDLER")) return config
def spSettings(cod_sp, cod_idp=None, binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, close=True): result = {'error': 0, 'result': None} # acquisisci una connessione dal pool #if conn is None: # conn = easyspid.lib.database.Database(globalsObj.DbConnections['samlDbPollSlave']['pool']) #sp_settings = conn.get_sp_settings(cod_sp, close) dbobj = globalsObj.DbConnections['samlDb'] sp_settings = dbobj.makeQuery("EXECUTE get_sp_settings(%s)", [cod_sp], type=dbobj.stmts['get_providers']['pool'], close=close) if sp_settings['error'] == 0 and sp_settings['result'] != None: # genera risposta tutto ok sp_settings['result']['settings']['idp'] = globalsObj.easyspidSettings[ 'idp'] sp_settings['result']['settings']['sp']['x509cert'] = sp_settings[ 'result']['public_key'] sp_settings['result']['settings']['sp']['privateKey'] = sp_settings[ 'result']['private_key'] sp_settings['result']['settings']['sp'][ 'x509cert_fingerprint'] = sp_settings['result']['fingerprint'] sp_settings['result']['settings']['sp'][ 'x509cert_fingerprintalg'] = sp_settings['result'][ 'fingerprintalg'] sp_settings['result']['settings']['security'] = sp_settings['result'][ 'advanced_settings']['security'] sp_settings['result']['settings']['contactPerson'] = sp_settings[ 'result']['advanced_settings']['contactPerson'] sp_settings['result']['settings']['organization'] = sp_settings[ 'result']['advanced_settings']['organization'] if cod_idp != None: #idp_metadata = globalsObj.DbConnections['samlSlave'].get_prvd_metadta(cod_idp) #idp_metadata = conn.get_prvd_metadta(cod_idp, close) idp_metadata = dbobj.makeQuery( "EXECUTE get_prvd_metadta(%s)", [cod_idp], type=dbobj.stmts['get_providers']['pool'], close=close) if idp_metadata['error'] == 0 and idp_metadata['result'] != None: metadata = idp_metadata['result']['xml'] idp_data = OneLogin_Saml2_IdPMetadataParser.parse( metadata, required_sso_binding=binding, required_slo_binding=binding) idp_settings = idp_data['idp'] if 'entityId' in idp_settings: sp_settings['result']['settings']['idp'][ 'entityId'] = idp_settings['entityId'] if 'singleLogoutService' in idp_settings: sp_settings['result']['settings']['idp'][ 'singleLogoutService'] = idp_settings[ 'singleLogoutService'] if 'singleSignOnService' in idp_settings: sp_settings['result']['settings']['idp'][ 'singleSignOnService'] = idp_settings[ 'singleSignOnService'] if 'x509cert' in idp_settings: sp_settings['result']['settings']['idp'][ 'x509cert'] = idp_settings['x509cert'] sp_settings['result']['settings']['idp'][ 'x509cert_fingerprint'] = idp_metadata['result'][ 'fingerprint'] sp_settings['result']['settings']['idp'][ 'x509cert_fingerprintalg'] = idp_metadata['result'][ 'fingerprintalg'] sp_settings['result']['settings']['idp']['metadata'] = metadata result['result'] = sp_settings['result']['settings'] return result elif idp_metadata['error'] > 0: result['error'] = 1 response_obj = ResponseObj( debugMessage=idp_metadata['result'].pgerror, httpcode=500, devMessage=("PostgreSQL error code: %s" % idp_metadata['result'].pgcode)) response_obj.setError('easyspid105') result['result'] = response_obj else: result['error'] = idp_metadata['error'] result['result'] = idp_metadata['result'] else: result['result'] = sp_settings['result']['settings'] return result elif sp_settings['error'] > 0: result['error'] = 1 response_obj = ResponseObj(debugMessage=sp_settings['result'].pgerror, httpcode=500, devMessage=("PostgreSQL error code: %s" % sp_settings['result'].pgcode)) response_obj.setError('easyspid105') result['result'] = response_obj else: result['error'] = sp_settings['error'] result['result'] = sp_settings['result'] return result
def loginAuthnReq(self, sp_settings, idp_metadata, attributeIndex, binding, srelay_cod): try: if binding == 'redirect': authn_request = authnreqBuildhandler.buildAthnReq( self, sp_settings, attributeIndex, signed=False) elif binding == 'post': authn_request = authnreqBuildhandler.buildAthnReq( self, sp_settings, attributeIndex, signed=True) bindingMap = { 'redirect': OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, 'post': OneLogin_Saml2_Constants.BINDING_HTTP_POST } if (sp_settings['error'] == 0 and sp_settings['result'] is not None and idp_metadata.error.code == '200' and authn_request.error.code == '200'): sp = sp_settings['result']['sp']['cod_sp'] idp = sp_settings['result']['idp']['cod_idp'] # get relay state task = asyncio.run_coroutine_threadsafe( self.dbobjSaml.execute_statment( "get_service(%s, '%s', '%s')" % ('True', str(srelay_cod), sp)), globalsObj.ioloop) #assert not task.done() #srelay = task.result() srelay = waitFuture(task) if srelay['error'] == 0 and srelay['result'] is None: response_obj = ResponseObj(httpcode=404) response_obj.setError('easyspid113') return response_obj elif srelay['error'] > 0: response_obj = ResponseObj( httpcode=500, debugMessage=sp_settings['result']) response_obj.setError("easyspid105") return response_obj #if (sp_settings['error'] == 0 and sp_settings['result'] is not None #and idp_metadata.error.code == '200' and authn_request.error.code == '200'): idp_data = OneLogin_Saml2_IdPMetadataParser.parse( idp_metadata.result.metadata, required_sso_binding=bindingMap[binding], required_slo_binding=bindingMap[binding]) idp_settings = idp_data['idp'] # fake authn_request req = { "http_host": "", "script_name": "", "server_port": "", "get_data": "", "post_data": "" } settings = sp_settings['result'] if 'entityId' in idp_settings: settings['idp']['entityId'] = idp_settings['entityId'] if 'singleLogoutService' in idp_settings: settings['idp']['singleLogoutService'] = idp_settings[ 'singleLogoutService'] if 'singleSignOnService' in idp_settings: settings['idp']['singleSignOnService'] = idp_settings[ 'singleSignOnService'] if 'x509cert' in idp_settings: settings['idp']['x509cert'] = idp_settings['x509cert'] auth = OneLogin_Saml2_Auth(req, sp_settings['result']) spSettings = Saml2_Settings(sp_settings['result']) sign_alg = ( spSettings.get_security_data())['signatureAlgorithm'] # build login message # redirect binding if binding == 'redirect': saml_request = OneLogin_Saml2_Utils.deflate_and_base64_encode( authn_request.result.authnrequest) parameters = {'SAMLRequest': saml_request} parameters['RelayState'] = srelay['result'][0][ 'relay_state'] auth.add_request_signature(parameters, sign_alg) redirectLocation = auth.redirect_to( auth.get_sso_url(), parameters) response_obj = ResponseObj(httpcode=200) response_obj.setError('200') response_obj.setResult(redirectTo=redirectLocation, jwt=authn_request.result.jwt) # POST binding elif binding == 'post': saml_request_signed = OneLogin_Saml2_Utils.b64encode( authn_request.result.authnrequest) relay_state = OneLogin_Saml2_Utils.b64encode( srelay['result'][0]['relay_state']) idpsso = idp_settings['singleSignOnService']['url'] try: with open( os.path.join(globalsObj.modules_basedir, globalsObj.easyspid_postFormPath), 'rb') as myfile: post_form = myfile.read().decode("utf-8").replace( '\n', '') except: with open(globalsObj.easyspid_postFormPath, 'rb') as myfile: post_form = myfile.read().decode("utf-8").replace( '\n', '') post_form = post_form.replace("%IDPSSO%", idpsso) post_form = post_form.replace("%AUTHNREQUEST%", saml_request_signed) post_form = post_form.replace("%RELAYSTATE%", relay_state) response_obj = ResponseObj(httpcode=200) response_obj.setError('200') response_obj.setResult(postTo=post_form, jwt=authn_request.result.jwt) elif sp_settings['error'] == 0 and sp_settings['result'] is None: response_obj = ResponseObj(httpcode=404) response_obj.setError('easyspid101') elif sp_settings['error'] > 0: response_obj = ResponseObj(httpcode=500, debugMessage=sp_settings['result']) response_obj.setError("easyspid105") except tornado.web.MissingArgumentError as error: response_obj = ResponseObj(debugMessage=error.log_message, httpcode=error.status_code, devMessage=error.log_message) response_obj.setError(str(error.status_code)) logging.getLogger( type(self).__module__ + "." + type(self).__qualname__).error( '%s' % error, exc_info=True) except Exception as inst: response_obj = ResponseObj(httpcode=500) response_obj.setError('500') logging.getLogger( type(self).__module__ + "." + type(self).__qualname__).error( 'Exception', exc_info=True) return response_obj
def handle_sso_command(cmd): if cmd['prefix'] not in ['dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2']: return -errno.ENOSYS, '', '' if not python_saml_imported: return -errno.EPERM, '', 'Required library not found: `python3-saml`' if cmd['prefix'] == 'dashboard sso enable saml2': try: Saml2Settings(mgr.SSO_DB.saml2.onelogin_settings) except Saml2Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': mgr.SSO_DB.protocol = '' mgr.SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if mgr.SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(mgr.SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert_path = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key_path = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert_path and not sp_private_key_path: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert_path and sp_private_key_path: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert_path != "" and sp_private_key_path != "" try: with open(sp_x_509_cert_path, 'r') as f: sp_x_509_cert = f.read() except FileNotFoundError: sp_x_509_cert = '' try: with open(sp_private_key_path, 'r') as f: sp_private_key = f.read() except FileNotFoundError: sp_private_key = '' if os.path.isfile(idp_metadata): warnings.warn( "Please prepend 'file://' to indicate a local SAML2 IdP file", DeprecationWarning) with open(idp_metadata, 'r') as f: idp_settings = Saml2Parser.parse(f.read(), entity_id=idp_entity_id) elif parse.urlparse(idp_metadata)[0] in ('http', 'https', 'file'): idp_settings = Saml2Parser.parse_remote( url=idp_metadata, validate_cert=False, entity_id=idp_entity_id) else: idp_settings = Saml2Parser.parse(idp_metadata, entity_id=idp_entity_id) url_prefix = prepare_url_prefix(mgr.get_module_option('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [ { "name": idp_username_attribute, "isRequired": True } ] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": has_sp_cert, "metadataValidUntil": '', "wantAttributeStatement": False } } settings = Saml2Parser.merge_settings(settings, idp_settings) mgr.SSO_DB.saml2.onelogin_settings = settings mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, json.dumps(mgr.SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''
def process_xml(form): # cast unicode xml to byte string so lxml won't complain when trying to # parse a xml document with a type declaration. xml = form.cleaned_data["metadata_xml"].encode("utf8") data = OneLogin_Saml2_IdPMetadataParser.parse(xml) return extract_idp_data_from_parsed_data(data)
import hashlib from app import app from certutil import * from distutils.version import StrictVersion from datetime import datetime, timedelta from threading import Thread if app.config['SAML_ENABLED']: from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.utils import OneLogin_Saml2_Utils from onelogin.saml2.settings import OneLogin_Saml2_Settings from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser idp_timestamp = datetime(1970, 1, 1) idp_data = None idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote( app.config['SAML_METADATA_URL']) if idp_data is None: print('SAML: IDP Metadata initial load failed') exit(-1) idp_timestamp = datetime.now() def get_idp_data(): global idp_data, idp_timestamp lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME']) if idp_timestamp + lifetime < datetime.now(): background_thread = Thread(target=retreive_idp_data) background_thread.start() return idp_data
def test_merge_settings(self): """ Tests the merge_settings method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings(None, {}) with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings({}, None) xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata.xml')) # Parse XML metadata. data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) # Read base settings. settings = self.loadSettingsJSON() # Merge settings from XML metadata into base settings, # let XML metadata have priority if there are conflicting # attributes. settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, data) # Generate readable JSON representation: # print("%s" % json.dumps(settings_result, indent=2).replace(r'\n', r'\\n')) expected_settings_json = """ { "custom_base_path": "../../../tests/data/customPath/", "contactPerson": { "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" }, "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" } }, "idp": { "singleSignOnService": { "url": "https://app.onelogin.com/trust/saml2/http-post/sso/383123", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "entityId": "https://app.onelogin.com/saml/metadata/383123", "singleLogoutService": { "url": "http://idp.example.com/SingleLogoutService.php" }, "x509cert": "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=" }, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "entityId": "http://stuff.com/endpoints/metadata.php", "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" } }, "security": { "wantAssertionsSigned": false, "authnRequestsSigned": false, "signMetadata": false }, "debug": false, "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } }, "strict": false } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings_result) # Commute merge operation. As the order determines which settings # dictionary has priority, here we expect a different result. settings_result2 = OneLogin_Saml2_IdPMetadataParser.merge_settings(data, settings) expected_settings2_json = """ { "debug": false, "idp": { "singleLogoutService": { "url": "http://idp.example.com/SingleLogoutService.php" }, "singleSignOnService": { "url": "http://idp.example.com/SSOService.php", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "entityId": "http://idp.example.com/", "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" }, "security": { "authnRequestsSigned": false, "wantAssertionsSigned": false, "signMetadata": false }, "contactPerson": { "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" }, "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" } }, "strict": false, "sp": { "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" }, "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "entityId": "http://stuff.com/endpoints/metadata.php", "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, "custom_base_path": "../../../tests/data/customPath/", "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } } } """ expected_settings2 = json.loads(expected_settings2_json) self.assertEqual(expected_settings2, settings_result2) # Test merging multiple certs xml_idp_metadata = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata_multi_certs.xml')) data3 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) settings_result3 = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, data3) expected_settings3_json = """ { "debug": false, "strict": false, "custom_base_path": "../../../tests/data/customPath/", "sp": { "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" }, "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "entityId": "http://stuff.com/endpoints/metadata.php", "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" }, "idp": { "singleLogoutService": { "url": "https://idp.examle.com/saml/slo", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509certMulti": { "encryption": [ "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==" ], "signing": [ "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" ] }, "entityId": "https://idp.examle.com/saml/metadata", "singleSignOnService": { "url": "https://idp.examle.com/saml/sso", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } }, "security": { "authnRequestsSigned": false, "wantAssertionsSigned": false, "signMetadata": false }, "contactPerson": { "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" }, "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" } }, "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } } } """ expected_settings3 = json.loads(expected_settings3_json) self.assertEqual(expected_settings3, settings_result3)
def test_parse_required_binding_all(self): """ Test all combinations of the `require_slo_binding` and `require_sso_binding` parameters. Note: IdP metadata contains a single logout (SLO) service and does not specify any endpoint for the POST binding. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" }, "idp": { "entityId": "urn:example:idp", "x509cert": "MIIDPDCCAiQCCQDydJgOlszqbzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEQMA4GA1UEChMHSmFua3lDbzESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE0MDMxMjE5NDYzM1oXDTI3MTExOTE5NDYzM1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEDAOBgNVBAoTB0phbmt5Q28xEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMGvJpRTTasRUSPqcbqCG+ZnTAurnu0vVpIG9lzExnh11o/BGmzu7lB+yLHcEdwrKBBmpepDBPCYxpVajvuEhZdKFx/Fdy6j5mH3rrW0Bh/zd36CoUNjbbhHyTjeM7FN2yF3u9lcyubuvOzr3B3gX66IwJlU46+wzcQVhSOlMk2tXR+fIKQExFrOuK9tbX3JIBUqItpI+HnAow509CnM134svw8PTFLkR6/CcMqnDfDK1m993PyoC1Y+N4X9XkhSmEQoAlAHPI5LHrvuujM13nvtoVYvKYoj7ScgumkpWNEvX652LfXOnKYlkB8ZybuxmFfIkzedQrbJsyOhfL03cMECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeHwzqwnzGEkxjzSD47imXaTqtYyETZow7XwBc0ZaFS50qRFJUgKTAmKS1xQBP/qHpStsROT35DUxJAE6NY1Kbq3ZbCuhGoSlY0L7VzVT5tpu4EY8+Dq/u2EjRmmhoL7UkskvIZ2n1DdERtd+YUMTeqYl9co43csZwDno/IKomeN5qaPc39IZjikJ+nUC6kPFKeu/3j9rgHNlRtocI6S1FdtFz9OZMQlpr0JbUt2T3xS/YoQJn6coDmJL5GTiiKM6cOe+Ur1VwzS1JEDbSS2TWWhzq8ojLdrotYLGd9JOsoQhElmz+tMfCFQUFLExinPAyy7YHlSiVX13QH2XTu/iQQ==", "singleSignOnService": { "url": "http://idp.example.com", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "singleLogoutService": { "url": "http://idp.example.com/logout", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ xmldoc = self.file_contents( join(self.data_path, 'metadata', 'idp_metadata2.xml')) expected_settings = json.loads(expected_settings_json) # Parse, require SLO and SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SLO and SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants. BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings1_2 = deepcopy(expected_settings) self.assertEqual(expected_settings1_2, settings1) self.assertEqual(expected_settings1_2, settings2) settings3 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings3 = deepcopy(expected_settings) del expected_settings3['idp']['singleLogoutService'] del expected_settings3['idp']['singleSignOnService'] self.assertEqual(expected_settings3, settings3) settings4 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) settings5 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings4_5 = deepcopy(expected_settings) del expected_settings4_5['idp']['singleSignOnService'] self.assertEqual(expected_settings4_5, settings4) self.assertEqual(expected_settings4_5, settings5) settings6 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants. BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) settings7 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST) expected_settings6_7 = deepcopy(expected_settings) del expected_settings6_7['idp']['singleLogoutService'] self.assertEqual(expected_settings6_7, settings6) self.assertEqual(expected_settings6_7, settings7)
def handle_sso_command(cmd): if cmd['prefix'] not in ['dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2']: return -errno.ENOSYS, '', '' if not python_saml_imported: python_saml_name = 'python3-saml' if sys.version_info >= (3, 0) else 'python-saml' return -errno.EPERM, '', 'Required library not found: `{}`'.format(python_saml_name) if cmd['prefix'] == 'dashboard sso enable saml2': try: OneLogin_Saml2_Settings(SSO_DB.saml2.onelogin_settings) except OneLogin_Saml2_Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' SSO_DB.protocol = 'saml2' SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': SSO_DB.protocol = '' SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert and not sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert and sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert != "" and sp_private_key != "" try: # pylint: disable=undefined-variable FileNotFoundError except NameError: # pylint: disable=redefined-builtin FileNotFoundError = IOError try: f = open(sp_x_509_cert, 'r') sp_x_509_cert = f.read() f.close() except FileNotFoundError: pass try: f = open(sp_private_key, 'r') sp_private_key = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote(idp_metadata, validate_cert=False, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: try: f = open(idp_metadata, 'r') idp_metadata = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse(idp_metadata, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: return -errno.EINVAL, '', 'Invalid parameter `idp_metadata`.' url_prefix = prepare_url_prefix(mgr.get_config('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [ { "name": idp_username_attribute, "isRequired": True } ] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": has_sp_cert, "metadataValidUntil": '', "wantAttributeStatement": False } } settings = OneLogin_Saml2_IdPMetadataParser.merge_settings(settings, idp_settings) SSO_DB.saml2.onelogin_settings = settings SSO_DB.protocol = 'saml2' SSO_DB.save() return 0, json.dumps(SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''
def test_merge_settings(self): """ Tests the merge_settings method of the OneLogin_Saml2_IdPMetadataParser """ with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( None, {}) with self.assertRaises(TypeError): settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( {}, None) xml_idp_metadata = self.file_contents( join(self.data_path, 'metadata', 'idp_metadata.xml')) # Parse XML metadata. data = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) # Read base settings. settings = self.loadSettingsJSON() # Merge settings from XML metadata into base settings, # let XML metadata have priority if there are conflicting # attributes. settings_result = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, data) # Generate readable JSON representation: # print("%s" % json.dumps(settings_result, indent=2) expected_settings_json = """ { "custom_base_path": "../../../tests/data/customPath/", "contactPerson": { "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" }, "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" } }, "idp": { "singleSignOnService": { "url": "https://app.onelogin.com/trust/saml2/http-post/sso/383123", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "entityId": "https://app.onelogin.com/saml/metadata/383123", "singleLogoutService": { "url": "http://idp.example.com/SingleLogoutService.php" }, "x509cert": "MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA2MDUxNzE2MjBaFw0xODA2MDUxNzE2MjBaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAse8rnep4qL2GmhH10pMQyJ2Jae+AQHyfgVjaQZ7Z0QQog5jX91vcJRSMi0XWJnUtOr6lF0dq1+yckjZ92wyLrH+7fvngNO1aV4Mjk9sTgf+iqMrae6y6fRxDt9PXrEFVjvd3vv7QTJf2FuIPy4vVP06Dt8EMkQIr8rmLmU0mTr1k2DkrdtdlCuNFTXuAu3QqfvNCRrRwfNObn9MP6JeOUdcGLJsBjGF8exfcN1SFzRF0JFr3dmOlx761zK5liD0T1sYWnDquatj/JD9fZMbKecBKni1NglH/LVd+b6aJUAr5LulERULUjLqYJRKW31u91/4Qazdo9tbvwqyFxaoUrwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFPWcXvQSlTXnzZD2xziuoUvrrDedMIGRBgNVHSMEgYkwgYaAFPWcXvQSlTXnzZD2xziuoUvrrDedoWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB/8xe3rzqXQVxzHyAHuAuPa73ClDoL1cko0Fp8CGcqEIyj6Te9gx5z6wyfv+Lo8RFvBLlnB1lXqbC+fTGcVgG/4oKLJ5UwRFxInqpZPnOAudVNnd0PYOODn9FWs6u+OTIQIaIcPUv3MhB9lwHIJsTk/bs9xcru5TPyLIxLLd6ib/pRceKH2mTkzUd0DYk9CQNXXeoGx/du5B9nh3ClPTbVakRzl3oswgI5MQIphYxkW70SopEh4kOFSRE1ND31NNIq1YrXlgtkguQBFsZWuQOPR6cEwFZzP0tHTYbI839WgxX6hfhIUTUz6mLqq4+3P4BG3+1OXeVDg63y8Uh781sE=" }, "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", "entityId": "http://stuff.com/endpoints/metadata.php", "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" } }, "security": { "wantAssertionsSigned": false, "authnRequestsSigned": false, "signMetadata": false }, "debug": false, "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } }, "strict": false } """ expected_settings = json.loads(expected_settings_json) self.assertEqual(expected_settings, settings_result) # Commute merge operation. As the order determines which settings # dictionary has priority, here we expect a different result. settings_result2 = OneLogin_Saml2_IdPMetadataParser.merge_settings( data, settings) expected_settings2_json = """ { "debug": false, "idp": { "singleLogoutService": { "url": "http://idp.example.com/SingleLogoutService.php" }, "singleSignOnService": { "url": "http://idp.example.com/SSOService.php", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "entityId": "http://idp.example.com/", "x509cert": "MIICgTCCAeoCCQCbOlrWDdX7FTANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCTk8xGDAWBgNVBAgTD0FuZHJlYXMgU29sYmVyZzEMMAoGA1UEBxMDRm9vMRAwDgYDVQQKEwdVTklORVRUMRgwFgYDVQQDEw9mZWlkZS5lcmxhbmcubm8xITAfBgkqhkiG9w0BCQEWEmFuZHJlYXNAdW5pbmV0dC5ubzAeFw0wNzA2MTUxMjAxMzVaFw0wNzA4MTQxMjAxMzVaMIGEMQswCQYDVQQGEwJOTzEYMBYGA1UECBMPQW5kcmVhcyBTb2xiZXJnMQwwCgYDVQQHEwNGb28xEDAOBgNVBAoTB1VOSU5FVFQxGDAWBgNVBAMTD2ZlaWRlLmVybGFuZy5ubzEhMB8GCSqGSIb3DQEJARYSYW5kcmVhc0B1bmluZXR0Lm5vMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDivbhR7P516x/S3BqKxupQe0LONoliupiBOesCO3SHbDrl3+q9IbfnfmE04rNuMcPsIxB161TdDpIesLCn7c8aPHISKOtPlAeTZSnb8QAu7aRjZq3+PbrP5uW3TcfCGPtKTytHOge/OlJbo078dVhXQ14d1EDwXJW1rRXuUt4C8QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBACDVfp86HObqY+e8BUoWQ9+VMQx1ASDohBjwOsg2WykUqRXF+dLfcUH9dWR63CtZIKFDbStNomPnQz7nbK+onygwBspVEbnHuUihZq3ZUdmumQqCw4Uvs/1Uvq3orOo/WJVhTyvLgFVK2QarQ4/67OZfHd7R+POBXhophSMv1ZOo" }, "security": { "authnRequestsSigned": false, "wantAssertionsSigned": false, "signMetadata": false }, "contactPerson": { "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" }, "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" } }, "strict": false, "sp": { "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" }, "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "entityId": "http://stuff.com/endpoints/metadata.php", "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" }, "custom_base_path": "../../../tests/data/customPath/", "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } } } """ expected_settings2 = json.loads(expected_settings2_json) self.assertEqual(expected_settings2, settings_result2) # Test merging multiple certs xml_idp_metadata = self.file_contents( join(self.data_path, 'metadata', 'idp_metadata_multi_certs.xml')) data3 = OneLogin_Saml2_IdPMetadataParser.parse(xml_idp_metadata) settings_result3 = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, data3) expected_settings3_json = """ { "debug": false, "strict": false, "custom_base_path": "../../../tests/data/customPath/", "sp": { "singleLogoutService": { "url": "http://stuff.com/endpoints/endpoints/sls.php" }, "assertionConsumerService": { "url": "http://stuff.com/endpoints/endpoints/acs.php" }, "entityId": "http://stuff.com/endpoints/metadata.php", "NameIDFormat": "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" }, "idp": { "singleLogoutService": { "url": "https://idp.examle.com/saml/slo", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "x509certMulti": { "encryption": [ "MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEFBQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJjaWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwWT25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUyMjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRwtnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xxVRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCBpIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaCFD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXMGI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65chjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIBvlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZWQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==" ], "signing": [ "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" ] }, "entityId": "https://idp.examle.com/saml/metadata", "singleSignOnService": { "url": "https://idp.examle.com/saml/sso", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } }, "security": { "authnRequestsSigned": false, "wantAssertionsSigned": false, "signMetadata": false }, "contactPerson": { "technical": { "emailAddress": "*****@*****.**", "givenName": "technical_name" }, "support": { "emailAddress": "*****@*****.**", "givenName": "support_name" } }, "organization": { "en-US": { "displayname": "SP test", "url": "http://sp.example.com", "name": "sp_test" } } } """ expected_settings3 = json.loads(expected_settings3_json) self.assertEqual(expected_settings3, settings_result3)
def test_parse_required_binding_all(self): """ Test all combinations of the `require_slo_binding` and `require_sso_binding` parameters. Note: IdP metadata contains a single logout (SLO) service and does not specify any endpoint for the POST binding. """ expected_settings_json = """ { "sp": { "NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" }, "idp": { "entityId": "urn:example:idp", "x509certs": ["MIIDPDCCAiQCCQDydJgOlszqbzANBgkqhkiG9w0BAQUFADBgMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEQMA4GA1UEChMHSmFua3lDbzESMBAGA1UEAxMJbG9jYWxob3N0MB4XDTE0MDMxMjE5NDYzM1oXDTI3MTExOTE5NDYzM1owYDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEDAOBgNVBAoTB0phbmt5Q28xEjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMGvJpRTTasRUSPqcbqCG+ZnTAurnu0vVpIG9lzExnh11o/BGmzu7lB+yLHcEdwrKBBmpepDBPCYxpVajvuEhZdKFx/Fdy6j5mH3rrW0Bh/zd36CoUNjbbhHyTjeM7FN2yF3u9lcyubuvOzr3B3gX66IwJlU46+wzcQVhSOlMk2tXR+fIKQExFrOuK9tbX3JIBUqItpI+HnAow509CnM134svw8PTFLkR6/CcMqnDfDK1m993PyoC1Y+N4X9XkhSmEQoAlAHPI5LHrvuujM13nvtoVYvKYoj7ScgumkpWNEvX652LfXOnKYlkB8ZybuxmFfIkzedQrbJsyOhfL03cMECAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAeHwzqwnzGEkxjzSD47imXaTqtYyETZow7XwBc0ZaFS50qRFJUgKTAmKS1xQBP/qHpStsROT35DUxJAE6NY1Kbq3ZbCuhGoSlY0L7VzVT5tpu4EY8+Dq/u2EjRmmhoL7UkskvIZ2n1DdERtd+YUMTeqYl9co43csZwDno/IKomeN5qaPc39IZjikJ+nUC6kPFKeu/3j9rgHNlRtocI6S1FdtFz9OZMQlpr0JbUt2T3xS/YoQJn6coDmJL5GTiiKM6cOe+Ur1VwzS1JEDbSS2TWWhzq8ojLdrotYLGd9JOsoQhElmz+tMfCFQUFLExinPAyy7YHlSiVX13QH2XTu/iQQ=="], "singleSignOnService": { "url": "http://idp.example.com", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" }, "singleLogoutService": { "url": "http://idp.example.com/logout", "binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" } } } """ xmldoc = self.file_contents(join(self.data_path, 'metadata', 'idp_metadata2.xml')) expected_settings = json.loads(expected_settings_json) # Parse, require SLO and SSO REDIRECT binding, implicitly. settings1 = OneLogin_Saml2_IdPMetadataParser.parse(xmldoc) # Parse, require SLO and SSO REDIRECT binding, explicitly. settings2 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) expected_settings1_2 = deepcopy(expected_settings) self.assertEqual(expected_settings1_2, settings1) self.assertEqual(expected_settings1_2, settings2) settings3 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST ) expected_settings3 = deepcopy(expected_settings) del expected_settings3['idp']['singleLogoutService'] del expected_settings3['idp']['singleSignOnService'] self.assertEqual(expected_settings3, settings3) settings4 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT ) settings5 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST ) expected_settings4_5 = deepcopy(expected_settings) del expected_settings4_5['idp']['singleSignOnService'] self.assertEqual(expected_settings4_5, settings4) self.assertEqual(expected_settings4_5, settings5) settings6 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_sso_binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST ) settings7 = OneLogin_Saml2_IdPMetadataParser.parse( xmldoc, required_slo_binding=OneLogin_Saml2_Constants.BINDING_HTTP_POST ) expected_settings6_7 = deepcopy(expected_settings) del expected_settings6_7['idp']['singleLogoutService'] self.assertEqual(expected_settings6_7, settings6) self.assertEqual(expected_settings6_7, settings7)
def process_url(form): url = form.cleaned_data["metadata_url"] response = safe_urlopen(url) data = OneLogin_Saml2_IdPMetadataParser.parse(response.content) return extract_idp_data_from_parsed_data(data)
async def spSettings(cod_sp, cod_idp=None, binding=OneLogin_Saml2_Constants.BINDING_HTTP_REDIRECT, close=True): result = {'error': 0, 'result': None} dbobj = globalsObj.DbConnections['samlDb'] task1 = asyncio.ensure_future(dbobj.execute_statment( "get_sp_settings('%s')" % cod_sp), loop=globalsObj.ioloop) if cod_idp is not None: task2 = asyncio.ensure_future(dbobj.execute_statment( "get_prvd_metadta('%s')" % cod_idp), loop=globalsObj.ioloop) await task2 idp_metadata = task2.result() await task1 sp_settings = task1.result() if sp_settings['error'] == 0 and sp_settings['result'] is not None: # genera risposta tutto o sp_settings['result'] = sp_settings['result'][0] sp_settings['result']['settings']['idp'] = globalsObj.easyspidSettings[ 'idp'] sp_settings['result']['settings']['sp']['cod_sp'] = cod_sp sp_settings['result']['settings']['sp']['x509cert'] = sp_settings[ 'result']['public_key'] sp_settings['result']['settings']['sp']['privateKey'] = sp_settings[ 'result']['private_key'] sp_settings['result']['settings']['sp'][ 'x509cert_fingerprint'] = sp_settings['result']['fingerprint'] sp_settings['result']['settings']['sp'][ 'x509cert_fingerprintalg'] = sp_settings['result'][ 'fingerprintalg'] sp_settings['result']['settings']['security'] = sp_settings['result'][ 'advanced_settings']['security'] sp_settings['result']['settings']['contactPerson'] = sp_settings[ 'result']['advanced_settings']['contactPerson'] sp_settings['result']['settings']['organization'] = sp_settings[ 'result']['advanced_settings']['organization'] if cod_idp is not None: #idp_metadata = dbobj.makeQuery("EXECUTE get_prvd_metadta(%s)", # [cod_idp],type = dbobj.stmts['get_providers']['pool'], close=close) #idp_metadata = await dbobj.execute_statment("get_prvd_metadta('%s')" % cod_idp) if idp_metadata['error'] == 0 and idp_metadata[ 'result'] is not None: metadata = idp_metadata['result'][0]['xml'] idp_data = OneLogin_Saml2_IdPMetadataParser.parse( metadata, required_sso_binding=binding, required_slo_binding=binding) idp_settings = idp_data['idp'] if 'entityId' in idp_settings: sp_settings['result']['settings']['idp'][ 'entityId'] = idp_settings['entityId'] if 'singleLogoutService' in idp_settings: sp_settings['result']['settings']['idp'][ 'singleLogoutService'] = idp_settings[ 'singleLogoutService'] if 'singleSignOnService' in idp_settings: sp_settings['result']['settings']['idp'][ 'singleSignOnService'] = idp_settings[ 'singleSignOnService'] if 'x509cert' in idp_settings: sp_settings['result']['settings']['idp'][ 'x509cert'] = idp_settings['x509cert'] sp_settings['result']['settings']['idp'][ 'x509cert_fingerprint'] = idp_metadata['result'][0][ 'fingerprint'] sp_settings['result']['settings']['idp'][ 'x509cert_fingerprintalg'] = idp_metadata['result'][0][ 'fingerprintalg'] sp_settings['result']['settings']['idp']['metadata'] = metadata sp_settings['result']['settings']['idp']['cod_idp'] = cod_idp result['result'] = sp_settings['result']['settings'] return result elif idp_metadata['error'] > 0: result['error'] = 1 sqlstate = "?" if hasattr(idp_metadata['result'], 'sqlstate'): sqlstate = idp_metadata['result'].sqlstate response_obj = ResponseObj( debugMessage="PostgreSQL error code: %s" % sqlstate, httpcode=500, devMessage=("PostgreSQL error message: %s" % idp_metadata['result'].message)) response_obj.setError('easyspid105') result['result'] = response_obj else: result['error'] = idp_metadata['error'] result['result'] = idp_metadata['result'] else: result['result'] = sp_settings['result']['settings'] return result elif sp_settings['error'] > 0: result['error'] = 1 sqlstate = "?" if hasattr(sp_settings['result'], 'sqlstate'): sqlstate = sp_settings['result'].sqlstate response_obj = ResponseObj(debugMessage="PostgreSQL error code: %s" % sqlstate, httpcode=500, devMessage=("PostgreSQL error message: %s" % sp_settings['result'].message)) response_obj.setError('easyspid105') result['result'] = response_obj else: result['error'] = sp_settings['error'] result['result'] = sp_settings['result'] return result
from app import app from distutils.version import StrictVersion from urllib.parse import urlparse from datetime import datetime, timedelta from threading import Thread from .certutil import KEY_FILE, CERT_FILE if app.config['SAML_ENABLED']: from onelogin.saml2.auth import OneLogin_Saml2_Auth from onelogin.saml2.idp_metadata_parser import OneLogin_Saml2_IdPMetadataParser idp_timestamp = datetime(1970, 1, 1) idp_data = None if 'SAML_IDP_ENTITY_ID' in app.config: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None), required_sso_binding=app.config['SAML_IDP_SSO_BINDING']) else: idp_data = OneLogin_Saml2_IdPMetadataParser.parse_remote(app.config['SAML_METADATA_URL'], entity_id=app.config.get('SAML_IDP_ENTITY_ID', None)) if idp_data is None: print('SAML: IDP Metadata initial load failed') exit(-1) idp_timestamp = datetime.now() def get_idp_data(): global idp_data, idp_timestamp lifetime = timedelta(minutes=app.config['SAML_METADATA_CACHE_LIFETIME']) if idp_timestamp+lifetime < datetime.now(): background_thread = Thread(target=retrieve_idp_data) background_thread.start() return idp_data
def handle_sso_command(cmd): if cmd['prefix'] not in [ 'dashboard sso enable saml2', 'dashboard sso disable', 'dashboard sso status', 'dashboard sso show saml2', 'dashboard sso setup saml2' ]: return -errno.ENOSYS, '', '' if not python_saml_imported: python_saml_name = 'python3-saml' if sys.version_info >= ( 3, 0) else 'python-saml' return -errno.EPERM, '', 'Required library not found: `{}`'.format( python_saml_name) if cmd['prefix'] == 'dashboard sso enable saml2': try: OneLogin_Saml2_Settings(mgr.SSO_DB.saml2.onelogin_settings) except OneLogin_Saml2_Error: return -errno.EPERM, '', 'Single Sign-On is not configured: ' \ 'use `ceph dashboard sso setup saml2`' mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, 'SSO is "enabled" with "SAML2" protocol.', '' if cmd['prefix'] == 'dashboard sso disable': mgr.SSO_DB.protocol = '' mgr.SSO_DB.save() return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso status': if mgr.SSO_DB.protocol == 'saml2': return 0, 'SSO is "enabled" with "SAML2" protocol.', '' return 0, 'SSO is "disabled".', '' if cmd['prefix'] == 'dashboard sso show saml2': return 0, json.dumps(mgr.SSO_DB.saml2.to_dict()), '' if cmd['prefix'] == 'dashboard sso setup saml2': ceph_dashboard_base_url = cmd['ceph_dashboard_base_url'] idp_metadata = cmd['idp_metadata'] idp_username_attribute = _get_optional_attr(cmd, 'idp_username_attribute', 'uid') idp_entity_id = _get_optional_attr(cmd, 'idp_entity_id', None) sp_x_509_cert = _get_optional_attr(cmd, 'sp_x_509_cert', '') sp_private_key = _get_optional_attr(cmd, 'sp_private_key', '') if sp_x_509_cert and not sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_private_key`.' if not sp_x_509_cert and sp_private_key: return -errno.EINVAL, '', 'Missing parameter `sp_x_509_cert`.' has_sp_cert = sp_x_509_cert != "" and sp_private_key != "" try: # pylint: disable=undefined-variable FileNotFoundError except NameError: # pylint: disable=redefined-builtin FileNotFoundError = IOError try: f = open(sp_x_509_cert, 'r', encoding='utf-8') if six.PY3 else \ open(sp_x_509_cert, 'rb') sp_x_509_cert = f.read() f.close() except FileNotFoundError: pass try: f = open(sp_private_key, 'r', encoding='utf-8') if six.PY3 else \ open(sp_private_key, 'rb') sp_private_key = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse_remote( idp_metadata, validate_cert=False, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: try: f = open(idp_metadata, 'r', encoding='utf-8') if six.PY3 else \ open(idp_metadata, 'rb') idp_metadata = f.read() f.close() except FileNotFoundError: pass try: idp_settings = OneLogin_Saml2_IdPMetadataParser.parse( idp_metadata, entity_id=idp_entity_id) # pylint: disable=broad-except except Exception: return -errno.EINVAL, '', 'Invalid parameter `idp_metadata`.' url_prefix = prepare_url_prefix( mgr.get_module_option('url_prefix', default='')) settings = { 'sp': { 'entityId': '{}{}/auth/saml2/metadata'.format(ceph_dashboard_base_url, url_prefix), 'assertionConsumerService': { 'url': '{}{}/auth/saml2'.format(ceph_dashboard_base_url, url_prefix), 'binding': "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" }, 'attributeConsumingService': { 'serviceName': "Ceph Dashboard", "serviceDescription": "Ceph Dashboard Service", "requestedAttributes": [{ "name": idp_username_attribute, "isRequired": True }] }, 'singleLogoutService': { 'url': '{}{}/auth/saml2/logout'.format(ceph_dashboard_base_url, url_prefix), 'binding': 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect' }, "x509cert": sp_x_509_cert, "privateKey": sp_private_key }, 'security': { "nameIdEncrypted": has_sp_cert, "authnRequestsSigned": has_sp_cert, "logoutRequestSigned": has_sp_cert, "logoutResponseSigned": has_sp_cert, "signMetadata": has_sp_cert, "wantMessagesSigned": has_sp_cert, "wantAssertionsSigned": has_sp_cert, "wantAssertionsEncrypted": has_sp_cert, "wantNameIdEncrypted": False, # Not all Identity Providers support this. "metadataValidUntil": '', "wantAttributeStatement": False } } settings = OneLogin_Saml2_IdPMetadataParser.merge_settings( settings, idp_settings) mgr.SSO_DB.saml2.onelogin_settings = settings mgr.SSO_DB.protocol = 'saml2' mgr.SSO_DB.save() return 0, json.dumps(mgr.SSO_DB.saml2.onelogin_settings), '' return -errno.ENOSYS, '', ''