Skip to content

Commit fdcd4a7

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

3 files changed

Lines changed: 149 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: 53 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,64 +16,87 @@
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+
// Mount the LDAP secret engine if not already mounted
64+
if (!prepare().hasSecretBackend(this.ldapProperties.getBackend())) {
65+
prepare().mountSecret(this.ldapProperties.getBackend());
66+
}
67+
68+
// Configure Vault LDAP backend to connect to the in-memory LDAP server
69+
Map<String, Object> ldapConfig = new HashMap<>();
70+
ldapConfig.put("binddn", "uid=vault-admin,ou=people," + ldapServer.getBaseDn());
71+
ldapConfig.put("bindpass", "vault-admin-password");
72+
ldapConfig.put("url", ldapServer.getLdapUrl());
73+
ldapConfig.put("userdn", "ou=people," + ldapServer.getBaseDn());
74+
75+
vaultOperations.write(String.format("%s/config", this.ldapProperties.getBackend()), ldapConfig);
76+
77+
this.configOperations = new VaultConfigTemplate(vaultOperations, this.vaultProperties);
6778
}
6879

6980
/**
70-
* Test for dynamic role credential retrieval. This test requires a configured dynamic
71-
* role in Vault's LDAP secret engine.
81+
* Test for dynamic role credential retrieval.
7282
*/
7383
@Test
7484
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");
85+
VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations();
86+
87+
// Configure a dynamic role
88+
Map<String, Object> dynamicRoleConfig = new HashMap<>();
89+
dynamicRoleConfig.put("creation_ldif",
90+
"dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\n" + "objectClass: person\n"
91+
+ "objectClass: top\n" + "cn: {{.Username}}\n" + "sn: {{.Username}}\n"
92+
+ "userPassword: {{.Password}}");
93+
dynamicRoleConfig.put("deletion_ldif",
94+
"dn: cn={{.Username}},ou=people," + ldapServer.getBaseDn() + "\nchangetype: delete");
95+
dynamicRoleConfig.put("default_ttl", "1h");
96+
dynamicRoleConfig.put("max_ttl", "24h");
97+
98+
vaultOperations.write(String.format("%s/role/dynamic-role", this.ldapProperties.getBackend()),
99+
dynamicRoleConfig);
77100

78101
this.ldapProperties.setRole("dynamic-role");
79102
this.ldapProperties.setStaticRole(false);
@@ -86,13 +109,20 @@ public void shouldCreateDynamicCredentialsCorrectly() {
86109
}
87110

88111
/**
89-
* Test for static role credential retrieval. This test requires a configured static
90-
* role in Vault's LDAP secret engine.
112+
* Test for static role credential retrieval.
91113
*/
92114
@Test
93115
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");
116+
VaultOperations vaultOperations = this.vaultRule.prepare().getVaultOperations();
117+
118+
// Configure a static role
119+
Map<String, Object> staticRoleConfig = new HashMap<>();
120+
staticRoleConfig.put("dn", "uid=static-user,ou=people," + ldapServer.getBaseDn());
121+
staticRoleConfig.put("username", "static-user");
122+
staticRoleConfig.put("rotation_period", "24h");
123+
124+
vaultOperations.write(String.format("%s/static-role/static-role", this.ldapProperties.getBackend()),
125+
staticRoleConfig);
96126

97127
this.ldapProperties.setRole("static-role");
98128
this.ldapProperties.setStaticRole(true);
@@ -104,44 +134,4 @@ public void shouldCreateStaticCredentialsCorrectly() {
104134
assertThat(secretProperties).containsKeys("spring.ldap.username", "spring.ldap.password");
105135
}
106136

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-
147137
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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+
// Configure the listener with the specific port
44+
config.setListenerConfigs(InMemoryListenerConfig.createLDAPConfig("default", this.ldapPort));
45+
46+
// Create the LDAP server
47+
this.ldapServer = new InMemoryDirectoryServer(config);
48+
49+
// Add base entries
50+
this.ldapServer.add("dn: " + this.baseDn, "objectClass: top", "objectClass: domain", "dc: springframework");
51+
52+
this.ldapServer.add("dn: ou=people," + this.baseDn, "objectClass: organizationalUnit", "ou: people");
53+
54+
// Add test users for static and dynamic role testing
55+
this.ldapServer.add("dn: uid=static-user,ou=people," + this.baseDn, "objectClass: inetOrgPerson",
56+
"objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Static User",
57+
"sn: User", "uid: static-user", "userPassword: initial-password");
58+
59+
this.ldapServer.add("dn: uid=vault-admin,ou=people," + this.baseDn, "objectClass: inetOrgPerson",
60+
"objectClass: organizationalPerson", "objectClass: person", "objectClass: top", "cn: Vault Admin",
61+
"sn: Admin", "uid: vault-admin", "userPassword: vault-admin-password");
62+
63+
// Start the server
64+
this.ldapServer.startListening();
65+
}
66+
67+
@Override
68+
public void afterAll(ExtensionContext context) {
69+
if (this.ldapServer != null) {
70+
this.ldapServer.shutDown(true);
71+
}
72+
}
73+
74+
public int getLdapPort() {
75+
return this.ldapPort;
76+
}
77+
78+
public String getBaseDn() {
79+
return this.baseDn;
80+
}
81+
82+
public String getLdapUrl() {
83+
return "ldap://localhost:" + this.ldapPort;
84+
}
85+
86+
public InMemoryDirectoryServer getLdapServer() {
87+
return this.ldapServer;
88+
}
89+
90+
}

0 commit comments

Comments
 (0)