Skip to content

Commit 477fbf4

Browse files
committed
include unboundid ldap in integration tests
Signed-off-by: Drew Mullen <drew.mullen@hashicorp.com>
1 parent 049cb44 commit 477fbf4

3 files changed

Lines changed: 137 additions & 63 deletions

File tree

spring-cloud-vault-config-ldap/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@
4949
<scope>test</scope>
5050
</dependency>
5151

52+
<dependency>
53+
<groupId>com.unboundid</groupId>
54+
<artifactId>unboundid-ldapsdk</artifactId>
55+
<scope>test</scope>
56+
</dependency>
57+
5258
</dependencies>
5359

5460
</project>

spring-cloud-vault-config-ldap/src/test/java/org/springframework/cloud/vault/config/ldap/LdapSecretIntegrationTests.java

Lines changed: 49 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,64 +16,84 @@
1616

1717
package org.springframework.cloud.vault.config.ldap;
1818

19+
import java.util.HashMap;
1920
import java.util.Map;
2021

2122
import org.junit.jupiter.api.BeforeEach;
22-
import org.junit.jupiter.api.Disabled;
2323
import org.junit.jupiter.api.Test;
24+
import org.junit.jupiter.api.extension.RegisterExtension;
2425

2526
import org.springframework.cloud.vault.config.VaultConfigOperations;
2627
import org.springframework.cloud.vault.config.VaultConfigTemplate;
2728
import org.springframework.cloud.vault.config.VaultProperties;
2829
import org.springframework.cloud.vault.config.ldap.VaultConfigLdapBootstrapConfiguration.LdapSecretBackendMetadataFactory;
2930
import org.springframework.cloud.vault.util.IntegrationTestSupport;
3031
import org.springframework.cloud.vault.util.Settings;
32+
import org.springframework.vault.core.VaultOperations;
3133

3234
import static org.assertj.core.api.Assertions.assertThat;
33-
import static org.junit.jupiter.api.Assumptions.assumeTrue;
3435

3536
/**
36-
* Integration tests for {@link VaultConfigTemplate} using the LDAP secret engine. These
37-
* tests require a running LDAP instance and properly configured Vault LDAP secret engine.
38-
* <p>
39-
* Note: This test will be skipped if the LDAP secret engine is not available or not
40-
* configured in Vault.
37+
* Integration tests for {@link VaultConfigTemplate} using the LDAP secret engine with an
38+
* in-memory UnboundID LDAP server.
4139
*
4240
* @author Drew Mullen
4341
*/
44-
@Disabled
4542
public class LdapSecretIntegrationTests extends IntegrationTestSupport {
4643

44+
@RegisterExtension
45+
public static final LdapServerExtension ldapServer = new LdapServerExtension();
46+
4747
private VaultProperties vaultProperties = Settings.createVaultProperties();
4848

4949
private VaultConfigOperations configOperations;
5050

5151
private VaultLdapProperties ldapProperties = new VaultLdapProperties();
5252

5353
/**
54-
* Initialize the LDAP secret engine configuration. Tests will be skipped if the LDAP
55-
* secret engine is not available.
54+
* Initialize the LDAP secret engine configuration with the in-memory LDAP server.
5655
*/
5756
@BeforeEach
5857
public void setUp() {
59-
// Skip tests if LDAP secret engine is not mounted/available
60-
assumeTrue(isLdapSecretEngineAvailable(), "LDAP secret engine is not available or configured");
61-
6258
this.ldapProperties.setEnabled(true);
6359
this.ldapProperties.setBackend("ldap");
6460

65-
this.configOperations = new VaultConfigTemplate(this.vaultRule.prepare().getVaultOperations(),
66-
this.vaultProperties);
61+
VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations();
62+
63+
if (!prepare().hasSecretBackend(this.ldapProperties.getBackend())) {
64+
prepare().mountSecret(this.ldapProperties.getBackend());
65+
}
66+
67+
Map<String, Object> ldapConfig = new HashMap<>();
68+
ldapConfig.put("binddn", "uid=vault-admin,ou=people," + ldapServer.getBaseDn());
69+
ldapConfig.put("bindpass", "vault-admin-password");
70+
ldapConfig.put("url", ldapServer.getLdapUrl());
71+
ldapConfig.put("userdn", "ou=people," + ldapServer.getBaseDn());
72+
73+
vaultOperations.write(String.format("%s/config", this.ldapProperties.getBackend()), ldapConfig);
74+
75+
this.configOperations = new VaultConfigTemplate(vaultOperations, this.vaultProperties);
6776
}
6877

6978
/**
70-
* Test for dynamic role credential retrieval. This test requires a configured dynamic
71-
* role in Vault's LDAP secret engine.
79+
* Test for dynamic role credential retrieval.
7280
*/
7381
@Test
7482
public void shouldCreateDynamicCredentialsCorrectly() {
75-
// This test requires a pre-configured dynamic role named "dynamic-role" in Vault
76-
assumeTrue(isDynamicRoleAvailable("dynamic-role"), "Dynamic role 'dynamic-role' is not configured");
83+
VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations();
84+
85+
Map<String, Object> dynamicRoleConfig = new HashMap<>();
86+
dynamicRoleConfig.put("creation_ldif",
87+
"dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\n" + "objectClass: person\n"
88+
+ "objectClass: top\n" + "cn: {{.Username}}\n" + "sn: {{.Username}}\n"
89+
+ "userPassword: {{.Password}}");
90+
dynamicRoleConfig.put("deletion_ldif",
91+
"dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\nchangetype: delete");
92+
dynamicRoleConfig.put("default_ttl", "1h");
93+
dynamicRoleConfig.put("max_ttl", "24h");
94+
95+
vaultOperations.write(String.format("%s/role/dynamic-role", this.ldapProperties.getBackend()),
96+
dynamicRoleConfig);
7797

7898
this.ldapProperties.setRole("dynamic-role");
7999
this.ldapProperties.setStaticRole(false);
@@ -86,13 +106,19 @@ public void shouldCreateDynamicCredentialsCorrectly() {
86106
}
87107

88108
/**
89-
* Test for static role credential retrieval. This test requires a configured static
90-
* role in Vault's LDAP secret engine.
109+
* Test for static role credential retrieval.
91110
*/
92111
@Test
93112
public void shouldCreateStaticCredentialsCorrectly() {
94-
// This test requires a pre-configured static role named "static-role" in Vault
95-
assumeTrue(isStaticRoleAvailable("static-role"), "Static role 'static-role' is not configured");
113+
VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations();
114+
115+
Map<String, Object> staticRoleConfig = new HashMap<>();
116+
staticRoleConfig.put("dn", "uid=static-user,ou=people," + ldapServer.getBaseDn());
117+
staticRoleConfig.put("username", "static-user");
118+
staticRoleConfig.put("rotation_period", "24h");
119+
120+
vaultOperations.write(String.format("%s/static-role/static-role", this.ldapProperties.getBackend()),
121+
staticRoleConfig);
96122

97123
this.ldapProperties.setRole("static-role");
98124
this.ldapProperties.setStaticRole(true);
@@ -104,44 +130,4 @@ public void shouldCreateStaticCredentialsCorrectly() {
104130
assertThat(secretProperties).containsKeys("spring.ldap.username", "spring.ldap.password");
105131
}
106132

107-
/**
108-
* Check if the LDAP secret engine is available in Vault. This can be determined by
109-
* attempting to read the LDAP config endpoint.
110-
*/
111-
private boolean isLdapSecretEngineAvailable() {
112-
try {
113-
this.vaultRule.prepare().getVaultOperations().read("ldap/config");
114-
return true;
115-
}
116-
catch (Exception e) {
117-
return false;
118-
}
119-
}
120-
121-
/**
122-
* Check if a dynamic role exists in Vault.
123-
*/
124-
private boolean isDynamicRoleAvailable(String roleName) {
125-
try {
126-
this.vaultRule.prepare().getVaultOperations().read("ldap/role/" + roleName);
127-
return true;
128-
}
129-
catch (Exception e) {
130-
return false;
131-
}
132-
}
133-
134-
/**
135-
* Check if a static role exists in Vault.
136-
*/
137-
private boolean isStaticRoleAvailable(String roleName) {
138-
try {
139-
this.vaultRule.prepare().getVaultOperations().read("ldap/static-role/" + roleName);
140-
return true;
141-
}
142-
catch (Exception e) {
143-
return false;
144-
}
145-
}
146-
147133
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2016-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.vault.config.ldap;
18+
19+
import com.unboundid.ldap.listener.InMemoryDirectoryServer;
20+
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
21+
import com.unboundid.ldap.listener.InMemoryListenerConfig;
22+
import org.junit.jupiter.api.extension.AfterAllCallback;
23+
import org.junit.jupiter.api.extension.BeforeAllCallback;
24+
import org.junit.jupiter.api.extension.ExtensionContext;
25+
26+
/**
27+
* JUnit 5 extension for managing an in-memory UnboundID LDAP server for testing.
28+
*
29+
* @author Drew Mullen
30+
*/
31+
public class LdapServerExtension implements BeforeAllCallback, AfterAllCallback {
32+
33+
private InMemoryDirectoryServer ldapServer;
34+
35+
private int ldapPort = 10389;
36+
37+
private String baseDn = "dc=springframework,dc=org";
38+
39+
@Override
40+
public void beforeAll(ExtensionContext context) throws Exception {
41+
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(this.baseDn);
42+
43+
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", this.ldapPort));
44+
45+
this.ldapServer = new InMemoryDirectoryServer(config);
46+
this.ldapServer.add("dn: " + this.baseDn, "objectClass: top", "objectClass: domain", "dc: springframework");
47+
this.ldapServer.add("dn: ou=people," + this.baseDn, "objectClass: organizationalUnit", "ou: people");
48+
this.ldapServer.add("dn: uid=static-user,ou=people," + this.baseDn, "objectClass: inetOrgPerson",
49+
"objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Static User",
50+
"sn: User", "uid: static-user", "userPassword: initial-password");
51+
52+
this.ldapServer.add("dn: uid=vault-admin,ou=people," + this.baseDn, "objectClass: inetOrgPerson",
53+
"objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Vault Admin",
54+
"sn: Admin", "uid: vault-admin", "userPassword: vault-admin-password");
55+
56+
this.ldapServer.startListening();
57+
}
58+
59+
@Override
60+
public void afterAll(ExtensionContext context) {
61+
if (this.ldapServer != null) {
62+
this.ldapServer.shutDown(true);
63+
}
64+
}
65+
66+
public int getLdapPort() {
67+
return this.ldapPort;
68+
}
69+
70+
public String getBaseDn() {
71+
return this.baseDn;
72+
}
73+
74+
public String getLdapUrl() {
75+
return "ldap://localhost:" + this.ldapPort;
76+
}
77+
78+
public InMemoryDirectoryServer getLdapServer() {
79+
return this.ldapServer;
80+
}
81+
82+
}

0 commit comments

Comments
 (0)