diff --git a/docs/modules/ROOT/pages/secret-backends.adoc b/docs/modules/ROOT/pages/secret-backends.adoc index 9bc85052..fa2dc3c8 100644 --- a/docs/modules/ROOT/pages/secret-backends.adoc +++ b/docs/modules/ROOT/pages/secret-backends.adoc @@ -161,6 +161,78 @@ spring.cloud.vault: See also: https://www.vaultproject.io/docs/secrets/rabbitmq/index.html[Vault Documentation: Setting up RabbitMQ with Vault] +[[vault.config.backends.ldap]] +== LDAP + +Spring Cloud Vault can obtain credentials for LDAP. + +The LDAP integration requires the `spring-cloud-vault-config-ldap` +dependency. + +.pom.xml +[source,xml,indent=0,subs="verbatim,quotes,attributes"] +---- + + + org.springframework.cloud + spring-cloud-vault-config-ldap + {project-version} + + +---- + +The integration can be enabled by setting +`spring.cloud.vault.ldap.enabled=true` (default `false`) and providing the role name with `spring.cloud.vault.ldap.role=…`. + +Vault's LDAP secret engine supports both dynamic and static roles: + +* **Dynamic roles** generate temporary credentials on-demand +* **Static roles** manage the rotation of existing LDAP user credentials + +To use a static role, set `spring.cloud.vault.ldap.static-role=true` (default `false`). + +Username and password are stored in `spring.ldap.username` +and `spring.ldap.password` so applications using Spring LDAP will pick up the generated credentials without further configuration. +You can configure the property names by setting `spring.cloud.vault.ldap.username-property` and +`spring.cloud.vault.ldap.password-property`. + +Example: Dynamic Role + +[source,yaml] +---- +spring.cloud.vault: + ldap: + enabled: true + role: my-dynamic-role + static-role: false + backend: ldap + username-property: spring.ldap.username + password-property: spring.ldap.password +---- + +Example: Static Role + +[source,yaml] +---- +spring.cloud.vault: + ldap: + enabled: true + role: my-static-role + static-role: true + backend: ldap + username-property: spring.ldap.username + password-property: spring.ldap.password +---- + +* `enabled` setting this value to `true` enables the LDAP backend config usage +* `role` sets the role name of the LDAP role definition +* `static-role` setting this value to `true` uses static role credentials instead of dynamic +* `backend` sets the path of the LDAP mount to use +* `username-property` sets the property name in which the LDAP username is stored +* `password-property` sets the property name in which the LDAP password is stored + +See also: https://www.vaultproject.io/docs/secrets/ldap/index.html[Vault Documentation: Setting up LDAP with Vault] + [[vault.config.backends.aws]] == AWS diff --git a/pom.xml b/pom.xml index 49394bf9..21e5634c 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,7 @@ spring-cloud-vault-config-consul spring-cloud-vault-config-rabbitmq spring-cloud-vault-config-aws + spring-cloud-vault-config-ldap spring-cloud-starter-vault-config docs diff --git a/spring-cloud-vault-config-ldap/pom.xml b/spring-cloud-vault-config-ldap/pom.xml new file mode 100644 index 00000000..2c40708f --- /dev/null +++ b/spring-cloud-vault-config-ldap/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + + org.springframework.cloud + spring-cloud-vault-parent + 5.0.1-SNAPSHOT + .. + + + spring-cloud-vault-config-ldap + Spring Cloud Vault Config LDAP support + Spring Cloud Vault Config LDAP support + + + + + org.springframework.cloud + spring-cloud-vault-config + + + + + org.springframework.boot + spring-boot-autoconfigure-processor + true + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.springframework.cloud + spring-cloud-vault-config + test-jar + test + + + + org.apache.httpcomponents.client5 + httpclient5 + test + + + + com.unboundid + unboundid-ldapsdk + test + + + + + diff --git a/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfiguration.java b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfiguration.java new file mode 100644 index 00000000..ebdd65a3 --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfiguration.java @@ -0,0 +1,117 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.cloud.vault.config.ldap; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.vault.config.PropertyNameTransformer; +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.SecretBackendMetadataFactory; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.util.Assert; +import org.springframework.vault.core.util.PropertyTransformer; + +/** + * Bootstrap configuration providing support for the LDAP secret engine. + * + * @author Drew Mullen + * @since 5.0.1 + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(VaultLdapProperties.class) +@Order(Ordered.LOWEST_PRECEDENCE - 15) +public class VaultConfigLdapBootstrapConfiguration { + + @Bean + @ConditionalOnMissingBean + public LdapSecretBackendMetadataFactory ldapSecretBackendMetadataFactory() { + return new LdapSecretBackendMetadataFactory(); + } + + /** + * {@link SecretBackendMetadataFactory} for LDAP integration using + * {@link VaultLdapProperties}. + */ + public static class LdapSecretBackendMetadataFactory implements SecretBackendMetadataFactory { + + /** + * Creates a {@link SecretBackendMetadata} for the LDAP secret engine using + * {@link VaultLdapProperties}. This accessor transforms Vault's username/password + * property names to names provided with + * {@link VaultLdapProperties#getUsernameProperty()} and + * {@link VaultLdapProperties#getPasswordProperty()}. + * @param properties must not be {@literal null}. + * @return the {@link SecretBackendMetadata} + */ + static SecretBackendMetadata forLdap(final VaultLdapProperties properties) { + + Assert.notNull(properties, "VaultLdapProperties must not be null"); + + PropertyNameTransformer transformer = new PropertyNameTransformer(); + transformer.addKeyTransformation("username", properties.getUsernameProperty()); + transformer.addKeyTransformation("password", properties.getPasswordProperty()); + + return new SecretBackendMetadata() { + + private final String credPath = properties.isStaticRole() ? "static-cred" : "creds"; + + @Override + public String getName() { + return String.format("%s with Role %s", properties.getBackend(), properties.getRole()); + } + + @Override + public String getPath() { + return String.format("%s/%s/%s", properties.getBackend(), this.credPath, properties.getRole()); + } + + @Override + public PropertyTransformer getPropertyTransformer() { + return transformer; + } + + @Override + public Map getVariables() { + + Map variables = new HashMap<>(); + variables.put("backend", properties.getBackend()); + variables.put("key", String.format("%s/%s", this.credPath, properties.getRole())); + return variables; + } + }; + } + + @Override + public SecretBackendMetadata createMetadata(VaultLdapProperties backendDescriptor) { + return forLdap(backendDescriptor); + } + + @Override + public boolean supports(VaultSecretBackendDescriptor backendDescriptor) { + return backendDescriptor instanceof VaultLdapProperties; + } + + } + +} diff --git a/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultLdapProperties.java b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultLdapProperties.java new file mode 100644 index 00000000..79e5d722 --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/VaultLdapProperties.java @@ -0,0 +1,114 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.cloud.vault.config.ldap; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.vault.config.VaultSecretBackendDescriptor; +import org.springframework.lang.Nullable; + +/** + * Configuration properties for Vault using the LDAP secret engine. + * + * @author Drew Mullen + * @since 5.0.1 + */ +@ConfigurationProperties("spring.cloud.vault.ldap") +public class VaultLdapProperties implements VaultSecretBackendDescriptor { + + /** + * Enable LDAP secret engine usage. + */ + private boolean enabled = false; + + /** + * Role name for credentials. + */ + @Nullable + private String role; + + /** + * Enable static role usage. + */ + private boolean staticRole = false; + + /** + * LDAP secret engine backend path. + */ + private String backend = "ldap"; + + /** + * Target property for the obtained username. + */ + private String usernameProperty = "spring.ldap.username"; + + /** + * Target property for the obtained password. + */ + private String passwordProperty = "spring.ldap.password"; + + @Override + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + @Nullable + public String getRole() { + return this.role; + } + + public void setRole(@Nullable String role) { + this.role = role; + } + + public boolean isStaticRole() { + return this.staticRole; + } + + public void setStaticRole(boolean staticRole) { + this.staticRole = staticRole; + } + + @Override + public String getBackend() { + return this.backend; + } + + public void setBackend(String backend) { + this.backend = backend; + } + + public String getUsernameProperty() { + return this.usernameProperty; + } + + public void setUsernameProperty(String usernameProperty) { + this.usernameProperty = usernameProperty; + } + + public String getPasswordProperty() { + return this.passwordProperty; + } + + public void setPasswordProperty(String passwordProperty) { + this.passwordProperty = passwordProperty; + } + +} diff --git a/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/package-info.java b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/package-info.java new file mode 100644 index 00000000..b7648d5f --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/main/java/org/springframework/cloud/vault/config/ldap/package-info.java @@ -0,0 +1,29 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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. + */ + +/** + * Support classes for Vault LDAP secret engine integration. Allows Spring Cloud Vault to + * fetch LDAP credentials from HashiCorp Vault using either dynamic or static roles. + *

+ * Dynamic roles generate temporary credentials on-demand, while static roles manage the + * rotation of existing LDAP user credentials. + *

+ * Configuration is done via {@code spring.cloud.vault.ldap} properties. + * + * @see org.springframework.cloud.vault.config.ldap.VaultLdapProperties + * @see org.springframework.cloud.vault.config.ldap.VaultConfigLdapBootstrapConfiguration + */ +package org.springframework.cloud.vault.config.ldap; diff --git a/spring-cloud-vault-config-ldap/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-vault-config-ldap/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 00000000..8f572d32 --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.springframework.cloud.vault.config.ldap.VaultConfigLdapBootstrapConfiguration diff --git a/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapSecretIntegrationTests.java b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapSecretIntegrationTests.java new file mode 100644 index 00000000..0a68dc83 --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapSecretIntegrationTests.java @@ -0,0 +1,133 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.cloud.vault.config.ldap; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import org.springframework.cloud.vault.config.VaultConfigOperations; +import org.springframework.cloud.vault.config.VaultConfigTemplate; +import org.springframework.cloud.vault.config.VaultProperties; +import org.springframework.cloud.vault.config.ldap.VaultConfigLdapBootstrapConfiguration.LdapSecretBackendMetadataFactory; +import org.springframework.cloud.vault.util.IntegrationTestSupport; +import org.springframework.cloud.vault.util.Settings; +import org.springframework.vault.core.VaultOperations; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link VaultConfigTemplate} using the LDAP secret engine with an + * in-memory UnboundID LDAP server. + * + * @author Drew Mullen + */ +public class LdapSecretIntegrationTests extends IntegrationTestSupport { + + @RegisterExtension + public static final LdapServerExtension ldapServer = new LdapServerExtension(); + + private VaultProperties vaultProperties = Settings.createVaultProperties(); + + private VaultConfigOperations configOperations; + + private VaultLdapProperties ldapProperties = new VaultLdapProperties(); + + /** + * Initialize the LDAP secret engine configuration with the in-memory LDAP server. + */ + @BeforeEach + public void setUp() { + this.ldapProperties.setEnabled(true); + this.ldapProperties.setBackend("ldap"); + + VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations(); + + if (!prepare().hasSecretBackend(this.ldapProperties.getBackend())) { + prepare().mountSecret(this.ldapProperties.getBackend()); + } + + Map ldapConfig = new HashMap<>(); + ldapConfig.put("binddn", "uid=vault-admin,ou=people," + ldapServer.getBaseDn()); + ldapConfig.put("bindpass", "vault-admin-password"); + ldapConfig.put("url", ldapServer.getLdapUrl()); + ldapConfig.put("userdn", "ou=people," + ldapServer.getBaseDn()); + + vaultOperations.write(String.format("%s/config", this.ldapProperties.getBackend()), ldapConfig); + + this.configOperations = new VaultConfigTemplate(vaultOperations, this.vaultProperties); + } + + /** + * Test for dynamic role credential retrieval. + */ + @Test + public void shouldCreateDynamicCredentialsCorrectly() { + VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations(); + + Map dynamicRoleConfig = new HashMap<>(); + dynamicRoleConfig.put("creation_ldif", + "dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\n" + "objectClass: person\n" + + "objectClass: top\n" + "cn: {{.Username}}\n" + "sn: {{.Username}}\n" + + "userPassword: {{.Password}}"); + dynamicRoleConfig.put("deletion_ldif", + "dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\nchangetype: delete"); + dynamicRoleConfig.put("default_ttl", "1h"); + dynamicRoleConfig.put("max_ttl", "24h"); + + vaultOperations.write(String.format("%s/role/dynamic-role", this.ldapProperties.getBackend()), + dynamicRoleConfig); + + this.ldapProperties.setRole("dynamic-role"); + this.ldapProperties.setStaticRole(false); + + Map secretProperties = this.configOperations + .read(LdapSecretBackendMetadataFactory.forLdap(this.ldapProperties)) + .getData(); + + assertThat(secretProperties).containsKeys("spring.ldap.username", "spring.ldap.password"); + } + + /** + * Test for static role credential retrieval. + */ + @Test + public void shouldCreateStaticCredentialsCorrectly() { + VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations(); + + Map staticRoleConfig = new HashMap<>(); + staticRoleConfig.put("dn", "uid=static-user,ou=people," + ldapServer.getBaseDn()); + staticRoleConfig.put("username", "static-user"); + staticRoleConfig.put("rotation_period", "24h"); + + vaultOperations.write(String.format("%s/static-role/static-role", this.ldapProperties.getBackend()), + staticRoleConfig); + + this.ldapProperties.setRole("static-role"); + this.ldapProperties.setStaticRole(true); + + Map secretProperties = this.configOperations + .read(LdapSecretBackendMetadataFactory.forLdap(this.ldapProperties)) + .getData(); + + assertThat(secretProperties).containsKeys("spring.ldap.username", "spring.ldap.password"); + } + +} diff --git a/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapServerExtension.java b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapServerExtension.java new file mode 100644 index 00000000..ee87fbce --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapServerExtension.java @@ -0,0 +1,82 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.cloud.vault.config.ldap; + +import com.unboundid.ldap.listener.InMemoryDirectoryServer; +import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig; +import com.unboundid.ldap.listener.InMemoryListenerConfig; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +/** + * JUnit 5 extension for managing an in-memory UnboundID LDAP server for testing. + * + * @author Drew Mullen + */ +public class LdapServerExtension implements BeforeAllCallback, AfterAllCallback { + + private InMemoryDirectoryServer ldapServer; + + private int ldapPort = 10389; + + private String baseDn = "dc=springframework,dc=org"; + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(this.baseDn); + + config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", this.ldapPort)); + + this.ldapServer = new InMemoryDirectoryServer(config); + this.ldapServer.add("dn: " + this.baseDn, "objectClass: top", "objectClass: domain", "dc: springframework"); + this.ldapServer.add("dn: ou=people," + this.baseDn, "objectClass: organizationalUnit", "ou: people"); + this.ldapServer.add("dn: uid=static-user,ou=people," + this.baseDn, "objectClass: inetOrgPerson", + "objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Static User", + "sn: User", "uid: static-user", "userPassword: initial-password"); + + this.ldapServer.add("dn: uid=vault-admin,ou=people," + this.baseDn, "objectClass: inetOrgPerson", + "objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Vault Admin", + "sn: Admin", "uid: vault-admin", "userPassword: vault-admin-password"); + + this.ldapServer.startListening(); + } + + @Override + public void afterAll(ExtensionContext context) { + if (this.ldapServer != null) { + this.ldapServer.shutDown(true); + } + } + + public int getLdapPort() { + return this.ldapPort; + } + + public String getBaseDn() { + return this.baseDn; + } + + public String getLdapUrl() { + return "ldap://localhost:" + this.ldapPort; + } + + public InMemoryDirectoryServer getLdapServer() { + return this.ldapServer; + } + +} diff --git a/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfigurationTests.java b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfigurationTests.java new file mode 100644 index 00000000..91c0deac --- /dev/null +++ b/spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/VaultConfigLdapBootstrapConfigurationTests.java @@ -0,0 +1,130 @@ +/* + * Copyright 2016-present the original author or authors. + * + * Licensed 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 + * + * https://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.springframework.cloud.vault.config.ldap; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.cloud.vault.config.SecretBackendMetadata; +import org.springframework.cloud.vault.config.ldap.VaultConfigLdapBootstrapConfiguration.LdapSecretBackendMetadataFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link VaultConfigLdapBootstrapConfiguration}. + * + * @author Drew Mullen + */ +public class VaultConfigLdapBootstrapConfigurationTests { + + private LdapSecretBackendMetadataFactory factory = new LdapSecretBackendMetadataFactory(); + + private VaultLdapProperties properties = new VaultLdapProperties(); + + @Test + public void shouldCreateDynamicRoleMetadata() { + + this.properties.setEnabled(true); + this.properties.setRole("my-role"); + this.properties.setStaticRole(false); + + SecretBackendMetadata metadata = this.factory.createMetadata(this.properties); + + assertThat(metadata.getName()).isEqualTo("ldap with Role my-role"); + assertThat(metadata.getPath()).isEqualTo("ldap/creds/my-role"); + assertThat(metadata.getVariables()).containsEntry("backend", "ldap").containsEntry("key", "creds/my-role"); + } + + @Test + public void shouldCreateStaticRoleMetadata() { + + this.properties.setEnabled(true); + this.properties.setRole("my-static-role"); + this.properties.setStaticRole(true); + + SecretBackendMetadata metadata = this.factory.createMetadata(this.properties); + + assertThat(metadata.getName()).isEqualTo("ldap with Role my-static-role"); + assertThat(metadata.getPath()).isEqualTo("ldap/static-cred/my-static-role"); + assertThat(metadata.getVariables()).containsEntry("backend", "ldap") + .containsEntry("key", "static-cred/my-static-role"); + } + + @Test + public void shouldCreateMetadataWithCustomBackend() { + + this.properties.setEnabled(true); + this.properties.setRole("my-role"); + this.properties.setBackend("custom-ldap"); + + SecretBackendMetadata metadata = this.factory.createMetadata(this.properties); + + assertThat(metadata.getName()).isEqualTo("custom-ldap with Role my-role"); + assertThat(metadata.getPath()).isEqualTo("custom-ldap/creds/my-role"); + assertThat(metadata.getVariables()).containsEntry("backend", "custom-ldap") + .containsEntry("key", "creds/my-role"); + } + + @Test + public void shouldTransformProperties() { + + this.properties.setEnabled(true); + this.properties.setRole("my-role"); + + SecretBackendMetadata metadata = this.factory.createMetadata(this.properties); + + Map input = new HashMap<>(); + input.put("username", "test-user"); + input.put("password", "test-pass"); + + Map transformed = metadata.getPropertyTransformer().transformProperties(input); + + assertThat(transformed).containsEntry("spring.ldap.username", "test-user") + .containsEntry("spring.ldap.password", "test-pass"); + } + + @Test + public void shouldTransformPropertiesWithCustomPropertyNames() { + + this.properties.setEnabled(true); + this.properties.setRole("my-role"); + this.properties.setUsernameProperty("custom.username"); + this.properties.setPasswordProperty("custom.password"); + + SecretBackendMetadata metadata = this.factory.createMetadata(this.properties); + + Map input = new HashMap<>(); + input.put("username", "test-user"); + input.put("password", "test-pass"); + + Map transformed = metadata.getPropertyTransformer().transformProperties(input); + + assertThat(transformed).containsEntry("custom.username", "test-user") + .containsEntry("custom.password", "test-pass"); + } + + @Test + public void shouldSupportVaultLdapProperties() { + + VaultLdapProperties properties = new VaultLdapProperties(); + + assertThat(this.factory.supports(properties)).isTrue(); + } + +}