/** * 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.util.concurrent.TimeUnit.SECONDS; import static org.codice.ddf.itests.common.WaitCondition.expect; import static org.codice.ddf.itests.common.catalog.CatalogTestCommons.ingest; import static org.codice.ddf.itests.common.matchers.ConfigurationPropertiesEqualTo.equalToConfigurationProperties; import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import static com.jayway.restassured.RestAssured.when; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.StringWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Dictionary; import java.util.Hashtable; import java.util.List; import java.util.Vector; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; import org.apache.karaf.jaas.boot.principal.RolePrincipal; import org.codice.ddf.configuration.persistence.felix.FelixPersistenceStrategy; import org.codice.ddf.itests.common.AbstractIntegrationTest; import org.codice.ddf.itests.common.annotations.AfterExam; import org.codice.ddf.itests.common.annotations.BeforeExam; import org.codice.ddf.itests.common.callables.GetConfigurationProperties; import org.codice.ddf.itests.common.matchers.ConfigurationPropertiesEqualTo; import org.codice.ddf.itests.common.utils.LoggingUtils; import org.junit.FixMethodOrder; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; 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.framework.BundleException; import org.osgi.service.cm.ConfigurationAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jayway.restassured.path.xml.XmlPath; import com.jayway.restassured.path.xml.element.NodeChildren; import com.jayway.restassured.response.Response; /** * Note: Tests prefixed with aRunFirst NEED to run before any other tests. For this reason, we * use the @FixMethodOrder(MethodSorters.NAME_ASCENDING) annotation. */ @RunWith(PaxExam.class) @ExamReactorStrategy(PerSuite.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class TestConfiguration extends AbstractIntegrationTest { private static final Logger LOGGER = LoggerFactory.getLogger(TestConfiguration.class); private static final String EXPORT_COMMAND = "migration:export"; private static final String STATUS_COMMAND = "migration:status"; private static final String CATALOG_REMOVE_ALL_COMMAND = "catalog:removeall --force"; private static final String CATALOG_INGEST_COMMAND = "catalog:ingest -t ser"; private static final String SUCCESSFUL_IMPORT_MESSAGE = "All config files imported successfully."; private static final String FAILED_IMPORT_MESSAGE = "Failed to import file [%s]. "; private static final String INVALID_CONFIG_FILE_1 = "ddf.test.itests.platform.TestPlatform.invalid.config"; private static final String INVALID_CONFIG_FILE_2 = "ddf.test.itests.platform.TestPlatform.startup.invalid.config"; private static final String VALID_CONFIG_FILE_1 = "ddf.test.itests.platform.TestPlatform.startup.config"; private static final String CRL_PEM = "crl.pem"; private static final String CRL_ENABLED_SERVER_ENCRYPTION_PROPERTIES_FILE = "/serverencryption.properties"; private static final String CRL_ENABLED_SERVER_SIGNATURE_PROPERTIES_FILE = "/serversignature.properties"; private static final String CRL_ENABLED_ISSUER_ENCRYPTION_PROPERTIES_FILE = "/issuerencryption.properties"; private static final String CRL_ENABLED_ISSUER_SIGNATURE_PROPERTIES_FILE = "/issuersignature.properties"; private static final String SERVER_SIGNATURE_DIR = "serversignature"; private static final Path SERVER_SIGNATURE_DIR_PATH = Paths.get("etc", SERVER_SIGNATURE_DIR); private static final String ISSUER_SIGNATURE_DIR = "issuersignature"; private static final Path ISSUER_SIGNATURE_DIR_PATH = Paths.get("etc", ISSUER_SIGNATURE_DIR); private static final String ISSUER_ENCRYPTION_DIR = "issuerencryption"; private static final Path ISSUER_ENCRYPTION_DIR_PATH = Paths.get("etc", ISSUER_ENCRYPTION_DIR); private static final Path DEMO_CA_CRL_PATH = Paths.get("certs", "demoCA", "crl"); private static final String TEST_FILE = "../cat.txt"; private static final Path SYSTEM_PROPERTIES = Paths.get("etc", "system.properties"); private static final Path SYSTEM_PROPERTIES_COPY = Paths.get("etc", "system.properties.copy"); private static final Path USERS_PROPERTIES = Paths.get("etc", "users.properties"); private static final Path USERS_PROPERTIES_COPY = Paths.get("etc", "users.properties.copy"); private static final Path WS_SECURITY = Paths.get("etc", "ws-security"); private static final Path WS_SECURITY_COPY = Paths.get("etc", "ws-security-copy"); private static final Path PDP = Paths.get("etc", "pdp"); private static final Path PDP_COPY = Paths.get("etc", "pdp-copy"); private static final Path SERVER_ENCRYPTION_PROPERTIES = Paths.get("etc", "ws-security", "server", "encryption.properties"); private static final Path SERVER_ENCRYPTION_PROPERTIES_COPY = Paths.get( "server.encryption.properties.copy"); private static final Path SERVER_SIGNATURE_PROPERTIES = Paths.get("etc", "ws-security", "server", "signature.properties"); private static final Path SERVER_SIGNATURE_PROPERTIES_COPY = Paths.get( "server.signature.properties.copy"); private static final Path ISSUER_ENCRYPTION_PROPERTIES = Paths.get("etc", "ws-security", "issuer", "encryption.properties"); private static final Path ISSUER_ENCRYPTION_PROPERTIES_COPY = Paths.get( "issuer.encryption.properties.copy"); private static final Path ISSUER_SIGNATURE_PROPERTIES = Paths.get("etc", "ws-security", "issuer", "signature.properties"); private static final Path ISSUER_SIGNATURE_PROPERTIES_COPY = Paths.get( "issuer.signature.properties.copy"); private static final String KEYSTORE_PROPERTY = "javax.net.ssl.keyStore"; private static final String FILE_SEPARATOR = System.getProperty("file.separator"); private static Path symbolicLink; private static ManagedServiceConfigFile managedServiceStartupConfig = new ManagedServiceConfigFile("ddf.test.itests.platform.TestPlatform.startup"); private static ManagedServiceConfigFile managedServiceNewConfig1 = new ManagedServiceConfigFile( "ddf.test.itests.platform.TestPlatform.new.1"); private static ManagedServiceConfigFile managedServiceNewConfig2 = new ManagedServiceConfigFile( "ddf.test.itests.platform.TestPlatform.new.2"); private static ManagedServiceConfigFile managedServiceFactoryStartupConfig = new ManagedServiceFactoryConfigFile("ddf.test.itests.platform.TestPlatform.msf.1"); private static ManagedServiceConfigFile managedServiceFactoryNewConfig = new ManagedServiceFactoryConfigFile("ddf.test.itests.platform.TestPlatform.msf.2"); private static ManagedServiceConfigFile configWithNoPid = new ManagedServiceConfigFile( "ddf.test.itests.platform.TestPlatform.nopid"); private static ManagedServiceConfigFile invalidConfig = new ManagedServiceConfigFile( "ddf.test.itests.platform.TestPlatform.invalid"); private static ManagedServiceConfigFile invalidStartupConfigFile = new ManagedServiceConfigFile( "ddf.test.itests.platform.TestPlatform.startup.invalid"); private static final String FELIX_FILE_INSTALLER = "org.apache.felix.fileinstall"; @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @BeforeExam public void beforeExam() throws Exception { try { waitForSystemReady(); symbolicLink = Paths.get(ddfHome) .resolve("link"); } catch (Exception e) { LoggingUtils.failWithThrowableStacktrace(e, "Failed in @BeforeExam: "); } } @AfterExam public void afterExam() throws Exception { getServiceManager().startBundle(FELIX_FILE_INSTALLER); } public void resetInitialState() throws Exception { FileUtils.deleteQuietly(getDefaultExportDirectory().toFile()); FileUtils.deleteQuietly(new File(TEST_FILE)); FileUtils.deleteQuietly(symbolicLink.toFile()); FileUtils.cleanDirectory(getPathToProcessedDirectory().toFile()); FileUtils.cleanDirectory(getPathToFailedDirectory().toFile()); restoreBackup(SYSTEM_PROPERTIES_COPY, SYSTEM_PROPERTIES); restoreBackup(USERS_PROPERTIES_COPY, USERS_PROPERTIES); restoreBackup(WS_SECURITY_COPY, WS_SECURITY); restoreBackup(PDP_COPY, PDP); System.setProperty(KEYSTORE_PROPERTY, "etc" + File.separator + "keystores" + File.separator + "serverKeystore.jks"); disableCrls(); console.runCommand(CATALOG_REMOVE_ALL_COMMAND, new RolePrincipal("admin")); } @Test public void aRunFirstTestStartUpWithExistingManagedServiceConfigurationFile() throws Exception { managedServiceStartupConfig.assertConfigurationPropertiesSet(configAdmin); managedServiceStartupConfig.assertFileMovedToProcessedDirectory(); } @Test public void aRunFirstTestCreateNewManagedServiceConfigurationFile() throws Exception { managedServiceNewConfig1.addConfigurationFileAndWait(configAdmin); managedServiceNewConfig1.assertFileMovedToProcessedDirectory(); } @Test public void aRunFirstTestStartUpWithExistingManagedServiceFactoryConfigurationFile() throws Exception { managedServiceFactoryStartupConfig.assertConfigurationPropertiesSet(configAdmin); managedServiceFactoryStartupConfig.assertFileMovedToProcessedDirectory(); } @Test public void aRunFirstTestStartUpWithInvalidFile() { invalidStartupConfigFile.assertFileMovedToFailedDirectory(); } @Test public void testCreateNewManagedServiceFactoryConfigurationFile() throws Exception { managedServiceFactoryNewConfig.addConfigurationFileAndWait(configAdmin); managedServiceFactoryNewConfig.assertFileMovedToProcessedDirectory(); } @Test public void testConfigurationFileWithNoFactoryOrServicePid() throws IOException { configWithNoPid.addConfigurationFile(); configWithNoPid.assertFileMovedToFailedDirectory(); } @Test public void testConfigurationFileWithInvalidFormat() throws Exception { invalidConfig.addConfigurationFile(); invalidConfig.assertFileMovedToFailedDirectory(); } @Test public void testExport() throws Exception { closeFileHandlesInEtc(); resetInitialState(); console.runCommand(EXPORT_COMMAND); assertExportContents(getDefaultExportDirectory()); } @Test public void testExportToDirectory() throws Exception { closeFileHandlesInEtc(); resetInitialState(); String response = console.runCommand( EXPORT_COMMAND + " \"" + temporaryFolder.getRoot() + "\""); assertThat(String.format("Exporting current configurations to %s.", temporaryFolder.toString()), response, containsString("Successfully exported all configurations.")); assertExportContents(temporaryFolder.getRoot() .toPath()); } @Test public void testExportOnTopOfFile() throws Exception { closeFileHandlesInEtc(); resetInitialState(); File file = getDefaultExportDirectory().toFile(); file.createNewFile(); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString("Unable to create export directories.")); } @Test public void testExportOnTopOfNestedFile() throws Exception { closeFileHandlesInEtc(); resetInitialState(); File file = getDefaultExportDirectory().toFile(); file.mkdir(); File fileEtc = getDefaultExportDirectory().resolve("etc") .toFile(); fileEtc.createNewFile(); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString("Unable to create export directories.")); } /** * Tests that a saved configuration will be exported * * @throws Exception */ @Test public void testExportAfterSavingAConfiguration() throws Exception { closeFileHandlesInEtc(); resetInitialState(); managedServiceNewConfig1.addConfigurationFileAndWait(configAdmin); console.runCommand(EXPORT_COMMAND); assertThat("Saved configuration should be exported.", getPathToExportedConfig( getDefaultExportDirectory(), managedServiceNewConfig1.pid).toFile() .isFile(), is(true)); } /** * Tests that deleted configurations are not exported * * @throws Exception */ @Test public void testExportAfterDeletingAConfiguration() throws Exception { closeFileHandlesInEtc(); resetInitialState(); managedServiceNewConfig2.addConfigurationFileAndWait(configAdmin); configAdmin.getConfiguration(managedServiceNewConfig2.pid, null) .delete(); console.runCommand(EXPORT_COMMAND); assertThat("Deleted configuration should not be exported.", getPathToExportedConfig( getDefaultExportDirectory(), managedServiceNewConfig2.pid).toFile() .isFile(), is(false)); } /** * Tests that absolute path pointing outside ddfHome causes a warning * * @throws Exception */ @Test public void testExportWarningForAbsolutePathOutsideDdfHome() throws Exception { closeFileHandlesInEtc(); resetInitialState(); FileUtils.copyFile(SYSTEM_PROPERTIES.toFile(), new File(TEST_FILE)); System.setProperty(KEYSTORE_PROPERTY, ddfHome + File.separator + TEST_FILE); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString(String.format("Failed to export all configurations to %s", getDefaultExportDirectory()))); } /** * Tests that absolute path pointing inside ddfHome causes a warning * * @throws Exception */ @Test public void testExportWarningForAbsolutePathInsideDdfHome() throws Exception { closeFileHandlesInEtc(); resetInitialState(); System.setProperty(KEYSTORE_PROPERTY, ddfHome + File.separator + "etc" + File.separator + "keystores" + File.separator + "serverKeystore.jks"); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString(String.format("Failed to export all configurations to %s", getDefaultExportDirectory()))); } /** * Tests that paths containing symbolic links cause a warning * * @throws Exception */ @Test public void testExportWarningForSymbolicLinkPath() throws Exception { if (System.getProperty("os.name") .startsWith("Win")) { // can't create symlinks in windows (borrowed from Apache commonsio) return; } resetInitialState(); FileUtils.copyFile(SYSTEM_PROPERTIES.toFile(), new File(TEST_FILE)); Files.createSymbolicLink(symbolicLink, Paths.get(TEST_FILE)); System.setProperty(KEYSTORE_PROPERTY, symbolicLink.toString()); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString(String.format("Failed to export all configurations to %s", getDefaultExportDirectory()))); } /** * Tests that relative paths that point outside ddfHome causes a warning * * @throws Exception */ @Test public void testExportWarningForRelativePathOutsideDdfHome() throws Exception { closeFileHandlesInEtc(); resetInitialState(); File systemProperties = new File( ddfHome + File.separator + "etc" + File.separator + "system.properties"); File testFile = new File(TEST_FILE); FileUtils.copyFile(systemProperties, testFile); System.setProperty(KEYSTORE_PROPERTY, TEST_FILE); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString(String.format("Failed to export all configurations to %s", getDefaultExportDirectory()))); } /** * Tests that when system properties file is missing, export fails * * @throws Exception */ @Test public void testExportFailureWithoutSystemPropertiesFile() throws Exception { closeFileHandlesInEtc(); resetInitialState(); FileUtils.moveFile(SYSTEM_PROPERTIES.toFile(), SYSTEM_PROPERTIES_COPY.toFile()); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Warning should have been returned when exporting to %s.", getDefaultExportDirectory()), response, containsString("Path [etc" + FILE_SEPARATOR + "system.properties] does not exist or cannot be read; therefore, it will not be included in the export.")); } /** * Tests that when system properties file is missing, export fails * * @throws Exception */ @Test public void testExportFailureWithoutUsersPropertiesFile() throws Exception { closeFileHandlesInEtc(); resetInitialState(); FileUtils.moveFile(USERS_PROPERTIES.toFile(), USERS_PROPERTIES_COPY.toFile()); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Warning should have been returned when exporting to %s.", getDefaultExportDirectory()), response, containsString("Path [etc" + FILE_SEPARATOR + "users.properties] does not exist or cannot be read; therefore, it will not be included in the export.")); } /** * Tests that when ws-security directory is missing, export fails */ @Test public void testExportFailureWithoutWSSecurityDirectory() throws Exception { closeFileHandlesInEtc(); resetInitialState(); FileUtils.moveDirectory(WS_SECURITY.toFile(), WS_SECURITY_COPY.toFile()); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Should not have been able to export to %s.", getDefaultExportDirectory()), response, containsString("An error was encountered while executing this command.")); } /** * Tests that when CRLs are enabled, they get exported */ @Test public void testExportCrlsEnabled() throws Exception { closeFileHandlesInEtc(); resetInitialState(); enableCrls(); console.runCommand(EXPORT_COMMAND); assertExportContentsWithCrlsEnabled(getDefaultExportDirectory()); } /** * Tests that when system properties file is missing, export fails * * @throws Exception */ @Test public void testExportFailureWithoutPDPDirectory() throws Exception { closeFileHandlesInEtc(); resetInitialState(); FileUtils.moveDirectory(PDP.toFile(), PDP_COPY.toFile()); String response = console.runCommand(EXPORT_COMMAND); assertThat(String.format("Warning should have been returned when exporting to %s.", getDefaultExportDirectory()), response, containsString("Path [etc" + FILE_SEPARATOR + "pdp] does not exist or cannot be read; therefore, it will not be included in the export.")); } /** * Test that exporting twice overrides the previous files * * @throws Exception */ @Test public void testExportOverridesPreviousExport() throws Exception { closeFileHandlesInEtc(); resetInitialState(); String firstExportMessage = console.runCommand(EXPORT_COMMAND); File firstExport = getExportSubDirectory(getDefaultExportDirectory(), "system.properties").toFile(); long firstLength = firstExport.length(); FileUtils.copyFile(SYSTEM_PROPERTIES.toFile(), SYSTEM_PROPERTIES_COPY.toFile()); FileUtils.writeStringToFile(SYSTEM_PROPERTIES.toFile(), "testtesttest", true); String secondExportMessage = console.runCommand(EXPORT_COMMAND); File secondExport = getExportSubDirectory(getDefaultExportDirectory(), "system.properties").toFile(); long secondLength = secondExport.length(); assertThat("The first export failed to export", firstExportMessage, not(containsString("Failed to export all configurations"))); assertThat("The second export failed to export", secondExportMessage, not(containsString("Failed to export all configurations"))); assertThat("The second failed to modify the first export's files.", firstLength, is(not(equalTo(secondLength)))); } @Test public void testConfigStatusImportSuccessful() throws Exception { closeFileHandlesInEtc(); resetInitialState(); addConfigurationFileAndWaitForSuccessfulProcessing(VALID_CONFIG_FILE_1, getFileContentAsStream(VALID_CONFIG_FILE_1)); String output = console.runCommand(STATUS_COMMAND); assertThat(output, containsString(SUCCESSFUL_IMPORT_MESSAGE)); assertThat(Files.exists(getPathToProcessedDirectory().resolve(VALID_CONFIG_FILE_1)), is(true)); } @Test public void testConfigStatusFailedImports() throws Exception { closeFileHandlesInEtc(); resetInitialState(); addConfigurationFileAndWaitForFailedProcessing(INVALID_CONFIG_FILE_1, getFileContentAsStream(INVALID_CONFIG_FILE_1)); addConfigurationFileAndWaitForFailedProcessing(INVALID_CONFIG_FILE_2, getFileContentAsStream(INVALID_CONFIG_FILE_2)); String output = console.runCommand(STATUS_COMMAND); assertThat(output, containsString(String.format(FAILED_IMPORT_MESSAGE, INVALID_CONFIG_FILE_1))); assertThat(output, containsString(String.format(FAILED_IMPORT_MESSAGE, INVALID_CONFIG_FILE_2))); assertThat(Files.exists(getPathToFailedDirectory().resolve(INVALID_CONFIG_FILE_1)), is(true)); assertThat(Files.exists(getPathToFailedDirectory().resolve(INVALID_CONFIG_FILE_2)), is(true)); } @Test public void testConfigStatusFailedImportReimportSuccessful() throws Exception { closeFileHandlesInEtc(); resetInitialState(); InputStream is = getFileContentAsStream(VALID_CONFIG_FILE_1); InputStream invalidConfigFileAsInputStream = replaceTextInResource(is, "service.pid", "invalid"); String invalidConfigFileName = VALID_CONFIG_FILE_1; addConfigurationFileAndWaitForFailedProcessing(invalidConfigFileName, invalidConfigFileAsInputStream); String output1 = console.runCommand(STATUS_COMMAND); assertThat(output1, containsString(String.format(FAILED_IMPORT_MESSAGE, invalidConfigFileName))); assertThat(Files.exists(getPathToFailedDirectory().resolve(invalidConfigFileName)), is(true)); SECONDS.sleep(11); addConfigurationFileAndWaitForSuccessfulProcessing(VALID_CONFIG_FILE_1, getFileContentAsStream(VALID_CONFIG_FILE_1)); String output2 = console.runCommand(STATUS_COMMAND); assertThat(output2, containsString(SUCCESSFUL_IMPORT_MESSAGE)); assertThat(Files.exists(getPathToProcessedDirectory().resolve(VALID_CONFIG_FILE_1)), is(true)); } @Test public void testExportMetacards() throws Exception { closeFileHandlesInEtc(); resetInitialState(); List<String> metacardIds = ingestMetacardsForExport(); console.runCommand(EXPORT_COMMAND); assertExportCatalog(getDefaultExportDirectory().resolve("ddf.metacards")); console.runCommand(CATALOG_REMOVE_ALL_COMMAND, new RolePrincipal("admin")); console.runCommand(String.format("%s \"%s\"", CATALOG_INGEST_COMMAND, getDefaultExportDirectory().resolve("ddf.metacards")), new RolePrincipal("admin")); assertMetacardsIngested(metacardIds.size()); } /** * The felix file installer keeps open file handles on files and directories in the etc directory on Windows. * This prevents files and directories from being deleted in some of the TestConfiguration itests when running * the itests on Windows. This method stops the felix file installer which releases the file handles. */ private void closeFileHandlesInEtc() throws BundleException { getServiceManager().stopBundle(FELIX_FILE_INSTALLER); } private void assertMetacardsIngested(int expectedumberOfMetacards) throws Exception { String queryUrl = OPENSEARCH_PATH.getUrl() + "?q=*&format=xml&src=local"; Response response = when().get(queryUrl); String bodyXml = response.body() .asString(); NodeChildren metacards = new XmlPath(bodyXml).get("metacards.metacard"); assertThat(metacards.size(), is(expectedumberOfMetacards)); } private List<String> ingestMetacardsForExport() { List<String> metacardIds = new ArrayList<>(2); String metacardId1 = ingest(getFileContent( JSON_RECORD_RESOURCE_PATH + "/SimpleGeoJsonRecord"), "application/json"); metacardIds.add(metacardId1); String metacardId2 = ingest(getFileContent(XML_RECORD_RESOURCE_PATH + "/SimpleXmlMetacard"), "text/xml"); metacardIds.add(metacardId2); return metacardIds; } private void enableCrls() throws IOException { backupCrl(SERVER_ENCRYPTION_PROPERTIES, SERVER_ENCRYPTION_PROPERTIES_COPY); copyCrlEnabledPropertiesFile(CRL_ENABLED_SERVER_ENCRYPTION_PROPERTIES_FILE, SERVER_ENCRYPTION_PROPERTIES); backupCrl(SERVER_SIGNATURE_PROPERTIES, SERVER_SIGNATURE_PROPERTIES_COPY); copyCrlEnabledPropertiesFile(CRL_ENABLED_SERVER_SIGNATURE_PROPERTIES_FILE, SERVER_SIGNATURE_PROPERTIES); copyCrl(SERVER_SIGNATURE_DIR_PATH); backupCrl(ISSUER_ENCRYPTION_PROPERTIES, ISSUER_ENCRYPTION_PROPERTIES_COPY); copyCrlEnabledPropertiesFile(CRL_ENABLED_ISSUER_ENCRYPTION_PROPERTIES_FILE, ISSUER_ENCRYPTION_PROPERTIES); copyCrl(ISSUER_ENCRYPTION_DIR_PATH); backupCrl(ISSUER_SIGNATURE_PROPERTIES, ISSUER_SIGNATURE_PROPERTIES_COPY); copyCrlEnabledPropertiesFile(CRL_ENABLED_ISSUER_SIGNATURE_PROPERTIES_FILE, ISSUER_SIGNATURE_PROPERTIES); copyCrl(ISSUER_SIGNATURE_DIR_PATH); } private void backupCrl(Path source, Path destination) throws IOException { FileUtils.moveFile(source.toFile(), destination.toFile()); } private void copyCrlEnabledPropertiesFile(String source, Path destination) throws IOException { FileUtils.copyInputStreamToFile(getFileContentAsStream(source), Paths.get(ddfHome) .resolve(destination) .toFile()); } private void copyCrl(Path destinationDir) throws IOException { FileUtils.forceMkdir(Paths.get(ddfHome) .resolve(destinationDir) .toFile()); FileUtils.copyInputStreamToFile(getFileContentAsStream(CRL_PEM), Paths.get(ddfHome) .resolve(destinationDir) .resolve(CRL_PEM) .toFile()); } private void disableCrls() throws IOException { restoreBackup(SERVER_ENCRYPTION_PROPERTIES_COPY, SERVER_ENCRYPTION_PROPERTIES); restoreBackup(SERVER_SIGNATURE_PROPERTIES_COPY, SERVER_SIGNATURE_PROPERTIES); restoreBackup(ISSUER_ENCRYPTION_PROPERTIES_COPY, ISSUER_ENCRYPTION_PROPERTIES); restoreBackup(ISSUER_SIGNATURE_PROPERTIES_COPY, ISSUER_SIGNATURE_PROPERTIES); FileUtils.deleteQuietly(Paths.get(ddfHome) .resolve(SERVER_SIGNATURE_DIR_PATH) .toFile()); FileUtils.deleteQuietly(Paths.get(ddfHome) .resolve(ISSUER_ENCRYPTION_DIR_PATH) .toFile()); FileUtils.deleteQuietly(Paths.get(ddfHome) .resolve(ISSUER_SIGNATURE_DIR_PATH) .toFile()); } private Path getPathToConfigDirectory() { return Paths.get(ddfHome) .resolve("etc"); } private Path getPathToProcessedDirectory() { return getPathToConfigDirectory().resolve("processed"); } private Path getPathToFailedDirectory() { return getPathToConfigDirectory().resolve("failed"); } private InputStream getResourceAsStream(String resource) { return getClass().getResourceAsStream("/" + resource); } /** * Returns the default location for exported configuration files * * @param exportDirectory root directory of the export * @param pid PID of the configuration file * @return Full path to the exported configuration file */ private Path getPathToExportedConfig(Path exportDirectory, String pid) { return getExportSubDirectory(exportDirectory, pid + ".config"); } private Path getDefaultExportDirectory() { return Paths.get(ddfHome, "etc", "exported"); } private Path getExportSubDirectory(Path exportDirectory, String... paths) { Path directory = exportDirectory.resolve("etc"); for (String path : paths) { directory = directory.resolve(path); } return directory; } private void assertExportContents(Path exportDirectory) { assertThat(getExportSubDirectory(exportDirectory, "system.properties").toFile() .exists(), is(true)); assertThat(getExportSubDirectory(exportDirectory, "users.properties").toFile() .exists(), is(true)); assertThat(getExportSubDirectory(exportDirectory, "users.attributes").toFile() .exists(), is(true)); assertThat(getExportSubDirectory(exportDirectory, "org.codice.ddf.admin.applicationlist.properties").toFile() .exists(), is(true)); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, "keystores"), "serverKeystore.jks", "serverTruststore.jks"); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, "pdp", "policies"), "access-policy.xml"); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, "ws-security"), "attributeMap.properties", "issuer", "server"); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, "ws-security", "issuer"), "encryption.properties", "signature.properties"); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, "ws-security", "server"), "encryption.properties", "signature.properties"); } private void assertExportCatalog(Path exportPath) { String[] metacards = exportPath.toFile() .list(); assertThat("Exported files should not be null.", metacards, is(notNullValue())); assertThat(metacards.length, is(2)); } private void assertExportContentsWithCrlsEnabled(Path exportDirectory) { assertExportContents(exportDirectory); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, DEMO_CA_CRL_PATH.toString()), CRL_PEM); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, SERVER_SIGNATURE_DIR), CRL_PEM); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, ISSUER_ENCRYPTION_DIR), CRL_PEM); assertThatDirectoryContains(getExportSubDirectory(exportDirectory, ISSUER_SIGNATURE_DIR), CRL_PEM); } private void addConfigurationFile(String fileName, InputStream inputStream) throws IOException { FileUtils.copyInputStreamToFile(inputStream, getPathToConfigDirectory().resolve(fileName) .toFile()); inputStream.close(); } private void addConfigurationFileAndWaitForSuccessfulProcessing(String resourceName, InputStream inputStream) throws IOException { addConfigurationFile(resourceName, inputStream); expect("File " + getPathToProcessedDirectory().resolve(resourceName) .toString() + " exists").within(20, SECONDS) .until(() -> Files.exists(getPathToProcessedDirectory().resolve(resourceName))); } private void addConfigurationFileAndWaitForFailedProcessing(String resourceName, InputStream inputStream) throws IOException { addConfigurationFile(resourceName, inputStream); expect("File " + getPathToFailedDirectory().resolve(resourceName) .toString() + " exists").within(20, SECONDS) .until(() -> Files.exists(getPathToFailedDirectory().resolve(resourceName))); } private InputStream replaceTextInResource(InputStream is, String textToReplace, String replacement) throws IOException { StringWriter writer = new StringWriter(); IOUtils.copy(is, writer); String original = writer.toString(); String modified = original.replace(textToReplace, replacement); return IOUtils.toInputStream(modified, "UTF-8"); } private void assertThatDirectoryContains(Path path, String... fileNames) { String[] keystoreFiles = path.toFile() .list(); assertThat("Exported files should not be null.", keystoreFiles, is(notNullValue())); assertThat(String.format("Files missing in %s directory", path.toString()), keystoreFiles, arrayContainingInAnyOrder(fileNames)); } private void restoreBackup(Path copy, Path original) throws IOException { if (Files.exists(copy) && Files.isDirectory(copy)) { FileUtils.deleteQuietly(original.toFile()); FileUtils.moveDirectory(copy.toFile(), original.toFile()); } else if (Files.exists(copy) && !Files.isDirectory(copy)) { FileUtils.deleteQuietly(original.toFile()); FileUtils.moveFile(copy.toFile(), original.toFile()); } } /** * Class that provides utility and assertion methods for a Managed Service Felix configuration * file. * <p> * Note: Since we have custom code in the @{link FelixPersistenceStrategy} class to convert * floats and doubles, we cannot simply rely on what we read from the file to assert that the * Configuration object has the right properties. For this reason, we need to create our own * Dictionary of properties in {@link #getExpectedProperties()} and use that when doing our * assertions. */ private static class ManagedServiceConfigFile { String pid; private ManagedServiceConfigFile(String pid) { this.pid = pid; } private String getResourcePath() { return String.format("/%s.config", pid); } /** * Copies the configuration file to the /etc directory. */ void addConfigurationFile() throws IOException { FileUtils.copyInputStreamToFile(getResourceAsStream(), getPathToEtcDirectory()); } /** * Copies the configuration file to the /etc directory and waits for the * {@link org.osgi.service.cm.Configuration} object to be initialized with all the values * found in the configuration file. */ void addConfigurationFileAndWait(ConfigurationAdmin configAdmin) throws Exception { addConfigurationFile(); expect("Configuration properties for PID " + pid + " to be set").within(20, SECONDS) .until(new GetConfigurationProperties(configAdmin, "id", pid), equalToConfigurationProperties(getFileProperties())); } /** * Asserts that the {@link org.osgi.service.cm.Configuration} object for the current PID * contains the expected properties. */ private void assertConfigurationPropertiesSet(ConfigurationAdmin configAdmin) throws Exception { Dictionary<String, Object> properties = new GetConfigurationProperties(configAdmin, "id", pid).call(); assertThat("No Configuration object exist for PID " + pid, properties, is(notNullValue())); assertPropertiesMatch(properties, getExpectedProperties()); } /** * Asserts that the configuration file has been moved to the /etc/processed directory. */ private void assertFileMovedToProcessedDirectory() { expect("File to be moved to /etc/processed directory").within(20, SECONDS) .until(() -> getPathToProcessedDirectory().exists()); assertThat(String.format("Configuration file %s has not been removed", getPathToEtcDirectory().getAbsolutePath()), getPathToEtcDirectory().exists(), is(false)); } /** * Asserts that the configuration file has been moved to the /etc/failed directory. */ private void assertFileMovedToFailedDirectory() { expect("Waiting for file to be moved to /etc/failed directory").within(20, SECONDS) .until(() -> getPathToFailedDirectory().exists()); assertThat(String.format("Configuration file %s has not been removed", getPathToEtcDirectory().getAbsolutePath()), getPathToEtcDirectory().exists(), is(false)); } /** * Asserts that the properties in the {@link org.osgi.service.cm.Configuration} object * match what' expected. */ void assertPropertiesMatch(Dictionary<String, Object> actualProperties, Dictionary<String, Object> expectedProperties) throws IOException { assertThat("Configuration properties do not match for PID " + pid, actualProperties, equalToConfigurationProperties(expectedProperties)); } /** * Gets the properties from the configuration file using the * {@link FelixPersistenceStrategy#read(InputStream)} method. This is used when waiting for * the {@link #addConfigurationFileAndWait(ConfigurationAdmin)} when waiting for the * {@link org.osgi.service.cm.Configuration} object to be initialized. */ Dictionary<String, Object> getFileProperties() throws Exception { return new FelixPersistenceStrategy().read(getResourceAsStream()); } /** * Adds the {@code service.pid} property to the list of expected properties. Needed * because MSF configuration files do not have that property. */ void addToExpectedProperties(Dictionary<String, Object> expectedProperties) { expectedProperties.put("service.pid", pid); } private Dictionary<String, Object> getExpectedProperties() { Dictionary<String, Object> properties = new Hashtable<>(); addToExpectedProperties(properties); properties.put("id", pid); properties.put("property.string", "string"); properties.put("property.boolean.true", true); properties.put("property.boolean.false", false); properties.put("property.int", 10); properties.put("property.long", 100L); properties.put("property.float", 10.5f); properties.put("property.double", 100.1234d); properties.put("property.array.strings", new String[] {"A", "B", "C"}); properties.put("property.array.booleans", new Boolean[] {Boolean.TRUE, Boolean.FALSE}); properties.put("property.array.ints", new Integer[] {10, 20, 30}); properties.put("property.array.longs", new Long[] {100L, 200L, 300L}); properties.put("property.array.floats", new Float[] {1.1f, 2.2f, 3.3f}); properties.put("property.array.doubles", new Double[] {1.123, 2.234, 3.345}); Vector<String> strings = new Vector<>(); strings.add("A"); strings.add("B"); strings.add("C"); properties.put("property.vector.strings", strings); Vector<Boolean> booleans = new Vector<>(); booleans.add(Boolean.TRUE); booleans.add(Boolean.FALSE); properties.put("property.vector.booleans", booleans); Vector<Integer> ints = new Vector<>(); ints.add(10); ints.add(20); ints.add(30); properties.put("property.vector.ints", ints); Vector<Long> longs = new Vector<>(); longs.add(100L); longs.add(200L); longs.add(300L); properties.put("property.vector.longs", longs); Vector<Float> floats = new Vector<>(); floats.add(1.1f); floats.add(2.2f); floats.add(3.3f); properties.put("property.vector.floats", floats); Vector<Double> doubles = new Vector<>(); doubles.add(1.123); doubles.add(2.234); doubles.add(3.345); properties.put("property.vector.doubles", doubles); return properties; } private File getPathToProcessedDirectory() { return new File(String.format("%s%setc%sprocessed%s", ddfHome, File.separator, File.separator, getResourcePath())); } private File getPathToFailedDirectory() { return new File(String.format("%s%setc%sfailed%s", ddfHome, File.separator, File.separator, getResourcePath())); } private File getPathToEtcDirectory() { return new File(String.format("%s%setc%s", ddfHome, File.separator, getResourcePath())); } private InputStream getResourceAsStream() { return getFileContentAsStream(getResourcePath()); } } /** * Class that provides utility and assertion methods for a Managed Service Factory Felix * configuration file. */ private static class ManagedServiceFactoryConfigFile extends ManagedServiceConfigFile { private final String factoryPid; private ManagedServiceFactoryConfigFile(String pid) { super(pid); this.factoryPid = pid.substring(0, pid.lastIndexOf('.')); } /** * {@inheritDoc} * <p> * Overridden to use {@link ManagedServiceFactoryConfigurationPropertiesEqualTo}. */ @Override void assertPropertiesMatch(Dictionary<String, Object> actualProperties, Dictionary<String, Object> expectedProperties) throws IOException { assertThat("Configuration properties do not match for PID " + pid, actualProperties, new ManagedServiceFactoryConfigurationPropertiesEqualTo(factoryPid, expectedProperties)); } /** * {@inheritDoc} * <p> * Overridden to use {@link ManagedServiceFactoryConfigurationPropertiesEqualTo}. */ @Override void addConfigurationFileAndWait(ConfigurationAdmin configAdmin) throws Exception { addConfigurationFile(); expect("Waiting for Configuration expectedProperties for PID " + pid + " to be set").within(20, SECONDS) .until(new GetConfigurationProperties(configAdmin, "id", pid), new ManagedServiceFactoryConfigurationPropertiesEqualTo(factoryPid, getFileProperties())); } /** * Adds the {@code service.factoryPid} property to the list of expected properties. Needed * because MSF configuration files use that property instead of {@code service.pid}. */ @Override void addToExpectedProperties(Dictionary<String, Object> expectedProperties) { expectedProperties.put("service.factoryPid", factoryPid); } } /** * Hamcrest {@link org.hamcrest.Matcher} class for Managed Service Factory properties. Ensures * that the {@code service.pid} property starts with the {@code factoryPid}. */ private static class ManagedServiceFactoryConfigurationPropertiesEqualTo extends ConfigurationPropertiesEqualTo { private String factoryPid; private ManagedServiceFactoryConfigurationPropertiesEqualTo(String factoryPid, Dictionary<String, Object> expectedProperties) { super(expectedProperties); this.factoryPid = factoryPid; } @Override public boolean matches(Object object) { if ((object == null) || !(object instanceof Dictionary)) { return false; } @SuppressWarnings("unchecked") Dictionary<String, Object> properties = (Dictionary<String, Object>) object; if (properties.get("service.pid") == null) { return false; } if (!((String) properties.get("service.pid")).startsWith(factoryPid)) { return false; } properties.remove("service.pid"); return super.matches(properties); } } }