diff --git a/.gitignore b/.gitignore index 625a98e1..f84ed318 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,16 @@ nginx.key api_key .settings/ **/.settings/ + +###################### +# Intellij +###################### +.idea/ +*.iml +*.iws +*.ipr +*.ids +*.orig +classes/ +out/ +.DS_Store diff --git a/README.md b/README.md index 5f07c137..d02851c1 100644 --- a/README.md +++ b/README.md @@ -310,18 +310,18 @@ You can do this by setting Conjur Properties or [Environment variables](#environ #### CyberArk Conjur Configuration Properties The following configuration properties can be set in the standard `spring-boot` configuration files, `application.properties` or `application.yml`: -| Parameter name | Description | -|:-------------------------|:----------------------------------------| -| conjur.account | CyberArk Conjur Account | -| conjur.appliance-url | CyberArk Conjur Appliance URL | -| conjur.authn-login | CyberArk Conjur User /host identity | -| conjur.authn-api-key | CyberArk Conjur API KEY of the host | -| conjur.auth-token-file | CyberArk Conjur Token, stored in a file | -| conjur.cert-file | CyberArk Conjur SSL Certificate path | -| conjur.ssl-certificate | CyberArk Conjur SSL Certificate Content | -| conjur.authenticator-id | CyberArk Conjur authenticator ID | -| conjur.jwt-token-path | CyberArk Conjur Path of the JWT Token | - +| Parameter name | Description | +|:------------------------|:----------------------------------------| +| conjur.account | CyberArk Conjur Account | +| conjur.appliance-url | CyberArk Conjur Appliance URL | +| conjur.authn-login | CyberArk Conjur User /host identity | +| conjur.authn-api-key | CyberArk Conjur API KEY of the host | +| conjur.auth-token-file | CyberArk Conjur Token, stored in a file | +| conjur.cert-file | CyberArk Conjur SSL Certificate path | +| conjur.ssl-certificate | CyberArk Conjur SSL Certificate Content | +| conjur.authenticator-id | CyberArk Conjur authenticator ID | +| conjur.jwt-token-path | CyberArk Conjur Path of the JWT Token | +| conjur.mapping-path | CyberArk Conjur Mapping Path |

Environment Variables @@ -334,7 +334,7 @@ For example:`appliance_url` is `CONJUR_APPLIANCE_URL`, `account` is `CONJUR_ACCO If no other configuration is done (e.g. over system properties or CLI parameters), include the following environment variables in the app's runtime environment to use the Spring Boot Plugin. | Name | Environment ID | Description | API KEY | JWT | -| ----------------------- | ----------------------- | -------------------------- | ------- | ---- | +|-------------------------|-------------------------|----------------------------| ------- | ---- | | Conjur Account | CONJUR_ACCOUNT | Account to connect | Yes | Yes | | API key | CONJUR_AUTHN_API_KEY | User/host API Key/password | Yes | No | | Connection url | CONJUR_APPLIANCE_URL | Conjur instance to connect | Yes | Yes | @@ -343,6 +343,7 @@ If no other configuration is done (e.g. over system properties or CLI parameters | SSL Certificate Content | CONJUR_SSL_CERTIFICATE | Certificate content | Yes | Yes | | Path of the JWT Token | CONJUR_JWT_TOKEN_PATH | Path of the JWT Token | No | Yes | | Conjur authenticator ID | CONJUR_AUTHENTICATOR_ID | Conjur authenticator ID | No | Yes | +| Conjur MAPPING PATH | CONJUR_MAPPING_PATH | Conjur Mapping PATH | Yes | Yes | Only one CONJUR_CERT_FILE and CONJUR_SSL_CERTIFICATE is required. There are two variables to allow the user to specify the path to a certificate file or provide the certificate data directly in an environment variable. diff --git a/src/main/java/com/cyberark/conjur/springboot/annotations/Registrar.java b/src/main/java/com/cyberark/conjur/springboot/annotations/Registrar.java index 20a4d472..9083711a 100644 --- a/src/main/java/com/cyberark/conjur/springboot/annotations/Registrar.java +++ b/src/main/java/com/cyberark/conjur/springboot/annotations/Registrar.java @@ -4,6 +4,7 @@ import java.util.LinkedHashMap; import com.cyberark.conjur.sdk.endpoint.SecretsApi; +import com.cyberark.conjur.springboot.core.env.ConjurConfig; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanDefinition; @@ -58,6 +59,7 @@ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) continue; } ps.setSecretsApi(beanFactory.getBean(SecretsApi.class)); + ps.setConjurConfig(beanFactory.getBean(ConjurConfig.class)); propertySources.addLast(ps); } } diff --git a/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurConfig.java b/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurConfig.java index 6e2e3653..da85f2f5 100644 --- a/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurConfig.java +++ b/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurConfig.java @@ -1,63 +1,97 @@ package com.cyberark.conjur.springboot.core.env; +import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.file.Files; import java.util.Properties; import com.cyberark.conjur.springboot.constant.ConjurConstant; +import com.cyberark.conjur.springboot.domain.ConjurProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.context.properties.bind.BindResult; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; +import org.springframework.util.ResourceUtils; + +import static com.cyberark.conjur.springboot.constant.ConjurConstant.CONJUR_PREFIX; + /** * * This class loads the external configured conjur.properties file and resolves * the keys values defined in properties file. * */ -public class ConjurConfig { +public class ConjurConfig implements EnvironmentAware, BeanFactoryPostProcessor { private static final Properties PROPS = new Properties(); - private static final ConjurConfig UNIQUE_INSTANCE = new ConjurConfig(); - private static final Logger LOGGER = LoggerFactory.getLogger(ConjurConfig.class); + /** + * The Environment. + */ + private Environment environment; - private ConjurConfig() { + /** + * + * @param name - key define at given property file. + * @return - corresponding value of key defined at given property file. + */ + public String mapProperty(String name) { + String mapped = PROPS.getProperty(ConjurConstant.CONJUR_MAPPING + name); + return mapped != null ? mapped : name; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + final BindResult result = Binder.get(environment).bind(CONJUR_PREFIX, ConjurProperties.class); + if (result.isBound()) { + loadMappingProps(result); + } + } - InputStream propsFile = ConjurConfig.class.getResourceAsStream(ConjurConstant.CONJUR_PROPERTIES); + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + private void loadMappingProps(BindResult result) { + String mappingPath = result.get().getMappingPath(); + InputStream propsFile = null; + if (mappingPath != null) { + try { + File file = ResourceUtils.getFile(mappingPath); + propsFile = Files.newInputStream(file.toPath()); + } + catch (IOException e) { + LOGGER.error(e.getMessage(), e); + } + } + else { + propsFile = ConjurConfig.class.getResourceAsStream(ConjurConstant.CONJUR_PROPERTIES); + } if (propsFile != null) { try { PROPS.load(propsFile); - } catch (IOException e) { + } + catch (IOException e) { LOGGER.error(e.getMessage(), e); - } finally { + } + finally { try { propsFile.close(); - } catch (IOException e) { + } + catch (IOException e) { LOGGER.error(e.getMessage(), e); } } } - - } - - /** - * - * @return unique instance of class. - */ - public static ConjurConfig getInstance() { - return UNIQUE_INSTANCE; - } - - /** - * - * @param name - key define at given property file. - * @return - corresponding value of key defined at given property file. - */ - public String mapProperty(String name) { - String mapped = PROPS.getProperty(ConjurConstant.CONJUR_MAPPING + name); - - return mapped != null ? mapped : name; } } diff --git a/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurPropertySource.java b/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurPropertySource.java index 1fb79616..cf21a7aa 100644 --- a/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurPropertySource.java +++ b/src/main/java/com/cyberark/conjur/springboot/core/env/ConjurPropertySource.java @@ -30,6 +30,8 @@ public class ConjurPropertySource extends EnumerablePropertySource { private SecretsApi secretsApi; private List properties; + + private ConjurConfig conjurConfig; private static final Logger LOGGER = LoggerFactory.getLogger(ConjurPropertySource.class); @@ -73,7 +75,7 @@ public Object getProperty(String key) { this.vaultPath = vaultPath.concat("/"); } if (propertyExists(key)) { - key = ConjurConfig.getInstance().mapProperty(key); + key = conjurConfig.mapProperty(key); try { String account = ConjurConnectionManager.getAccount(secretsApi); String secretValue = secretsApi.getSecret(account, ConjurConstant.CONJUR_KIND, vaultPath + key); @@ -98,4 +100,8 @@ public void setSecretsApi(SecretsApi secretsApi) { private boolean propertyExists(String key) { return properties.stream().anyMatch(property -> property.contains(key)); } + + public void setConjurConfig(ConjurConfig conjurConfig) { + this.conjurConfig = conjurConfig; + } } \ No newline at end of file diff --git a/src/main/java/com/cyberark/conjur/springboot/domain/ConjurProperties.java b/src/main/java/com/cyberark/conjur/springboot/domain/ConjurProperties.java index bf2152ab..c8011b5c 100644 --- a/src/main/java/com/cyberark/conjur/springboot/domain/ConjurProperties.java +++ b/src/main/java/com/cyberark/conjur/springboot/domain/ConjurProperties.java @@ -56,6 +56,11 @@ public class ConjurProperties{ */ private String authenticatorId; + /** + * The Conjur mapping path. + */ + private String mappingPath; + /** * Gets account. * @@ -218,6 +223,24 @@ public void setAuthenticatorId(String authenticatorId) { this.authenticatorId = authenticatorId; } + /** + * Gets conjur mapping path. + * + * @return the conjur mapping path + */ + public String getMappingPath() { + return mappingPath; + } + + /** + * Sets conjur mapping path. + * + * @param mappingPath the conjur mapping path + */ + public void setMappingPath(String mappingPath) { + this.mappingPath = mappingPath; + } + @Override public String toString() { return "ConjurProperties{" + @@ -230,6 +253,7 @@ public String toString() { ", sslCertificate='" + sslCertificate + '\'' + ", jwtTokenPath='" + jwtTokenPath + '\'' + ", authenticatorId='" + authenticatorId + '\'' + + ", conjurMappingPath='" + mappingPath + '\'' + '}'; } } \ No newline at end of file diff --git a/src/main/java/com/cyberark/conjur/springboot/processor/ConjurCloudProcessor.java b/src/main/java/com/cyberark/conjur/springboot/processor/ConjurCloudProcessor.java index cf831676..fbae2f58 100644 --- a/src/main/java/com/cyberark/conjur/springboot/processor/ConjurCloudProcessor.java +++ b/src/main/java/com/cyberark/conjur/springboot/processor/ConjurCloudProcessor.java @@ -8,6 +8,7 @@ import org.springframework.core.env.Environment; import com.cyberark.conjur.sdk.endpoint.SecretsApi; +import com.cyberark.conjur.springboot.core.env.ConjurConfig; import com.cyberark.conjur.springboot.service.CustomPropertySourceChain; import com.cyberark.conjur.springboot.service.DefaultPropertySourceChain; import com.cyberark.conjur.springboot.service.PropertyProcessorChain; @@ -31,14 +32,17 @@ public class ConjurCloudProcessor implements BeanPostProcessor, InitializingBean private PropertyProcessorChain processorChain; + private ConjurConfig conjurConfig; + @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } - public ConjurCloudProcessor(SecretsApi secretsApi) { + public ConjurCloudProcessor(SecretsApi secretsApi, ConjurConfig conjurConfig) { super(); this.secretsApi = secretsApi; + this.conjurConfig= conjurConfig; } @Override @@ -48,6 +52,7 @@ public void afterPropertiesSet() throws Exception { CustomPropertySourceChain customPS = new CustomPropertySourceChain("CustomPropertySource"); processorChain.setNextChain(customPS); customPS.setSecretsApi(secretsApi); + customPS.setConjurConfig(conjurConfig); environment.getPropertySources().addLast(processorChain); } diff --git a/src/main/java/com/cyberark/conjur/springboot/processor/SpringBootConjurAutoConfiguration.java b/src/main/java/com/cyberark/conjur/springboot/processor/SpringBootConjurAutoConfiguration.java index c509c05c..eb26192f 100644 --- a/src/main/java/com/cyberark/conjur/springboot/processor/SpringBootConjurAutoConfiguration.java +++ b/src/main/java/com/cyberark/conjur/springboot/processor/SpringBootConjurAutoConfiguration.java @@ -2,6 +2,7 @@ import static com.cyberark.conjur.springboot.constant.ConjurConstant.CONJUR_PREFIX; +import com.cyberark.conjur.springboot.core.env.ConjurConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -61,11 +62,16 @@ ConjurProperties conjurProperties(){ @ConditionalOnMissingBean(ConjurPropertySource.class) @Bean - static ConjurCloudProcessor conjurCloudProcessor(SecretsApi secretsApi) { + static ConjurCloudProcessor conjurCloudProcessor(SecretsApi secretsApi, ConjurConfig conjurConfig) { LOGGER.info("Creating ConjurCloudProcessor instance"); - return new ConjurCloudProcessor(secretsApi); + return new ConjurCloudProcessor(secretsApi, conjurConfig); + } + + @ConditionalOnMissingBean + @Bean + static ConjurConfig conjurConfig() { + return new ConjurConfig(); } - } \ No newline at end of file diff --git a/src/main/java/com/cyberark/conjur/springboot/service/CustomPropertySourceChain.java b/src/main/java/com/cyberark/conjur/springboot/service/CustomPropertySourceChain.java index cfb109a6..a9eeaa2a 100644 --- a/src/main/java/com/cyberark/conjur/springboot/service/CustomPropertySourceChain.java +++ b/src/main/java/com/cyberark/conjur/springboot/service/CustomPropertySourceChain.java @@ -24,6 +24,8 @@ public class CustomPropertySourceChain extends PropertyProcessorChain { private SecretsApi secretsApi; + private ConjurConfig conjurConfig; + public CustomPropertySourceChain(String name) { super("customPropertySource"); @@ -41,6 +43,10 @@ public void setSecretsApi(SecretsApi secretsApi) { this.secretsApi = secretsApi; } + public void setConjurConfig(ConjurConfig conjurConfig) { + this.conjurConfig = conjurConfig; + } + @Override public String[] getPropertyNames() { @@ -52,7 +58,7 @@ public Object getProperty(String key) { byte[] result = null; - key = ConjurConfig.getInstance().mapProperty(key); + key = conjurConfig.mapProperty(key); if (!(key.startsWith(ConjurConstant.SPRING_VAR)) && !(key.startsWith(ConjurConstant.SERVER_VAR)) && !(key.startsWith(ConjurConstant.ERROR)) && !(key.startsWith(ConjurConstant.SPRING_UTIL)) diff --git a/src/test/java/com/cyberark/conjur/springboot/core/env/ConjurConfigTest.java b/src/test/java/com/cyberark/conjur/springboot/core/env/ConjurConfigTest.java new file mode 100644 index 00000000..e18010ff --- /dev/null +++ b/src/test/java/com/cyberark/conjur/springboot/core/env/ConjurConfigTest.java @@ -0,0 +1,30 @@ +package com.cyberark.conjur.springboot.core.env; + +import com.cyberark.conjur.sdk.ApiException; +import com.cyberark.conjur.springboot.annotations.ConjurPropertySource; +import com.cyberark.conjur.springboot.processor.SpringBootConjurAutoConfiguration; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * @author bnasslahsen + */ +@SpringBootTest(classes = SpringBootConjurAutoConfiguration.class) +@ConjurPropertySource({}) +public class ConjurConfigTest { + + @Autowired + private ConjurConfig conjurConfig; + + @Test + public void testGetMappings() throws ApiException { + assertEquals("vault/bnl-k8s-safe/mysql-test-db/dsn", conjurConfig.mapProperty("testUrl")); + assertEquals("vault/bnl-k8s-safe/mysql-test-db/username", conjurConfig.mapProperty("testUsername")); + } + + +} diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties index 1a62693d..27de5b0e 100644 --- a/src/test/resources/application.properties +++ b/src/test/resources/application.properties @@ -1,2 +1,3 @@ logging.level.com.cyberark = DEBUG -conjur.authn_api_key=***REMOVED*** \ No newline at end of file +conjur.authn_api_key=***REMOVED*** +conjur.mapping-path=classpath:mapping-dir/conjur.properties \ No newline at end of file diff --git a/src/test/resources/mapping-dir/conjur.properties b/src/test/resources/mapping-dir/conjur.properties new file mode 100644 index 00000000..de0ef70f --- /dev/null +++ b/src/test/resources/mapping-dir/conjur.properties @@ -0,0 +1,3 @@ +conjur.mapping.testUrl=vault/bnl-k8s-safe/mysql-test-db/dsn +conjur.mapping.testUsername=vault/bnl-k8s-safe/mysql-test-db/username +