/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import com.emc.storageos.services.util.EnvConfig; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.emc.storageos.model.RestLinkRep; import com.emc.storageos.model.auth.AuthnCreateParam; import com.emc.storageos.model.auth.AuthnProviderRestRep; import com.emc.storageos.svcs.errorhandling.resources.ServiceCode; import com.emc.vipr.model.keystore.TrustedCertificateChanges; import com.emc.vipr.model.keystore.TrustedCertificates; import com.emc.vipr.model.keystore.TruststoreSettings; import com.emc.vipr.model.keystore.TruststoreSettingsChanges; import com.sun.jersey.api.client.ClientResponse; /** * Tests truststore functionality */ public class TrustStoreTest extends ApiTestBase { private final static String TRUSTED_CERTIFICATE = "-----BEGIN CERTIFICATE-----\r\n" + "MIIE/zCCA+egAwIBAgIRAJ9si9NLc1lAY+R202n9/fowDQYJKoZIhvcNAQEFBQAw\r\n" + "gZcxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQH\r\n" + "EwdCZWRmb3JkMRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgTExDMSUwIwYDVQQLExxH\r\n" + "bG9iYWwgU2VjdXJpdHkgT3JnYW5pemF0aW9uMRwwGgYDVQQDExNSU0EgQ29ycG9y\r\n" + "YXRlIENBIHYyMB4XDTExMDMxMDIxNDA1N1oXDTE5MDIyODIxNTYzM1owgZ4xCzAJ\r\n" + "BgNVBAYTAlVTMRYwFAYDVQQIEw1NYXNzYWNodXNldHRzMRAwDgYDVQQHEwdCZWRm\r\n" + "b3JkMRkwFwYDVQQKExBSU0EgU2VjdXJpdHkgTExDMSUwIwYDVQQLExxHbG9iYWwg\r\n" + "U2VjdXJpdHkgT3JnYW5pemF0aW9uMSMwIQYDVQQDExpSU0EgQ29ycG9yYXRlIFNl\r\n" + "cnZlciBDQSB2MjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMlEfyTA\r\n" + "hnX8JlErtRFUAIougscUT91SFwxYsDoqjuw1jOQPASUPcJDq4Axjje8kHwSlcpeB\r\n" + "23lehX+yutvWBXKRsr4Exu2ObkSYkrli2dpgl+LpLVAEnZaOikZLjHzXIeH6O79u\r\n" + "UsB0JZbvQ9B3X5q2IFrjLiB55Mc1IBNJY/Ebr4OU/HkvxB3GWmqeHL9uH2yC15CE\r\n" + "5iM+Za83+nuGulthVguBSeQWyAodvAKW5BE9W4XoYpMYuIzL5haiOz0fvgf2PbGo\r\n" + "44EVhrN1sxyi9qGEslRy4poXGXD3WQltVbOk6QlssKBTG9wOcVIiXO0t6RyuzXIn\r\n" + "sGX8pV3csrJdsDECAwEAAaOCATswggE3MA8GA1UdEwQIMAYBAf8CAQIwgZEGA1Ud\r\n" + "IASBiTCBhjCBgwYJKoZIhvcNBQcCMHYwLgYIKwYBBQUHAgEWImh0dHA6Ly9jYS5y\r\n" + "c2FzZWN1cml0eS5jb20vQ1BTLmh0bWwwRAYIKwYBBQUHAgIwODAXFhBSU0EgU2Vj\r\n" + "dXJpdHkgTExDMAMCAQEaHUNQUyBJbmNvcnBvcmF0ZWQgYnkgcmVmZXJlbmNlMEAG\r\n" + "A1UdHwQ5MDcwNaAzoDGGL2h0dHA6Ly9jcmwucnNhc2VjdXJpdHkuY29tL1JTQUNv\r\n" + "cnBvcmF0ZUNBdjIuY3JsMA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUKfPCY9Px\r\n" + "9Qulv7Jd32EQlDTPRwwwHwYDVR0jBBgwFoAUcxs4SyXLWo69AuzfXSn2EHQO2Jgw\r\n" + "DQYJKoZIhvcNAQEFBQADggEBAB7jJkSi8fSAIWG9bqsNzC0/6F3Vsism5BizSxtU\r\n" + "X8nTRHaCzYOLY2PnjieySxqVOofsCKrnGQpIeax2Vre8UHvIhU9fhzj2+n4LbmfJ\r\n" + "GcWCGk75CKTn/tWc8jemllyT/5pSQOtt+Qw6LJ6+sprJtnQ7st/e+PzG8MkLjNVl\r\n" + "U7WIrxCns2ZEbqHO/easHZ3rMu3jG4RfNa44r6zrU58TPQ3y3Tnwbo3vRrOvVOTG\r\n" + "2zJiPPbNMuFlAKmc2TYhODc0aDFUtdeskbc/SKcb5PvlQesG8J2PkktKAhoTxeFj\r\n" + "pvsXSNCQ5DpPyB/uGozgI8tgoNjDm11O57DCxZFQ6qPsIwI=\r\n" + "-----END CERTIFICATE-----"; private final static String CERTIFICATE = "-----BEGIN CERTIFICATE-----\r\n" + "MIICZDCCAc2gAwIBAgIJAKeFkwH41qufMA0GCSqGSIb3DQEBBQUAMB8xHTAbBgNV\r\n" + "BAMTFGxnbHc4MDk1Lmxzcy5lbWMuY29tMCAXDTcwMDEwMTAwMDAwMFoYDzIwNzAw\r\n" + "MTAxMDAwMDAwWjAfMR0wGwYDVQQDExRsZ2x3ODA5NS5sc3MuZW1jLmNvbTCBnzAN\r\n" + "BgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAyWHdcMQfO6P7avtTGXSM73iDWF4zw1Jo\r\n" + "I0+WQmZWY41dh3RtcIfT6emOnGr+OXOwXL0KuH7Jk+aWVczhuVOXCUxHYzyfZzia\r\n" + "4ddYECVXpzl6hkSO96Wc4YhuLljRVfOLJMVODmz07Hq5zfJE6zUKMoEfME7Gc4sA\r\n" + "GmNs4b8rrl0CAwEAAaOBpTCBojAdBgNVHQ4EFgQUnFPfhlGJixUjlDnxDPguQr5a\r\n" + "Ux0wTwYDVR0jBEgwRoAUnFPfhlGJixUjlDnxDPguQr5aUx2hI6QhMB8xHTAbBgNV\r\n" + "BAMTFGxnbHc4MDk1Lmxzcy5lbWMuY29tggkAp4WTAfjWq58wCQYDVR0SBAIwADAl\r\n" + "BgNVHREEHjAcghRsZ2x3ODA5NS5sc3MuZW1jLmNvbYcECvdiXzANBgkqhkiG9w0B\r\n" + "AQUFAAOBgQAxd5VQA31X01gXTNSUhXTQ6y7VWox7OCGAhINhtdpLBp54CL30W8oK\r\n" + "qQnIlpEP+GB4CJYfdSa8ltNUx4yRJjzG8QiPVkJV1b88Uba+gn4/xlHLLH3PqPDX\r\n" + "TzLGkt6+Prz+1w/ZAMKAXr6KAi4I0pnduqVRJ++GPmYBhPZre5auvw==\r\n" + "-----END CERTIFICATE-----"; private final static String LDAPS_CERTIFICATE = "-----BEGIN CERTIFICATE-----\r\n" + "MIIDeTCCAmGgAwIBAgIJAOX+um16uTH4MA0GCSqGSIb3DQEBBQUAMDExCzAJBgNV\r\n" + "BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMQ0wCwYDVQQKEwRyb290MB4XDTEz\r\n" + "MDQyNTE0MjYwNVoXDTE2MDQyNDE0MjYwNVowMTELMAkGA1UEBhMCQVUxEzARBgNV\r\n" + "BAgTClNvbWUtU3RhdGUxDTALBgNVBAoTBHJvb3QwggEiMA0GCSqGSIb3DQEBAQUA\r\n" + "A4IBDwAwggEKAoIBAQDPyeboLSowXuxSW1UjpTmreT9QrEvfnEHUV3ICoXuDRK2Q\r\n" + "ERvlA4euWu3Gzbdid880NuShfwy1Lk6Ood1zeObHek4ZV0KzWqeKuEf9x5ZA6l5n\r\n" + "m/wLyemHoVNLjhwa9tdxljll3KBCiuTjKdsF6myjXPduKBkdlb4EWKzt2RwskaHx\r\n" + "OhyLU/zqZL1QEdfm317EEG88i0HzlTugfvR6v0uXjPkRMg91ruuLRZ2TA7OsWdVN\r\n" + "Wv+sbgbg6X2JSrGet6xlylmLiZWHqMw6ewuwT/wiV7mOMaE7KxmXUDsSXbt1f2kt\r\n" + "h8XwjpNHv5FXlmFxiZGfgP4AqMw40yXQ5ltb+eyfAgMBAAGjgZMwgZAwHQYDVR0O\r\n" + "BBYEFLruWD/S08ORz706k5s5tyPNJlZIMGEGA1UdIwRaMFiAFLruWD/S08ORz706\r\n" + "k5s5tyPNJlZIoTWkMzAxMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0\r\n" + "ZTENMAsGA1UEChMEcm9vdIIJAOX+um16uTH4MAwGA1UdEwQFMAMBAf8wDQYJKoZI\r\n" + "hvcNAQEFBQADggEBAH7Wcd9bjxB0I1jDB+UEa/a3c5mFU/UU+2DrysHIXt+gmiJJ\r\n" + "jI8erYcHHfJ6VENKEz73GV53HGddfMw8r1isDPJ7gBSQ0tsC8YGP1WdxWmu4dEwN\r\n" + "pYHP/GW3PWF+V4pWLIRaI7MlyuAVyGBc4hV2BqvyhCce7l2LbTyKyYrkRoED4sIO\r\n" + "H0DRWNvRHZWPrP/ryE3n3YuuHdd09LAWekqCyZLsTsQvX1OQ2JJBw/JHK9JqAmPV\r\n" + "EgbhUKTYUKDyvltW2L62hGvVD9myzbiXvu7B/3vAQUO+J3W/7UQ2vKHgAGjWTqZI\r\n" + "RKVg94/3k7lboynwu9Ec6TNQAzTaY1MClwvm/rM=\r\n" + "-----END CERTIFICATE-----"; List<RestLinkRep> resourcesToRemove; int nExistedCert = 0; private static String LDAP_SERVER1_IP = EnvConfig.get("sanity", "ldap1.ip"); @Before public void setup() throws NoSuchAlgorithmException { initLoadBalancer(true); rSys = createHttpsClient(SYSADMIN, SYSADMIN_PASS_WORD, baseUrls); rSys.path("/tenant").get(String.class); addControllerLicense(); waitForClusterToBeStable(); updateADConfig(); rRootUser2 = createHttpsClient(ROOTUSER2, AD_PASS_WORD, baseUrls); rRootUser2.path("/tenant").get(String.class); } @Test public void testTrustStore() { testDefaultSettings(); // add some resources while acceptAllCerts = true, this means that the resources // should get added successfully addResourcesShouldSucceed(); // remove the resources we just added so we can re-add them later on removeAllAddedResources(); // change truststore setting to acceptAllCerts = false changeTruststoreSettingsTest(false); // now try again to add the same resources, it should fail this time. addResourcesShouldFail(); // do general update truststore tests (with good/bad inputs)... generalTruststoreTest(); // now try again to add all resources, should be successful // need to add ldap's certs before adding auth provider addResourcesCertificates(); addResourcesShouldSucceed(); // adding this so that this test can be run multiple times removeAllAddedResources(); removeResourcesCertificate(); changeTruststoreSettingsTest(true); } /** * */ private void removeResourcesCertificate() { TrustedCertificateChanges changes = new TrustedCertificateChanges(); changes.setRemove(getResourcesCertList()); ClientResponse response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); TrustedCertificates certs = response.getEntity(TrustedCertificates.class); Assert.assertEquals(nExistedCert, certs.getTrustedCertificates().size()); waitForClusterToBeStable(); } /** * @return */ private List<String> getResourcesCertList() { List<String> certs = new ArrayList<String>(); certs.add(LDAPS_CERTIFICATE); return certs; } /** * */ private void addResourcesCertificates() { TrustedCertificateChanges changes = new TrustedCertificateChanges(); changes.setAdd(getResourcesCertList()); ClientResponse response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); TrustedCertificates certs = response.getEntity(TrustedCertificates.class); // Assert.assertEquals(changes.getAdd().size(), certs.getTrustedCertificates() // .size()); waitForClusterToBeStable(); } /** * */ public void testDefaultSettings() { // test GET with a non-privileged user -should fail ClientResponse response = rRootUser2.path("/vdc/truststore/settings").get(ClientResponse.class); Assert.assertEquals(403, response.getStatus()); // test GET with a security admin user -should succeed // and acceptAllCertificates should be true response = rSys.path("/vdc/truststore/settings").get(ClientResponse.class); Assert.assertEquals(200, response.getStatus()); TruststoreSettings settings = response.getEntity(TruststoreSettings.class); Assert.assertNotNull(settings); Assert.assertTrue(settings.isAcceptAllCertificates()); } /** * */ public void changeTruststoreSettingsTest(boolean acceptAllCerts) { ClientResponse response; TruststoreSettings settings; // change the settings to the value of acceptAllCerts TruststoreSettingsChanges settingsChanges = new TruststoreSettingsChanges(); settingsChanges.setAcceptAllCertificates(acceptAllCerts); // test PUT with a non-privileged user -should fail response = rRootUser2.path("/vdc/truststore/settings").put(ClientResponse.class, settingsChanges); Assert.assertEquals(403, response.getStatus()); // test PUT with a security admin user -should succeed response = rSys.path("/vdc/truststore/settings").put(ClientResponse.class, settingsChanges); Assert.assertEquals(200, response.getStatus()); settings = response.getEntity(TruststoreSettings.class); Assert.assertNotNull(settings); Assert.assertEquals(acceptAllCerts, settings.isAcceptAllCertificates()); // a change in the truststore setting causes a reboot. waitForClusterToBeStable(); // do another get to make sure the result is same as acceptAllCerts response = rSys.path("/vdc/truststore/settings").get(ClientResponse.class); Assert.assertEquals(200, response.getStatus()); settings = response.getEntity(TruststoreSettings.class); Assert.assertNotNull(settings); Assert.assertEquals(acceptAllCerts, settings.isAcceptAllCertificates()); } /** * */ public void generalTruststoreTest() { ClientResponse response; // test GET with a non-privileged user -should fail response = rRootUser2.path("/vdc/truststore").get(ClientResponse.class); Assert.assertEquals(403, response.getStatus()); // test GET with a security admin user -should succeed response = rSys.path("/vdc/truststore").get(ClientResponse.class); Assert.assertEquals(200, response.getStatus()); TrustedCertificates certs = response.getEntity(TrustedCertificates.class); nExistedCert = certs.getTrustedCertificates().size(); // should have ca certificates by default Assert.assertTrue(!certs.getTrustedCertificates().isEmpty()); TrustedCertificateChanges changes = new TrustedCertificateChanges(); List<String> add = new ArrayList<String>(); List<String> remove = new ArrayList<String>(); changes.setAdd(add); // test PUT with a non-privileged user -should fail response = rRootUser2.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(403, response.getStatus()); // test PUT with no changes - should succeed, and not cause a reboot response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); // test PUT with a bad format certificate in both sections, and a good certificate // that doesn't exist in the keystore - should fail String certStr = "this is a bad certificate"; String anotherCertStr = "this is another bad cert"; changes = new TrustedCertificateChanges(); changes.setAdd(add); add.add(certStr); remove.add(anotherCertStr); remove.add(TRUSTED_CERTIFICATE); changes.setRemove(remove); String expectedMessage = "Truststore update had some failures. The following certificates could not be parsed: [" + certStr + ", " + anotherCertStr + "], the following certificates in the remove section were not in the truststore: [" + TRUSTED_CERTIFICATE + "]"; response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); assertExpectedError(response, 400, ServiceCode.API_PARAMETER_INVALID, expectedMessage); waitForClusterToBeStable(); // test PUT with adding a good cert - should succeed changes = new TrustedCertificateChanges(); add = new ArrayList<String>(); add.add(CERTIFICATE); changes.setAdd(add); response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); certs = response.getEntity(TrustedCertificates.class); Assert.assertEquals(nExistedCert + 1, certs.getTrustedCertificates().size()); Assert.assertEquals(removeNewLines(CERTIFICATE), removeNewLines(certs .getTrustedCertificates().get(0).getCertString())); waitForClusterToBeStable(); // test adding the same certificate, should be successful and the trusted // certificates should be the same as before response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); certs = response.getEntity(TrustedCertificates.class); Assert.assertEquals(nExistedCert + 1, certs.getTrustedCertificates().size()); Assert.assertEquals(removeNewLines(CERTIFICATE), removeNewLines(certs .getTrustedCertificates().get(0).getCertString())); waitForClusterToBeStable(); add = new ArrayList<String>(); add.add(TRUSTED_CERTIFICATE); changes.setAdd(add); remove = new ArrayList<String>(); remove.add(CERTIFICATE); changes.setRemove(remove); // test adding and removing in the same operation should succeed, and response // should have only newly added cert response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); certs = response.getEntity(TrustedCertificates.class); Assert.assertEquals(nExistedCert + 1, certs.getTrustedCertificates().size()); // Assert.assertEquals(removeNewLines(TRUSTED_CERTIFICATE), removeNewLines(certs // .getTrustedCertificates().get(0).getCertString())); // test just remove- should succeed remove = new ArrayList<String>(); remove.add(TRUSTED_CERTIFICATE); changes.setRemove(remove); changes.setAdd(new ArrayList<String>()); waitForClusterToBeStable(); response = rSys.path("/vdc/truststore").put(ClientResponse.class, changes); Assert.assertEquals(200, response.getStatus()); certs = response.getEntity(TrustedCertificates.class); Assert.assertEquals(nExistedCert, certs.getTrustedCertificates().size()); waitForClusterToBeStable(); } /** * */ private void addResourcesShouldFail() { ClientResponse response = addLDAPSAuthProvider(); String errorMessage = "The authentication provider could not be added or modified " + "because of the following error: Connection to LDAP server [ldaps:\\" + LDAP_SERVER1_IP + "] " + "failed. Please, check the scheme, accessibility of the LDAP server and port. " + "LDAP error: simple bind failed: " + LDAP_SERVER1_IP + ":636; nested exception is " + "javax.naming.CommunicationException: simple bind failed: " + LDAP_SERVER1_IP + ":636 " + "[Root exception is javax.net.ssl.SSLHandshakeException: " + "sun.security.validator.ValidatorException: No trusted certificate found]."; assertExpectedError(response, 400, ServiceCode.API_PARAMETER_INVALID, errorMessage); } /** * */ private void removeAllAddedResources() { if (resourcesToRemove != null) { for (RestLinkRep link : resourcesToRemove) { rSys.path(link.getLinkRef().toString()).delete(); } resourcesToRemove = new ArrayList<RestLinkRep>(); } } /** * */ private void addResourcesShouldSucceed() { resourcesToRemove = new ArrayList<RestLinkRep>(); ClientResponse response = addLDAPSAuthProvider(); Assert.assertEquals(200, response.getStatus()); AuthnProviderRestRep authnResp = response.getEntity(AuthnProviderRestRep.class); Assert.assertNotNull(authnResp); resourcesToRemove.add(authnResp.getLink()); } /** * @return */ private ClientResponse addLDAPSAuthProvider() { AuthnCreateParam param = new AuthnCreateParam(); param.setLabel("ldaps apitest config"); param.setDescription("ldaps configuration created by ApiTest.java"); param.setDisable(false); param.getDomains().add("secureldap.com"); param.setManagerDn("CN=Manager,DC=root,DC=com"); param.setManagerPassword("secret"); param.setSearchBase("OU=People,DC=root,DC=com"); param.setSearchFilter("mail=%u"); param.setServerUrls(new HashSet<String>()); param.getServerUrls().add("ldaps:\\" + LDAP_SERVER1_IP); param.setMode("ldap"); return rSys.path("/vdc/admin/authnproviders").post(ClientResponse.class, param); } /** * @param chain * @return */ private String removeNewLines(String chain) { return chain.replaceAll("\n", "").replaceAll("\r", ""); } }