Java tutorial
/** * Copyright (c) Codice Foundation * <p> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package ddf.test.itests.platform; import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; import static org.codice.ddf.itests.common.catalog.CatalogTestCommons.deleteMetacard; import static org.codice.ddf.itests.common.catalog.CatalogTestCommons.ingest; import static org.codice.ddf.itests.common.config.ConfigureTestCommons.configureAuthZRealm; import static org.codice.ddf.itests.common.config.ConfigureTestCommons.configureMetacardAttributeSecurityFiltering; import static org.codice.ddf.itests.common.csw.CswTestCommons.CSW_FEDERATED_SOURCE_FACTORY_PID; import static org.codice.ddf.itests.common.csw.CswTestCommons.getCswSourceProperties; import static org.codice.ddf.itests.common.opensearch.OpenSearchTestCommons.OPENSEARCH_FACTORY_PID; import static org.codice.ddf.itests.common.opensearch.OpenSearchTestCommons.getOpenSearchSourceProperties; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyCollectionOf; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasXPath; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static com.jayway.restassured.RestAssured.get; import static com.jayway.restassured.RestAssured.given; import static com.jayway.restassured.RestAssured.when; import static com.jayway.restassured.authentication.CertificateAuthSettings.certAuthSettings; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.TimeZone; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLHandshakeException; import javax.ws.rs.core.MediaType; import org.apache.http.HttpStatus; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.tika.io.IOUtils; import org.codice.ddf.itests.common.AbstractIntegrationTest; import org.codice.ddf.itests.common.annotations.BeforeExam; import org.codice.ddf.itests.common.catalog.CatalogTestCommons; import org.codice.ddf.itests.common.opensearch.OpenSearchFeature; import org.codice.ddf.itests.common.utils.LoggingUtils; import org.codice.ddf.security.common.jaxrs.RestSecurity; import org.hamcrest.xml.HasXPath; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.junit.PaxExam; import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; import org.ops4j.pax.exam.spi.reactors.PerSuite; import org.osgi.service.cm.Configuration; import com.google.common.collect.ImmutableList; import com.jayway.restassured.http.ContentType; import com.jayway.restassured.path.json.JsonPath; import com.jayway.restassured.response.Response; import ddf.catalog.data.Metacard; import ddf.security.SecurityConstants; @RunWith(PaxExam.class) @ExamReactorStrategy(PerSuite.class) public class TestSecurity extends AbstractIntegrationTest { /***************** * USERS *****************/ private static final String USER_PASSWORD = "password1"; private static final String A_USER = "slang"; private static final String B_USER = "tchalla"; private static final String ACCESS_GROUP_REPLACE_TOKEN = "ACCESS_GROUP_REPLACE_TOKEN"; protected static final String TRUST_STORE_PATH = System.getProperty("javax.net.ssl.trustStore"); protected static final String KEY_STORE_PATH = System.getProperty("javax.net.ssl.keyStore"); protected static final String PASSWORD = System.getProperty("javax.net.ssl.trustStorePassword"); protected static final List<String> SERVICES_TO_FILTER = Arrays.asList( "org.codice.ddf.catalog.security.CatalogPolicy", "org.codice.ddf.security.policy.context.impl.PolicyManager", "ddf.security.pdp.realm.AuthzRealm", "testCreateFactoryPid", "org.codice.ddf.admin.config.policy.AdminConfigPolicy"); protected static final List<String> FEATURES_TO_FILTER = Arrays.asList("catalog-security-plugin", "security-sts-propertyclaimshandler", "security-all"); protected static final String ADD_SDK_APP_JOLOKIA_REQ = "{\"type\":\"EXEC\",\"mbean\":\"org.codice.ddf.admin.application.service.ApplicationService:service=application-service\",\"operation\":\"addApplications\",\"arguments\":[[{\"value\":\"mvn:ddf.distribution/sdk-app/" + System.getProperty("ddf.version") + "/xml/features\"}]]}"; protected static final String SOAP_ENV = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + " <soap:Header>\n" + " <Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</Action>\n" + " <MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">urn:uuid:c0c43e1e-0264-4018-9a58-d1fda4332ab3</MessageID>\n" + " <To xmlns=\"http://www.w3.org/2005/08/addressing\">https://localhost:8993/services/SecurityTokenService</To>\n" + " <ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\">\n" + " <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>\n" + " </ReplyTo>\n" + " <wsse:Security xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\" soap:mustUnderstand=\"1\">\n" + " <wsu:Timestamp wsu:Id=\"TS-3\">\n" + " <wsu:Created>CREATED</wsu:Created>\n" + " <wsu:Expires>EXPIRES</wsu:Expires>\n" + " </wsu:Timestamp>\n" + " </wsse:Security>\n" + " </soap:Header>\n" + " <soap:Body>\n" + " <wst:RequestSecurityToken xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\">\n" + " <wst:SecondaryParameters>\n" + " <t:TokenType xmlns:t=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\">http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>\n" + " <t:KeyType xmlns:t=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</t:KeyType>\n" + " <t:Claims xmlns:ic=\"http://schemas.xmlsoap.org/ws/2005/05/identity\" xmlns:t=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\" Dialect=\"http://schemas.xmlsoap.org/ws/2005/05/identity\">\n" + " <!--Add any additional claims you want to grab for the service-->\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"/>\n" + " </t:Claims>\n" + " </wst:SecondaryParameters>\n" + " ON_BEHALF_OF" + " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>\n" + " <wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">\n" + " <wsa:EndpointReference xmlns:wsa=\"http://www.w3.org/2005/08/addressing\">\n" + " <wsa:Address>https://localhost:8993/services/QueryService</wsa:Address>\n" + " </wsa:EndpointReference>\n" + " </wsp:AppliesTo>\n" + " <wst:Renewing/>\n" + " </wst:RequestSecurityToken>\n" + " </soap:Body>\n" + "</soap:Envelope>"; protected static final String SDK_SOAP_CONTEXT = "/services/sdk"; private static final String BAD_X509_TOKEN = " MIIDQDCCAqmgAwIBAgICAQUwDQYJKoZIhvcNAQEFBQAwTjELMAkGA1UEBhMCSlAxETAPBg\n" + " NVBAgTCEthbmFnYXdhMQwwCgYDVQQKEwNJQk0xDDAKBgNVBAsTA1RSTDEQMA4GA1UEAxMH\n" + " SW50IENBMjAeFw0wMTEwMDExMDAwMzlaFw0xMTEwMDExMDAwMzlaMFMxCzAJBgNVBAYTAk\n" + " pQMREwDwYDVQQIEwhLYW5hZ2F3YTEMMAoGA1UEChMDSUJNMQwwCgYDVQQLEwNUUkwxFTAT\n" + " BgNVBAMTDFNPQVBQcm92aWRlcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAraakNJ\n" + " 1JzkPUuvPdXRvPOOCl12nBwmqvt65dk/x+QzxxarDNwH+eWRbLyyKcrAyd0XGV+Zbvj6V3\n" + " O9DSVCZUCJttw6bbqqeYhwAP3V8s24sID77tk3gOhUTEGYxsljX2orL26SLqFJMrvnvk2F\n" + " RS2mrdkZEBUG97mD4QWcln4d0CAwEAAaOCASYwggEiMAkGA1UdEwQCMAAwCwYDVR0PBAQD\n" + " AgXgMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBg\n" + " NVHQ4EFgQUlXSsrVRfZOLGdJdjEIwTbuSTe4UwgboGA1UdIwSBsjCBr4AUvfkg1Tj5ZHLT\n" + " 29p/3M6w/tC872+hgZKkgY8wgYwxCzAJBgNVBAYTAkpQMREwDwYDVQQIEwhLYW5hZ2F3YT\n" + " EPMA0GA1UEBxMGWWFtYXRvMQwwCgYDVQQKEwNJQk0xDDAKBgNVBAsTA1RSTDEZMBcGA1UE\n" + " AxMQU09BUCAyLjEgVGVzdCBDQTEiMCAGCSqGSIb3DQEJARYTbWFydXlhbWFAanAuaWJtLm\n" + " NvbYICAQEwDQYJKoZIhvcNAQEFBQADgYEAXE7mE1RPb3lYAYJFzBb3VAHvkCWa/HQtCOZd\n" + " yniCHp3MJ9EbNTq+QpOHV60YE8u0+5SejCzFSOHOpyBgLPjWoz8JXQnjV7VcAbTglw+ZoO\n" + " SYy64rfhRdr9giSs47F4D6woPsAd2ubg/YhMaXLTSyGxPdV3VqQsutuSgDUDoqWCA="; private static final String GOOD_X509_TOKEN = "MIIC8DCCAlmgAwIBAgIJAIzc4FYrIp9pMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD\n" + "VQQGEwJVUzELMAkGA1UECBMCQVoxDDAKBgNVBAoTA0RERjEMMAoGA1UECxMDRGV2\n" + "MRkwFwYDVQQDExBEREYgRGVtbyBSb290IENBMTEwLwYJKoZIhvcNAQkBFiJlbWFp\n" + "bEFkZHJlc3M9ZGRmcm9vdGNhQGV4YW1wbGUub3JnMCAXDTE1MTIxMTE1NDMyM1oY\n" + "DzIxMTUxMTE3MTU0MzIzWjBwMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQVoxDDAK\n" + "BgNVBAoTA0RERjEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QxJDAi\n" + "BgkqhkiG9w0BCQEWFWxvY2FsaG9zdEBleGFtcGxlLm9yZzCBnzANBgkqhkiG9w0B\n" + "AQEFAAOBjQAwgYkCgYEAx4LI1lsJNmmEdB8HmDwWuAGrVFjNXuKRXD+lUaTPyDHe\n" + "XcD32zxa0DiZEB5vqfS9NH3I0E56Rbidg6IQ6r/9hOL9+sjWTPRBsQfWzZwjmcUG\n" + "61psPc9gbFRK5qltz4BLv4+SWvRMMjgxHM8+SROnjCU5FD9roJ9Ww2v+ZWAvYJ8C\n" + "AwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5l\n" + "cmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFID3lAgzIEAdGx3RHizsLcGt4Wuw\n" + "MB8GA1UdIwQYMBaAFOFUx5ffCsK/qV94XjsLK+RIF73GMA0GCSqGSIb3DQEBCwUA\n" + "A4GBACWWsi4WusO5/u1O91obGn8ctFnxVlogBQ/tDZ+neQDxy8YB2J28tztELrRH\n" + "kaGiCPT4CCKdy0hx/bG/jSM1ypJnPKrPVrCkYL3Y68pzxvrFNq5NqAFCcBOCNsDN\n" + "fvCSZ/XHvFyGHIuso5wNVxJyvTdhQ+vWbnpiX8qr6vTx2Wgw"; private static final String GOOD_X509_PATH_TOKEN = "MIIC9DCCAvAwggJZoAMCAQICCQCM3OBWKyKfaTANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkFaMQwwCgYDVQQKEwNEREYxDDAKBgNVBAsTA0RldjEZMBcGA1UEAxMQRERGIERlbW8gUm9vdCBDQTExMC8GCSqGSIb3DQEJARYiZW1haWxBZGRyZXNzPWRkZnJvb3RjYUBleGFtcGxlLm9yZzAgFw0xNTEyMTExNTQzMjNaGA8yMTE1MTExNzE1NDMyM1owcDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkFaMQwwCgYDVQQKEwNEREYxDDAKBgNVBAsTA0RldjESMBAGA1UEAxMJbG9jYWxob3N0MSQwIgYJKoZIhvcNAQkBFhVsb2NhbGhvc3RAZXhhbXBsZS5vcmcwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMeCyNZbCTZphHQfB5g8FrgBq1RYzV7ikVw/pVGkz8gx3l3A99s8WtA4mRAeb6n0vTR9yNBOekW4nYOiEOq//YTi/frI1kz0QbEH1s2cI5nFButabD3PYGxUSuapbc+AS7+Pklr0TDI4MRzPPkkTp4wlORQ/a6CfVsNr/mVgL2CfAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSA95QIMyBAHRsd0R4s7C3BreFrsDAfBgNVHSMEGDAWgBThVMeX3wrCv6lfeF47CyvkSBe9xjANBgkqhkiG9w0BAQsFAAOBgQAllrIuFrrDuf7tTvdaGxp/HLRZ8VZaIAUP7Q2fp3kA8cvGAdidvLc7RC60R5Ghogj0+AginctIcf2xv40jNcqSZzyqz1awpGC92OvKc8b6xTauTagBQnATgjbAzX7wkmf1x7xchhyLrKOcDVcScr03YUPr1m56Yl/Kq+r08dloMA=="; private static final String OPENSEARCH_SAML_SOURCE_ID = "openSearchSamlSource"; private static final DynamicUrl SECURE_ROOT_AND_PORT = new DynamicUrl(DynamicUrl.SECURE_ROOT, HTTPS_PORT); private static final DynamicUrl ADMIN_PATH = new DynamicUrl(SECURE_ROOT_AND_PORT, "/admin/index.html"); //this uses a cert that won't be sent by the TLS connection private static final String BAD_HOK_EXAMPLE = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + " <soap:Header>\n" + " <Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</Action>\n" + " <MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">urn:uuid:c0c43e1e-0264-4018-9a58-d1fda4332ab3</MessageID>\n" + " <To xmlns=\"http://www.w3.org/2005/08/addressing\">https://localhost:8993/services/SecurityTokenService</To>\n" + " <ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\">\n" + " <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>\n" + " </ReplyTo>\n" + " <wsse:Security soap:mustUnderstand=\"1\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">\n" + " <wsu:Timestamp wsu:Id=\"TS-EF182B133DACAE158E14503737766347\">\n" + " <wsu:Created>CREATED</wsu:Created>\n" + " <wsu:Expires>EXPIRES</wsu:Expires>\n" + " </wsu:Timestamp>\n" + " </wsse:Security>\n" + " </soap:Header>\n" + " <soap:Body>\n" + " <wst:RequestSecurityToken xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\">\n" + " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>\n" + " <wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">\n" + " <wsa:EndpointReference xmlns:wsa=\"http://www.w3.org/2005/08/addressing\">\n" + " <wsa:Address>https://localhost:8993/services/SecurityTokenService</wsa:Address>\n" + " </wsa:EndpointReference>\n" + " </wsp:AppliesTo>\n" + " <wst:Claims Dialect=\"http://schemas.xmlsoap.org/ws/2005/05/identity\" xmlns:ic=\"http://schemas.xmlsoap.org/ws/2005/05/identity\">\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role\"/>\n" + " </wst:Claims>\n" + " <wst:OnBehalfOf>\n" + " <wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" + " <wsse:Username>admin</wsse:Username>\n" + " <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">admin</wsse:Password>\n" + " </wsse:UsernameToken>\n" + " </wst:OnBehalfOf>\n" + " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>\n" + " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</wst:KeyType>\n" + " <wst:UseKey>\n" + " <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n" + " <ds:X509Data>\n" + " <ds:X509Certificate>MIIDcDCCAtmgAwIBAgIJAIzc4FYrIp9qMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD\n" + "VQQGEwJVUzELMAkGA1UECBMCQVoxDDAKBgNVBAoTA0RERjEMMAoGA1UECxMDRGV2\n" + "MRkwFwYDVQQDExBEREYgRGVtbyBSb290IENBMTEwLwYJKoZIhvcNAQkBFiJlbWFp\n" + "bEFkZHJlc3M9ZGRmcm9vdGNhQGV4YW1wbGUub3JnMCAXDTE1MTIxNzE3MzUzMloY\n" + "DzIxMTUxMTIzMTczNTMyWjBsMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQVoxDDAK\n" + "BgNVBAoTA0RERjEMMAoGA1UECxMDRGV2MRAwDgYDVQQDEwdleGFtcGxlMSIwIAYJ\n" + "KoZIhvcNAQkBFhNleGFtcGxlQGV4YW1wbGUub3JnMIIBIjANBgkqhkiG9w0BAQEF\n" + "AAOCAQ8AMIIBCgKCAQEAoMoUxCQOxA8INQ1NQQcd4k/pwraU+x58ymGJPWeT+SCA\n" + "OiD4xJs3qzqC4Ex9tztxUhGyAH56YYaZCtVrJxejYUPbXYRBLuU2ecw3adWJyk2f\n" + "fL+hyc4eDa640KQ8+W0dz2hI1OPSsI1KzRdaYbe8f1GcWL8TshOZ+o0fC036GOsi\n" + "szCnqXaQZbObddEMGHWMEPJzToIEUrt/+t3eAeNNF9A/jjhELJrzgaWqJNuEcC3q\n" + "gfgdeF/itjurRjIkmBDs4VkplUX+JWFPF78pyYcbLEle1dV1ZxZIZv7vFlZYjZn2\n" + "Qacf+iLQnk3m+tGCtA2Q8DKWCFl/fGtJPoIyHQsmswIDAQABo3sweTAJBgNVHRME\n" + "AjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0\n" + "ZTAdBgNVHQ4EFgQUBicdpPA//+rQjR/DJwD/beoIwREwHwYDVR0jBBgwFoAU4VTH\n" + "l98Kwr+pX3heOwsr5EgXvcYwDQYJKoZIhvcNAQELBQADgYEALUz4LJAtaGfRpEuC\n" + "VtjdpQT1E2gL0PXyBgR5jchBVzvHckectvaUh+rHbwATh1jahbk/0/0J53NMEi49\n" + "TOuYQtmHtiMvl1oBqAke1mJgDPgoGE9T3wWM4FcnA8z7LpBJeo661mchRge+vyW/\n" + "kVCd/oPtz1DRhKttYBa6LB7gswk=</ds:X509Certificate>\n" + " </ds:X509Data>\n" + " </ds:KeyInfo>\n" + " </wst:UseKey>\n" + " <wst:Renewing/>\n" + " </wst:RequestSecurityToken>\n" + " </soap:Body>\n" + "</soap:Envelope>"; //this uses the default localhost cert which will be in the TLS connection private static final String GOOD_HOK_EXAMPLE = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + " <soap:Header>\n" + " <Action xmlns=\"http://www.w3.org/2005/08/addressing\">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</Action>\n" + " <MessageID xmlns=\"http://www.w3.org/2005/08/addressing\">urn:uuid:c0c43e1e-0264-4018-9a58-d1fda4332ab3</MessageID>\n" + " <To xmlns=\"http://www.w3.org/2005/08/addressing\">https://localhost:8993/services/SecurityTokenService</To>\n" + " <ReplyTo xmlns=\"http://www.w3.org/2005/08/addressing\">\n" + " <Address>http://www.w3.org/2005/08/addressing/anonymous</Address>\n" + " </ReplyTo>\n" + " <wsse:Security soap:mustUnderstand=\"1\" xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" xmlns:wsu=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">\n" + " <wsu:Timestamp wsu:Id=\"TS-EF182B133DACAE158E14503728805225\">\n" + " <wsu:Created>CREATED</wsu:Created>\n" + " <wsu:Expires>EXPIRES</wsu:Expires>\n" + " </wsu:Timestamp>\n" + " </wsse:Security>\n" + " </soap:Header>\n" + " <soap:Body>\n" + " <wst:RequestSecurityToken xmlns:wst=\"http://docs.oasis-open.org/ws-sx/ws-trust/200512\">\n" + " <wst:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</wst:RequestType>\n" + " <wsp:AppliesTo xmlns:wsp=\"http://schemas.xmlsoap.org/ws/2004/09/policy\">\n" + " <wsa:EndpointReference xmlns:wsa=\"http://www.w3.org/2005/08/addressing\">\n" + " <wsa:Address>https://localhost:8993/services/SecurityTokenService</wsa:Address>\n" + " </wsa:EndpointReference>\n" + " </wsp:AppliesTo>\n" + " <wst:Claims Dialect=\"http://schemas.xmlsoap.org/ws/2005/05/identity\" xmlns:ic=\"http://schemas.xmlsoap.org/ws/2005/05/identity\">\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname\"/>\n" + " <ic:ClaimType Optional=\"true\" Uri=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role\"/>\n" + " </wst:Claims>\n" + " <wst:OnBehalfOf>\n" + " <wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" + " <wsse:Username>admin</wsse:Username>\n" + " <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">admin</wsse:Password>\n" + " </wsse:UsernameToken>\n" + " </wst:OnBehalfOf>\n" + " <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</wst:TokenType>\n" + " <wst:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</wst:KeyType>\n" + " <wst:UseKey>\n" + " <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\n" + " <ds:X509Data>\n" + " <ds:X509Certificate>MIIC8DCCAlmgAwIBAgIJAIzc4FYrIp9pMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD\n" + "VQQGEwJVUzELMAkGA1UECBMCQVoxDDAKBgNVBAoTA0RERjEMMAoGA1UECxMDRGV2\n" + "MRkwFwYDVQQDExBEREYgRGVtbyBSb290IENBMTEwLwYJKoZIhvcNAQkBFiJlbWFp\n" + "bEFkZHJlc3M9ZGRmcm9vdGNhQGV4YW1wbGUub3JnMCAXDTE1MTIxMTE1NDMyM1oY\n" + "DzIxMTUxMTE3MTU0MzIzWjBwMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQVoxDDAK\n" + "BgNVBAoTA0RERjEMMAoGA1UECxMDRGV2MRIwEAYDVQQDEwlsb2NhbGhvc3QxJDAi\n" + "BgkqhkiG9w0BCQEWFWxvY2FsaG9zdEBleGFtcGxlLm9yZzCBnzANBgkqhkiG9w0B\n" + "AQEFAAOBjQAwgYkCgYEAx4LI1lsJNmmEdB8HmDwWuAGrVFjNXuKRXD+lUaTPyDHe\n" + "XcD32zxa0DiZEB5vqfS9NH3I0E56Rbidg6IQ6r/9hOL9+sjWTPRBsQfWzZwjmcUG\n" + "61psPc9gbFRK5qltz4BLv4+SWvRMMjgxHM8+SROnjCU5FD9roJ9Ww2v+ZWAvYJ8C\n" + "AwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5l\n" + "cmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFID3lAgzIEAdGx3RHizsLcGt4Wuw\n" + "MB8GA1UdIwQYMBaAFOFUx5ffCsK/qV94XjsLK+RIF73GMA0GCSqGSIb3DQEBCwUA\n" + "A4GBACWWsi4WusO5/u1O91obGn8ctFnxVlogBQ/tDZ+neQDxy8YB2J28tztELrRH\n" + "kaGiCPT4CCKdy0hx/bG/jSM1ypJnPKrPVrCkYL3Y68pzxvrFNq5NqAFCcBOCNsDN\n" + "fvCSZ/XHvFyGHIuso5wNVxJyvTdhQ+vWbnpiX8qr6vTx2Wgw</ds:X509Certificate>\n" + " </ds:X509Data>\n" + " </ds:KeyInfo>\n" + " </wst:UseKey>\n" + " <wst:Renewing/>\n" + " </wst:RequestSecurityToken>\n" + " </soap:Body>\n" + "</soap:Envelope>"; public static final String SAMPLE_SOAP = "<?xml version=\"1.0\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><helloWorld xmlns=\"http://ddf.sdk/soap/hello\" /></soap:Body></soap:Envelope>"; @BeforeExam public void beforeTest() throws Exception { try { waitForSystemReady(); Configuration config = getAdminConfig() .getConfiguration("org.codice.ddf.admin.config.policy.AdminConfigPolicy"); config.setBundleLocation( "mvn:ddf.admin.core/admin-core-configpolicy/" + System.getProperty("ddf.version")); Dictionary properties = new Hashtable<>(); List<String> featurePolicies = new ArrayList<>(); featurePolicies.addAll(Arrays.asList(getDefaultRequiredApps())); featurePolicies.addAll(FEATURES_TO_FILTER); featurePolicies.replaceAll(featureName -> featureName + "=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role=admin\""); List<String> servicePolicies = new ArrayList<>(); servicePolicies.addAll(SERVICES_TO_FILTER); servicePolicies.replaceAll(serviceName -> serviceName + "=\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role=admin\""); properties.put("featurePolicies", featurePolicies.stream().toArray(String[]::new)); properties.put("servicePolicies", servicePolicies.stream().toArray(String[]::new)); config.update(properties); } catch (Exception e) { LoggingUtils.failWithThrowableStacktrace(e, "Failed in @BeforeExam: "); } } @After public void teardown() throws Exception { clearCatalog(); } @Test public void testGuestRestAccess() throws Exception { String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; configureRestForGuest(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); //test that guest works and check that we get an sso token String cookie = when().get(url).then().log().all().assertThat().statusCode(equalTo(200)).assertThat() .header("Set-Cookie", containsString("JSESSIONID")).extract().cookie("JSESSIONID"); //try again with the sso token given().cookie("JSESSIONID", cookie).when().get(url).then().log().all().assertThat() .statusCode(equalTo(200)); //try to hit an admin restricted page and see that we are unauthorized given().cookie("JSESSIONID", cookie).when().get(ADMIN_PATH.getUrl()).then().log().all().assertThat() .statusCode(equalTo(403)); } @Test public void testBasicRestAccess() throws Exception { String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; configureRestForBasic(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); //test that we get a 401 if no credentials are specified getSecurityPolicy().waitForBasicAuthReady(url); when().get(url).then().log().all().assertThat().statusCode(equalTo(401)); //try a random user and get a 401 given().auth().basic("bad", "user").when().get(url).then().log().all().assertThat() .statusCode(equalTo(401)); //try a real user and get an sso token back String cookie = given().auth().basic("admin", "admin").when().get(url).then().log().all().assertThat() .statusCode(equalTo(200)).assertThat().header("Set-Cookie", containsString("JSESSIONID")).extract() .cookie("JSESSIONID"); //try the sso token instead of basic auth given().cookie("JSESSIONID", cookie).when().get(url).then().log().all().assertThat() .statusCode(equalTo(200)); //try that admin level sso token on a restricted resource and get in... sso works! given().cookie("JSESSIONID", cookie).when().get(ADMIN_PATH.getUrl()).then().log().all().assertThat() .statusCode(equalTo(200)); configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(url); } @Test public void testTLSv11IsAllowed() throws Exception { String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; HttpClient client = createHttpClient("TLSv1.1", createBasicAuth("admin", "admin")); assertBasicAuth(client, url, 200); } @Test public void testTLSv12IsAllowed() throws Exception { String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; HttpClient client = createHttpClient("TLSv1.2", createBasicAuth("admin", "admin")); assertBasicAuth(client, url, 200); } @Test(expected = SSLHandshakeException.class) public void testTLSv1IsDisabled() throws Exception { String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; HttpClient client = createHttpClient("TLSv1"); HttpGet get = new HttpGet(url); client.execute(get); } @Test public void testAllowedCipherSuites() throws Exception { String[] supportedCipherSuites = { "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" }; List<String> systemCipherSuites = Arrays.asList(System.getProperty("https.cipherSuites").split(",")); assertThat("Missing a supported cipher suite", systemCipherSuites, equalTo(Arrays.asList(supportedCipherSuites))); // Used to filter out cipher's that don't use our current key algorithm KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType()); keystore.load(new FileInputStream(KEY_STORE_PATH), "changeit".toCharArray()); String keyAlgorithm = keystore.getKey("localhost", "changeit".toCharArray()).getAlgorithm(); String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; CredentialsProvider credentialsProvider = createBasicAuth("admin", "admin"); for (String cipher : supportedCipherSuites) { if (cipher.contains("_" + keyAlgorithm + "_")) { HttpClient client = createHttpClient("TLSv1.2", new String[] { cipher }, credentialsProvider); assertBasicAuth(client, url, 200); } } } @Test(expected = SSLHandshakeException.class) public void testDisallowedCipherSuites() throws Exception { String[] disallowedCipherSuites = new String[] { // We can't test any cipher suite with > 128 encryption. 256 requires the unlimited strength policy to be installed // "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", "TLS_RSA_WITH_AES_256_CBC_SHA256", // "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", // "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", // "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", // "TLS_RSA_WITH_AES_256_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", // "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", // "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", // "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384", // "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", // "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", "TLS_RSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", "TLS_ECDHE_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_RC4_128_SHA", "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", "TLS_ECDH_RSA_WITH_RC4_128_SHA", "TLS_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA", "SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_RC4_128_MD5", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" }; List<String> systemCipherSuites = Arrays.asList(System.getProperty("https.cipherSuites").split(",")); List<String> intersection = new ArrayList<>(Arrays.asList(disallowedCipherSuites)); intersection.retainAll(systemCipherSuites); assertThat("Supported cipher suite in disallowed ciphers", intersection, emptyCollectionOf(String.class)); String url = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=local"; CredentialsProvider credentialsProvider = createBasicAuth("admin", "admin"); HttpClient client = createHttpClient("TLSv1.2", disallowedCipherSuites, credentialsProvider); HttpGet get = new HttpGet(url); client.execute(get); } @Test public void testSamlFederatedAuth() throws Exception { configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(SERVICE_ROOT.getUrl()); String recordId = ingest(getFileContent(JSON_RECORD_RESOURCE_PATH + "/SimpleGeoJsonRecord"), "application/json"); configureRestForBasic(SDK_SOAP_CONTEXT); // Creating a new OpenSearch source with no username/password. // When an OpenSearch source attempts to authenticate without a username/password it will // use the subject in the request to create a SAML authentication token Map<String, Object> openSearchProperties = getOpenSearchSourceProperties(OPENSEARCH_SAML_SOURCE_ID, OPENSEARCH_PATH.getUrl(), getServiceManager()); getServiceManager().createManagedService(OPENSEARCH_FACTORY_PID, openSearchProperties); getCatalogBundle().waitForFederatedSource(OPENSEARCH_SAML_SOURCE_ID); String openSearchQuery = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=" + OPENSEARCH_SAML_SOURCE_ID; waitForSecurityHandlers(openSearchQuery); getSecurityPolicy().waitForBasicAuthReady(openSearchQuery); given().auth().basic("admin", "admin").when().get(openSearchQuery).then().log().all().assertThat() .statusCode(equalTo(200)).assertThat() .body(hasXPath("//metacard/string[@name='" + Metacard.TITLE + "']/value[text()='myTitle']")); configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(openSearchQuery); deleteMetacard(recordId); } @Test public void testBasicFederatedAuth() throws Exception { configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(SERVICE_ROOT.getUrl()); String recordId = ingest(getFileContent(JSON_RECORD_RESOURCE_PATH + "/SimpleGeoJsonRecord"), "application/json"); configureRestForBasic(SDK_SOAP_CONTEXT); //Positive tests Map<String, Object> openSearchProperties = getOpenSearchSourceProperties(OPENSEARCH_SOURCE_ID, OPENSEARCH_PATH.getUrl(), getServiceManager()); openSearchProperties.put("username", "admin"); openSearchProperties.put("password", "admin"); getServiceManager().createManagedService(OPENSEARCH_FACTORY_PID, openSearchProperties); Map<String, Object> cswProperties = getCswSourceProperties(CSW_SOURCE_ID, CSW_PATH.getUrl(), getServiceManager()); cswProperties.put("username", "admin"); cswProperties.put("password", "admin"); getServiceManager().createManagedService(CSW_FEDERATED_SOURCE_FACTORY_PID, cswProperties); getCatalogBundle().waitForFederatedSource(OPENSEARCH_SOURCE_ID); getCatalogBundle().waitForFederatedSource(CSW_SOURCE_ID); String openSearchQuery = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=" + OPENSEARCH_SOURCE_ID; waitForSecurityHandlers(openSearchQuery); getSecurityPolicy().waitForBasicAuthReady(openSearchQuery); given().auth().basic("admin", "admin").when().get(openSearchQuery).then().log().all().assertThat() .statusCode(equalTo(200)).assertThat() .body(hasXPath("//metacard/string[@name='" + Metacard.TITLE + "']/value[text()='myTitle']")); String cswQuery = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=" + CSW_SOURCE_ID; given().auth().basic("admin", "admin").when().get(cswQuery).then().log().all().assertThat() .statusCode(equalTo(200)).assertThat() .body(hasXPath("//metacard/string[@name='" + Metacard.TITLE + "']/value[text()='myTitle']")); //Negative tests String unavailableCswSourceId = "Unavailable Csw"; cswProperties = getCswSourceProperties(unavailableCswSourceId, CSW_PATH.getUrl(), getServiceManager()); cswProperties.put("username", "bad"); cswProperties.put("password", "auth"); getServiceManager().createManagedService(CSW_FEDERATED_SOURCE_FACTORY_PID, cswProperties); String cswQueryUnavail = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=" + unavailableCswSourceId; given().auth().basic("admin", "admin").when().get(cswQueryUnavail).then().log().all().assertThat() .statusCode(equalTo(500)); String unavailableOpenSourceId = "Unavailable OpenSearchSource"; OpenSearchFeature.createManagedService(getServiceManager(), unavailableOpenSourceId, "bad", "auth"); String unavailableOpenSearchQuery = SERVICE_ROOT.getUrl() + "/catalog/query?q=*&src=" + unavailableOpenSourceId; given().auth().basic("admin", "admin").when().get(unavailableOpenSearchQuery).then().log().all() .assertThat().statusCode(equalTo(200)).assertThat() .body(not(hasXPath("//metacard/string[@name='" + Metacard.TITLE + "']/value[text()='myTitle']"))); configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(openSearchQuery); deleteMetacard(recordId); } @Test public void testGuestSoapAccess() throws Exception { String body = "<soapenv:Envelope xmlns:hel=\"http://ddf.sdk/soap/hello\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + " <soapenv:Header>\n" + " </soapenv:Header>\n" + " <soapenv:Body>\n" + " <hel:helloWorld/>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>"; //we are only testing guest because that hits the most code, testing with an assertion would be mostly testing the same stuff that this is hitting given().log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "helloWorld").expect().statusCode(equalTo(200)).when() .post(SERVICE_ROOT.getUrl() + "/sdk/SoapTransportService").then().log().all().assertThat() .body(HasXPath.hasXPath("//*[local-name()='helloWorldResponse']/result/text()", containsString("Guest"))); } @Test public void testGuestSoapAccessHttp() throws Exception { getServiceManager().startFeature(true, "platform-http-proxy"); String body = "<soapenv:Envelope xmlns:hel=\"http://ddf.sdk/soap/hello\" xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + " <soapenv:Header>\n" + " </soapenv:Header>\n" + " <soapenv:Body>\n" + " <hel:helloWorld/>\n" + " </soapenv:Body>\n" + "</soapenv:Envelope>"; //we are only testing guest because that hits the most code, testing with an assertion would be mostly testing the same stuff that this is hitting given().log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "helloWorld").expect().statusCode(equalTo(200)).when() .post(INSECURE_SERVICE_ROOT.getUrl() + "/sdk/SoapTransportService").then().log().all().assertThat() .body(HasXPath.hasXPath("//*[local-name()='helloWorldResponse']/result/text()", containsString("Guest"))); getServiceManager().stopFeature(false, "platform-http-proxy"); } /* These STS tests are here to prove out functionality that doesn't get hit when accessing internal services. The standard UsernameToken and BinarySecurityToken elements are supported * by DDF, but not used internally. These elements need to be checked for functionality independently since going through our REST security framework won't touch these validators. */ @Test public void testUsernameTokenSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>" + " <wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" + " <wsse:Username>admin</wsse:Username>\n" + " <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">admin</wsse:Password>\n" + " </wsse:UsernameToken>\n" + " </wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all().assertThat().body(HasXPath.hasXPath("//*[local-name()='Assertion']")); } @Test public void testBadUsernameTokenSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>" + " <wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" + " <wsse:Username>admin</wsse:Username>\n" + " <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">blah</wsse:Password>\n" + " </wsse:UsernameToken>\n" + " </wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(500)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all(); } @Test public void testBadX509TokenSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>\n" + " <wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\" >\n" + BAD_X509_TOKEN + " </wsse:BinarySecurityToken>\n" + " </wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(500)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all(); } @Test public void testX509TokenSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>\n" + "<wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " + "EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" " + "ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\" >" + GOOD_X509_TOKEN + "</wsse:BinarySecurityToken>\n" + "</wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all().assertThat().body(HasXPath.hasXPath("//*[local-name()='Assertion']")); } @Test public void testX509PathSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>\n" + "<wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " + "EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" " + "ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1\" >" + GOOD_X509_PATH_TOKEN + "</wsse:BinarySecurityToken>\n" + "</wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all().assertThat().body(HasXPath.hasXPath("//*[local-name()='Assertion']")); } @Test public void testX509TokenWithPathSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>\n" + "<wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " + "EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" " + "ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3\" >\n" + GOOD_X509_PATH_TOKEN + "</wsse:BinarySecurityToken>\n" + "</wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(500)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all(); } @Test public void testX509PathWithTokenSTS() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>\n" + "<wsse:BinarySecurityToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\" " + "EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\" " + "ValueType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1\" >" + GOOD_X509_TOKEN + "</wsse:BinarySecurityToken>\n" + "</wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); given().log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(500)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then().log() .all(); } @Test public void testSamlAssertionInHeaders() throws Exception { String onBehalfOf = "<wst:OnBehalfOf>" + " <wsse:UsernameToken xmlns:wsse=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd\">\n" + " <wsse:Username>admin</wsse:Username>\n" + " <wsse:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">admin</wsse:Password>\n" + " </wsse:UsernameToken>\n" + " </wst:OnBehalfOf>\n"; String body = getSoapEnvelope(onBehalfOf); String assertionHeader = given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then() .extract().response().asString(); assertionHeader = assertionHeader.substring(assertionHeader.indexOf("<saml2:Assertion"), assertionHeader.indexOf("</saml2:Assertion>") + "</saml2:Assertion>".length()); LOGGER.trace(assertionHeader); //try that admin level assertion token on a restricted resource given().header(SecurityConstants.SAML_HEADER_NAME, "SAML " + RestSecurity.deflateAndBase64Encode(assertionHeader)).when().get(ADMIN_PATH.getUrl()) .then().log().all().assertThat().statusCode(equalTo(200)); } @Test public void testGoodHokSamlAssertionInHeaders() throws Exception { String body = getSoapEnvelope(GOOD_HOK_EXAMPLE, null); String assertionHeader = given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then() .extract().response().asString(); assertionHeader = assertionHeader.substring(assertionHeader.indexOf("<saml2:Assertion"), assertionHeader.indexOf("</saml2:Assertion>") + "</saml2:Assertion>".length()); LOGGER.trace(assertionHeader); //try that admin level assertion token on a restricted resource given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .header(SecurityConstants.SAML_HEADER_NAME, "SAML " + RestSecurity.deflateAndBase64Encode(assertionHeader)) .when().get(ADMIN_PATH.getUrl()).then().log().all().assertThat().statusCode(equalTo(200)); } @Test public void testBadHokSamlAssertionInHeaders() throws Exception { String body = getSoapEnvelope(BAD_HOK_EXAMPLE, null); String assertionHeader = given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .log().all().body(body).header("Content-Type", "text/xml; charset=utf-8") .header("SOAPAction", "http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue").expect() .statusCode(equalTo(200)).when().post(SERVICE_ROOT.getUrl() + "/SecurityTokenService").then() .extract().response().asString(); assertionHeader = assertionHeader.substring(assertionHeader.indexOf("<saml2:Assertion"), assertionHeader.indexOf("</saml2:Assertion>") + "</saml2:Assertion>".length()); LOGGER.trace(assertionHeader); //try that admin level assertion token on a restricted resource given().auth() .certificate(KEY_STORE_PATH, PASSWORD, certAuthSettings().sslSocketFactory(SSLSocketFactory.getSystemSocketFactory())) .header(SecurityConstants.SAML_HEADER_NAME, "SAML " + RestSecurity.deflateAndBase64Encode(assertionHeader)) .when().get(ADMIN_PATH.getUrl()).then().log().all().assertThat().statusCode(equalTo(401)); } private String getSoapEnvelope(String onBehalfOf) { return getSoapEnvelope(SOAP_ENV, onBehalfOf); } private String getSoapEnvelope(String body, String onBehalfOf) { Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC")); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'.500Z'"); format.setCalendar(calendar); String created = format.format(new Date(calendar.getTimeInMillis())); long now = calendar.getTimeInMillis(); now += 60000; String expires = format.format(new Date(now)); if (onBehalfOf != null) { body = body.replace("ON_BEHALF_OF", onBehalfOf); } body = body.replace("CREATED", created); body = body.replace("EXPIRES", expires); return body; } String getKeystoreFilename() { return System.getProperty("javax.net.ssl.keyStore"); } String getBackupFilename() { return getKeystoreFilename() + ".backup"; } void getBackupKeystoreFile() throws IOException { Files.copy(Paths.get(getKeystoreFilename()), Paths.get(getBackupFilename()), REPLACE_EXISTING); } void restoreKeystoreFile() throws IOException { Files.copy(Paths.get(getBackupFilename()), Paths.get(getKeystoreFilename()), REPLACE_EXISTING); } //Purpose is to make sure operations of the security certificate generator are accessible //at runtime. The actual functionality of these operations is proved in unit tests. @Test public void testCertificateGeneratorService() throws Exception { String commonName = "myCn"; String expectedValue = "CN=" + commonName; String featureName = "security-certificate"; String certGenPath = SECURE_ROOT_AND_PORT + "/admin/jolokia/exec/org.codice.ddf.security.certificate.generator.CertificateGenerator:service=certgenerator"; getBackupKeystoreFile(); try { getServiceManager().startFeature(true, featureName); //Test first operation Response response = given().auth().preemptive().basic("admin", "admin").when() .get(certGenPath + "/configureDemoCert/" + commonName); String actualValue = JsonPath.from(response.getBody().asString()).getString("value"); assertThat(actualValue, equalTo(expectedValue)); //Test second operation response = given().auth().preemptive().basic("admin", "admin").when() .get(certGenPath + "/configureDemoCertWithDefaultHostname"); String jsonString = response.getBody().asString(); JsonPath jsonPath = JsonPath.from(jsonString); //If the key value exists, the return value is well-formatted (i.e. not a stacktrace) assertThat(jsonPath.getString("value"), notNullValue()); //Make sure an invalid key would return null assertThat(jsonPath.getString("someinvalidkey"), nullValue()); } finally { restoreKeystoreFile(); getServiceManager().stopFeature(false, featureName); } } @Test public void testTransportSoapPolicy() { //verify that transport policy is observed indirectly by verifying that a security header with a timestamp was added to the response given().log().all().body(SAMPLE_SOAP).header("Content-Type", "application/json").when() .post(SERVICE_ROOT + "/sdk/SoapTransportService").then().log().all().assertThat() .statusCode(equalTo(200)).body(hasXPath("/Envelope/Header/Security/Timestamp")); } @Test public void testAsymmetricSoapPolicy() { //verify that asymmetric policy is observed indirectly by verifying that a security header with encrypted data was added to the response given().log().all().body(SAMPLE_SOAP).header("Content-Type", "application/json").when() .post(SERVICE_ROOT + "/sdk/SoapAsymmetricService").then().log().all().assertThat() .statusCode(equalTo(200)).body(hasXPath("/Envelope/Header/Security/EncryptedData")); } @Test public void testSymmetricSoapPolicy() throws Exception { //verify that symmetric policy is observed indirectly by verifying that a security header with a signature was added to the response given().log().all().body(SAMPLE_SOAP).header("Content-Type", "application/xml").when() .post(SERVICE_ROOT + "/sdk/SoapSymmetricService").then().log().all().assertThat() .statusCode(equalTo(200)).body(hasXPath("/Envelope/Header/Security/Signature/SignatureValue")); } //ConfigurationAdmin tests @Test public void testAdminConfigPolicyGetServices() { String getAllServicesResponsePermitted = sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/listServices"); for (String service : SERVICES_TO_FILTER) { assertTrue(getAllServicesResponsePermitted.contains(service)); } String getAllServicesResponseNotPermitted = sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/listServices"); //Verify there are configurations the user can see other than the restricted ones assertTrue(getAllServicesResponseNotPermitted.contains("org.codice.ddf.ui.admin.api.ConfigurationAdmin")); for (String filteredService : SERVICES_TO_FILTER) { assertFalse(getAllServicesResponseNotPermitted.contains(filteredService)); } } @Test public void testAdminConfigPolicyCreateAndModifyConfiguration() { //create config CreateFactoryConfiguration String createFactoryConfigPermittedResponse = sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/createFactoryConfiguration/testCreateFactoryPid"); assertThat(JsonPath.given(createFactoryConfigPermittedResponse).getString("value"), containsString("testCreateFactoryPid")); String createFactoryConfigNotPermittedResponse = sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/createFactoryConfiguration/testCreateFactoryPid"); assertNull(JsonPath.given(createFactoryConfigNotPermittedResponse).get("value")); //get config GetConfigurations String getConfigurationsPermitted = sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/getConfigurations/(service.pid=ddf.security.pdp.realm.AuthzRealm)"); assertThat(JsonPath.given(getConfigurationsPermitted).getString("value"), containsString("ddf.security.pdp.realm.AuthzRealm")); String getConfigurationsNotPermitted = sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.ui.admin.api.ConfigurationAdmin:service=ui,version=2.3.0/getConfigurations/(service.pid=ddf.security.pdp.realm.AuthzRealm)"); assertEquals(JsonPath.given(getConfigurationsNotPermitted).getString("value"), "[]"); } // ApplicationService tests @Test public void testAdminConfigPolicyGetAllFeaturesAndApps() { String getAllFeaturesResponseNotPermitted = sendNotPermittedRequest( "/admin/jolokia/read/org.codice.ddf.admin.application.service.ApplicationService:service=application-service"); String filteredApplications = JsonPath.given(getAllFeaturesResponseNotPermitted) .getString("value.Applications.name"); String filteredFeatures = JsonPath.given(getAllFeaturesResponseNotPermitted) .getString("value.AllFeatures.name"); for (String app : getDefaultRequiredApps()) { assertThat(filteredApplications, not(containsString(app))); } for (String feature : FEATURES_TO_FILTER) { assertThat(filteredFeatures, not(containsString(feature))); } String getAllFeaturesResponsePermitted = sendPermittedRequest( "/admin/jolokia/read/org.codice.ddf.admin.application.service.ApplicationService:service=application-service"); filteredApplications = JsonPath.given(getAllFeaturesResponsePermitted).getString("value.Applications.name"); filteredFeatures = JsonPath.given(getAllFeaturesResponsePermitted).getString("value.AllFeatures.name"); for (String app : getDefaultRequiredApps()) { assertThat(filteredApplications, containsString(app)); } for (String feature : FEATURES_TO_FILTER) { assertThat(filteredFeatures, containsString(feature)); } } @Test public void testAdminConfigPolicyConfigureApplication() { //stop sdk-app String stopApplicationNotPermitted = sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/stopApplication/sdk-app"); assertFalse(JsonPath.given(stopApplicationNotPermitted).get("value")); String stopApplicationPermitted = sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/stopApplication/sdk-app"); assertTrue(JsonPath.given(stopApplicationPermitted).get("value")); //remove sdk-app sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/removeApplication/sdk-app"); String checkApplicationRemovedResponse = sendPermittedRequest( "/admin/jolokia/read/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/Applications"); checkApplicationRemovedResponse = JsonPath.given(checkApplicationRemovedResponse).getString("value.name"); assertThat(checkApplicationRemovedResponse, containsString("sdk-app")); sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/removeApplication/sdk-app"); checkApplicationRemovedResponse = sendPermittedRequest( "/admin/jolokia/read/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/Applications"); checkApplicationRemovedResponse = JsonPath.given(checkApplicationRemovedResponse).getString("value.name"); assertThat(checkApplicationRemovedResponse, not(containsString("sdk-app"))); //add the sdk-app back given().auth().basic("admin", "admin").contentType(ContentType.JSON).body(ADD_SDK_APP_JOLOKIA_REQ) .post(SECURE_ROOT_AND_PORT + "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/addApplications") .body().print(); //start sdk-app String startApplicationNotPermitted = sendNotPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/startApplication/sdk-app"); assertFalse(JsonPath.given(startApplicationNotPermitted).get("value")); String startApplicationPermitted = sendPermittedRequest( "/admin/jolokia/exec/org.codice.ddf.admin.application.service.ApplicationService:service=application-service/startApplication/sdk-app"); assertTrue(JsonPath.given(startApplicationPermitted).get("value")); } private void waitForSecurityHandlers(String url) throws InterruptedException { long stop = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5); while (get(url).statusCode() == 503) { Thread.sleep(1000); if (System.currentTimeMillis() > stop) { fail("Failed waiting for security handlers to become available."); } } } public String sendPermittedRequest(String jolokiaEndpoint) { return given().auth().basic("admin", "admin").get(SECURE_ROOT_AND_PORT + jolokiaEndpoint).body().print(); } public String sendNotPermittedRequest(String jolokiaEndpoint) { return given().auth().basic(SYSTEM_ADMIN_USER, SYSTEM_ADMIN_USER_PASSWORD) .get(SECURE_ROOT_AND_PORT + jolokiaEndpoint).body().print(); } @Test public void testAccessGroupsGuest() throws Exception { List attr = ImmutableList .of("security.access-groups=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); Dictionary authZProperties = configureAuthZRealm(attr, getAdminConfig()); Dictionary metacardAttributeSecurityFilterProperties = null; String url = null; try { String testData = IOUtils.toString(IOUtils .toInputStream(getFileContent(XML_RECORD_RESOURCE_PATH + "/accessGroupTokenMetacard.xml"))); testData = testData.replace(ACCESS_GROUP_REPLACE_TOKEN, "guest"); String id = ingest(testData, MediaType.TEXT_XML); metacardAttributeSecurityFilterProperties = configureMetacardAttributeSecurityFiltering(attr, ImmutableList.of(""), getAdminConfig()); url = SERVICE_ROOT.getUrl() + "/catalog/query?q=" + id + "&src=local"; configureRestForGuest(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); getSecurityPolicy().waitForGuestAuthReady(url); //anon guest String response = getGuestRestResponseAsString(url); assertThat(response, containsString("Lady Liberty")); //configure for basic configureRestForBasic(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); getSecurityPolicy().waitForBasicAuthReady(url); //user with permissions gets results response = getBasicRestResponseAsString(url, A_USER, USER_PASSWORD); assertThat(response, containsString("Lady Liberty")); //user without permissions gets results response = getBasicRestResponseAsString(url, B_USER, USER_PASSWORD); assertThat(response, containsString("Lady Liberty")); } finally { configureAuthZRealm(authZProperties, getAdminConfig()); if (metacardAttributeSecurityFilterProperties != null) { configureMetacardAttributeSecurityFiltering(metacardAttributeSecurityFilterProperties, getAdminConfig()); } configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(url); //metacard will be deleted in @After } } @Test public void testAccessGroups() throws Exception { List attr = ImmutableList .of("security.access-groups=http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role"); Dictionary authZProperties = configureAuthZRealm(attr, getAdminConfig()); Dictionary metacardAttributeSecurityFilterProperties = null; String url = null; try { String testData = IOUtils.toString(IOUtils .toInputStream(getFileContent(XML_RECORD_RESOURCE_PATH + "/accessGroupTokenMetacard.xml"))); testData = testData.replace(ACCESS_GROUP_REPLACE_TOKEN, "B"); String id = CatalogTestCommons.ingest(testData, MediaType.TEXT_XML); metacardAttributeSecurityFilterProperties = configureMetacardAttributeSecurityFiltering(attr, ImmutableList.of(""), getAdminConfig()); url = SERVICE_ROOT.getUrl() + "/catalog/query?q=" + id + "&src=local"; configureRestForBasic(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); getSecurityPolicy().waitForBasicAuthReady(url); //user without permissions cannot get results String response = getBasicRestResponseAsString(url, A_USER, USER_PASSWORD); assertThat(response, not(containsString("Lady Liberty"))); //user with permissions gets results response = getBasicRestResponseAsString(url, B_USER, USER_PASSWORD); assertThat(response, containsString("Lady Liberty")); //configure for guest configureRestForGuest(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); getSecurityPolicy().waitForGuestAuthReady(url); //anon guest cannot get results response = getGuestRestResponseAsString(url); assertThat(response, not(containsString("Lady Liberty"))); } finally { configureAuthZRealm(authZProperties, getAdminConfig()); if (metacardAttributeSecurityFilterProperties != null) { configureMetacardAttributeSecurityFiltering(metacardAttributeSecurityFilterProperties, getAdminConfig()); } configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(url); //metacard will be deleted in @After } } private String getBasicRestResponseAsString(String url, String user, String pass) { return given().auth().basic(user, pass).when().get(url).then().log().all().assertThat() .statusCode(equalTo(HttpStatus.SC_OK)).extract().response().print(); } private String getGuestRestResponseAsString(String url) { return given().when().get(url).then().log().all().assertThat().statusCode(equalTo(HttpStatus.SC_OK)) .extract().response().print(); } private CredentialsProvider createBasicAuth(String username, String password) { CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials(username, password); credentialsProvider.setCredentials(AuthScope.ANY, usernamePasswordCredentials); return credentialsProvider; } private HttpClient createHttpClient(String protocol) throws KeyManagementException, NoSuchAlgorithmException { return createHttpClient(protocol, new BasicCredentialsProvider()); } private HttpClient createHttpClient(String protocol, CredentialsProvider credentialsProvider) throws KeyManagementException, NoSuchAlgorithmException { return createHttpClient(protocol, null, credentialsProvider); } private HttpClient createHttpClient(String protocol, String[] cipherSuites, CredentialsProvider credentialsProvider) throws KeyManagementException, NoSuchAlgorithmException { SSLContext context = SSLContexts.custom().useProtocol(protocol).build(); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(context, null, cipherSuites, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); return HttpClients.custom().setDefaultCredentialsProvider(credentialsProvider) .setSSLSocketFactory(socketFactory).build(); } private void assertBasicAuth(HttpClient client, String url, int statusCode) throws Exception { configureRestForBasic(SDK_SOAP_CONTEXT); waitForSecurityHandlers(url); try { HttpGet get = new HttpGet(url); int result = client.execute(get).getStatusLine().getStatusCode(); assertThat(result, equalTo(statusCode)); } finally { configureRestForGuest(SDK_SOAP_CONTEXT); getSecurityPolicy().waitForGuestAuthReady(url); } } }