alopresto commented on a change in pull request #3672: NIFI-6363 Additional Sensitive Property Providers URL: https://github.com/apache/nifi/pull/3672#discussion_r318212886
########## File path: nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-properties-loader/src/test/java/org/apache/nifi/properties/sensitive/hashicorp/vault/VaultHttpSensitivePropertyProviderIT.java ########## @@ -0,0 +1,421 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.properties.sensitive.hashicorp.vault; + +import org.apache.nifi.properties.sensitive.AbstractSensitivePropertyProviderTest; +import org.apache.nifi.properties.sensitive.SensitivePropertyProvider; +import org.apache.nifi.security.util.CipherUtils; +import org.jetbrains.annotations.NotNull; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.vault.VaultException; +import org.springframework.vault.authentication.TokenAuthentication; +import org.springframework.vault.client.VaultEndpoint; +import org.springframework.vault.core.VaultOperations; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.support.VaultMount; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; + +import java.net.URI; +import java.security.SecureRandom; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + + +/** + * These tests use a containerized Vault server in "dev" mode to test our Vault Sensitive Property Provider. + * The image starts a Vault server with an HTTP endpoint, and uses the random token we pass into it as + * its initial root token. + * + * The tests manipulate the system properties, much in the same way that a user would set Vault values + * by way of setting system properties (or environment variables). + * + * Before the tests are run, we have a static method to configure the Vault server to match our expectations, + * also much in the same way that a user would set Vault options by way of the Vault CLI or API. + * + */ +public class VaultHttpSensitivePropertyProviderIT extends AbstractSensitivePropertyProviderTest { + private static final Logger logger = LoggerFactory.getLogger(VaultHttpSensitivePropertyProviderIT.class); + private static final SecureRandom random = new SecureRandom(); + + static GenericContainer vaultContainer; + static VaultOperations vaultOperations; + + static final String vaultToken = CipherUtils.getRandomHex(16); + private static final int vaultPort = 8200; + static String vaultUri; + + // For app role auth tests: + private static String authRoleId; + private static String authSecretId; + + // For app id auth tests: + private static String authAppId; + private static String authUserId; + + // For token and cubbyhole auth types: + private static String transitKeyId; + private static String cubbyholeTokenId; + + // System property names that we clear and check before each test: + private Set<String> vaultSystemPropertyNames = Stream.of( + "vault.ssl.trust-store", + "vault.authentication", + "vault.uri", + "vault.token", + "vault.app-role.role-id", + "vault.app-role.secret-id", + "vault.app-id.app-id", + "vault.app-id.user-id" + ).collect(Collectors.toSet()); + + private Set<String> keySpecs; + + /** + * This method creates the Vault environment needed by the test cases. + */ + @BeforeClass + public static void createTestContainer() { + vaultContainer = new GenericContainer<>("vault:latest") + .withEnv("VAULT_DEV_ROOT_TOKEN_ID", vaultToken) + .withExposedPorts(vaultPort) + .waitingFor(Wait.forLogMessage("==> Vault server started.*", 1)); + + vaultContainer.start(); + vaultUri = "http://" + vaultContainer.getContainerIpAddress() + ":" + vaultContainer.getMappedPort(vaultPort); + vaultOperations = new VaultTemplate(VaultEndpoint.from(URI.create(vaultUri)), new TokenAuthentication(vaultToken)); + configureVaultTestEnvironment(vaultOperations); + } + + /** + * Given a Vault operations client, this method configures a Vault environment for our unit tests. + * + * @param operations Vault operations client + */ + static void configureVaultTestEnvironment(VaultOperations operations) { + try { + operations.opsForSys().authMount("app-id", VaultMount.create("app-id")); + operations.opsForSys().authMount("approle", VaultMount.create("approle")); + operations.opsForSys().mount("transit", VaultMount.create("transit")); + } catch (final VaultException ignored) { + } + + // This block creates a vault policy for our apps, users, etc: + String rules = "{'name': 'test-policy', 'path': {'*': {'policy': 'write'}}}"; + rules = rules.replace("'", "\""); + operations.write("sys/policy/test-policy", Collections.singletonMap("rules", rules)); + + // This block enables our expected "app role" authentication configuration: + authAppId = CipherUtils.getRandomHex(12); + String vaultRoleName = authAppId; + Map<String, String> config = new HashMap<>(); + config.put("policies", "test-policy"); + config.put("bind_secret_id", "true"); + operations.write("auth/approle/role/" + vaultRoleName, config); + authRoleId = (String) operations.read(String.format("auth/approle/role/%s/role-id", vaultRoleName)).getData().get("role_id"); + authSecretId = (String) operations.write(String.format("auth/approle/role/%s/secret-id", vaultRoleName), null).getData().get("secret_id"); + logger.info("Constructed 'app role' with role-id=" + authRoleId + " and secret-id=" + authSecretId); + + // This block enables our expected "app id" authentication configuration: + config = new HashMap<>(); + config.put("value", "test-policy"); + config.put("display_name", authAppId); + operations.write("auth/app-id/map/app-id/" + authAppId, config); + authUserId = CipherUtils.getRandomHex(6); + config = new HashMap<>(); + config.put("value", authAppId); + operations.write("auth/app-id/map/user-id/" + authUserId, config); + logger.info("Constructed 'app id' with app-id:" + authAppId + " user-id:" + authUserId); + + // This block creates a transit key, much like a user would create via the vault ui or cli: + transitKeyId = CipherUtils.getRandomHex(12); + operations.opsForTransit().createKey(transitKeyId); + logger.info("Constructed 'transit key' with key-id:" + transitKeyId); + + // This block creates a token for cubbyhole authentication: + cubbyholeTokenId = operations.opsForToken().create().getToken().getToken(); + config = new HashMap<>(); + config.put("token", cubbyholeTokenId); + operations.write("cubbyhole/token", config); + logger.info("Constructed 'cubbyhole token' with token-id:" + cubbyholeTokenId); + } + + @AfterClass + public static void stopServerContainer() { + try { + vaultContainer.stop(); + } catch (final Exception ignored) { + } Review comment: Add a `finally()` block which enforces the `.stop()` call. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: [email protected] With regards, Apache Git Services
