Skip to content

Commit

Permalink
fix: (hashicorp-vault) token self-lookup path as segments (#4512)
Browse files Browse the repository at this point in the history
* #4508 interpret token self-lookup path as several segments, not a single segment which encodes the path slashes.

* feature: introduce/clone integration test to run against hashicorps BS-Licencesed product.

* fix: stay with static container/test initialization not to risk multiple vault instances. Courtesy of @paullatzelsperger.

* chore: update DEPs
  • Loading branch information
drcgjung authored Oct 9, 2024
1 parent d6721f5 commit 84917d2
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 90 deletions.
3 changes: 2 additions & 1 deletion DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ maven/mavencentral/org.jetbrains.kotlin/kotlin-stdlib/1.9.10, Apache-2.0, approv
maven/mavencentral/org.jetbrains/annotations/13.0, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.jetbrains/annotations/17.0.0, Apache-2.0, approved, clearlydefined
maven/mavencentral/org.jetbrains/annotations/25.0.0, , restricted, clearlydefined
maven/mavencentral/org.jetbrains/annotations/26.0.0, , restricted, clearlydefined
maven/mavencentral/org.junit-pioneer/junit-pioneer/2.2.0, EPL-2.0, approved, #11857
maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.11.1, EPL-2.0, approved, #15935
maven/mavencentral/org.junit.jupiter/junit-jupiter-api/5.11.2, EPL-2.0, approved, #15935
Expand Down Expand Up @@ -347,7 +348,7 @@ maven/mavencentral/org.slf4j/slf4j-api/2.0.6, MIT, approved, #5915
maven/mavencentral/org.slf4j/slf4j-api/2.0.9, MIT, approved, #5915
maven/mavencentral/org.testcontainers/database-commons/1.20.2, , restricted, clearlydefined
maven/mavencentral/org.testcontainers/jdbc/1.20.2, , restricted, clearlydefined
maven/mavencentral/org.testcontainers/junit-jupiter/1.20.2, , restricted, clearlydefined
maven/mavencentral/org.testcontainers/junit-jupiter/1.20.2, None, restricted, #16552
maven/mavencentral/org.testcontainers/kafka/1.20.2, , restricted, clearlydefined
maven/mavencentral/org.testcontainers/postgresql/1.20.2, , restricted, clearlydefined
maven/mavencentral/org.testcontainers/testcontainers/1.20.2, MIT, approved, #15747
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public Result<Void> doHealthCheck() {
public Result<Boolean> isTokenRenewable() {
var uri = settings.url()
.newBuilder()
.addPathSegment(TOKEN_LOOK_UP_SELF_PATH)
.addPathSegments(TOKEN_LOOK_UP_SELF_PATH)
.build();
var request = httpGet(uri);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.edc.junit.annotations.ComponentTest;
import org.eclipse.edc.spi.monitor.ConsoleMonitor;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
Expand All @@ -35,98 +36,158 @@
import static org.eclipse.edc.junit.assertions.AbstractResultAssert.assertThat;
import static org.testcontainers.shaded.org.awaitility.Awaitility.await;

@ComponentTest
@Testcontainers
class HashicorpVaultClientIntegrationTest {
@Container
static final VaultContainer<?> VAULT_CONTAINER = new VaultContainer<>("vault:1.9.6")
.withVaultToken(UUID.randomUUID().toString());

private static final String HTTP_URL_FORMAT = "http://%s:%s";
private static final String HEALTH_CHECK_PATH = "/health/path";
private static final String CLIENT_TOKEN_KEY = "client_token";
private static final String AUTH_KEY = "auth";
private static final long CREATION_TTL = 6L;
private static final long TTL = 5L;
private static final long RENEW_BUFFER = 4L;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final ConsoleMonitor MONITOR = new ConsoleMonitor();

private static HashicorpVaultClient client;

@BeforeEach
void beforeEach() throws IOException, InterruptedException {
assertThat(CREATION_TTL).isGreaterThan(TTL);
client = new HashicorpVaultClient(
testHttpClient(),
OBJECT_MAPPER,
MONITOR,
getSettings()
);
}

@Test
void lookUpToken_whenTokenNotExpired_shouldSucceed() {
var tokenLookUpResult = client.isTokenRenewable();

assertThat(tokenLookUpResult).isSucceeded().isEqualTo(true);
}

@Test
void lookUpToken_whenTokenExpired_shouldFail() {
await()
.pollDelay(CREATION_TTL, TimeUnit.SECONDS)
.atMost(CREATION_TTL + 1, TimeUnit.SECONDS)
.untilAsserted(() -> {
var tokenLookUpResult = client.isTokenRenewable();
assertThat(tokenLookUpResult).isFailed();
assertThat(tokenLookUpResult.getFailureDetail()).isEqualTo("Token look up failed with status 403");
});
}

@Test
void renewToken_whenTokenNotExpired_shouldSucceed() {
var tokenRenewResult = client.renewToken();
class HashicorpVaultClientIntegrationTest {

assertThat(tokenRenewResult).isSucceeded().satisfies(ttl -> assertThat(ttl).isEqualTo(TTL));
@ComponentTest
@Testcontainers
@Nested
abstract static class Tests {

protected static final String HTTP_URL_FORMAT = "http://%s:%s";
protected static final String HEALTH_CHECK_PATH = "/health/path";
protected static final String CLIENT_TOKEN_KEY = "client_token";
protected static final String AUTH_KEY = "auth";
protected static final long CREATION_TTL = 6L;
protected static final long TTL = 5L;
protected static final long RENEW_BUFFER = 4L;
protected HashicorpVaultClient client;
protected final ObjectMapper mapper = new ObjectMapper();
protected final ConsoleMonitor monitor = new ConsoleMonitor();

@BeforeEach
void beforeEach() throws IOException, InterruptedException {
assertThat(CREATION_TTL).isGreaterThan(TTL);
}

@Test
void lookUpToken_whenTokenNotExpired_shouldSucceed() {
var tokenLookUpResult = client.isTokenRenewable();

assertThat(tokenLookUpResult).isSucceeded().isEqualTo(true);
}

@Test
void lookUpToken_whenTokenExpired_shouldFail() {
await()
.pollDelay(CREATION_TTL, TimeUnit.SECONDS)
.atMost(CREATION_TTL + 1, TimeUnit.SECONDS)
.untilAsserted(() -> {
var tokenLookUpResult = client.isTokenRenewable();
assertThat(tokenLookUpResult).isFailed();
assertThat(tokenLookUpResult.getFailureDetail()).isEqualTo("Token look up failed with status 403");
});
}

@Test
void renewToken_whenTokenNotExpired_shouldSucceed() {
var tokenRenewResult = client.renewToken();

assertThat(tokenRenewResult).isSucceeded().satisfies(ttl -> assertThat(ttl).isEqualTo(TTL));
}

@Test
void renewToken_whenTokenExpired_shouldFail() {
await()
.pollDelay(CREATION_TTL, TimeUnit.SECONDS)
.atMost(CREATION_TTL + 1, TimeUnit.SECONDS)
.untilAsserted(() -> {
var tokenRenewResult = client.renewToken();
assertThat(tokenRenewResult).isFailed();
assertThat(tokenRenewResult.getFailureDetail()).isEqualTo("Token renew failed with status: 403");
});
}
}

@Test
void renewToken_whenTokenExpired_shouldFail() {
await()
.pollDelay(CREATION_TTL, TimeUnit.SECONDS)
.atMost(CREATION_TTL + 1, TimeUnit.SECONDS)
.untilAsserted(() -> {
var tokenRenewResult = client.renewToken();
assertThat(tokenRenewResult).isFailed();
assertThat(tokenRenewResult.getFailureDetail()).isEqualTo("Token renew failed with status: 403");
});
@ComponentTest
@Testcontainers
@Nested
class LastKnownFoss extends Tests {
@Container
static final VaultContainer<?> VAULT_CONTAINER = new VaultContainer<>("vault:1.9.6")
.withVaultToken(UUID.randomUUID().toString());

public static HashicorpVaultSettings getSettings() throws IOException, InterruptedException {
var execResult = VAULT_CONTAINER.execInContainer(
"vault",
"token",
"create",
"-policy=root",
"-ttl=%d".formatted(CREATION_TTL),
"-format=json");

var jsonParser = Json.createParser(new StringReader(execResult.getStdout()));
jsonParser.next();
var auth = jsonParser.getObjectStream().filter(e -> e.getKey().equals(AUTH_KEY))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow()
.asJsonObject();
var clientToken = auth.getString(CLIENT_TOKEN_KEY);

return HashicorpVaultSettings.Builder.newInstance()
.url(HTTP_URL_FORMAT.formatted(VAULT_CONTAINER.getHost(), VAULT_CONTAINER.getFirstMappedPort()))
.healthCheckPath(HEALTH_CHECK_PATH)
.token(clientToken)
.ttl(TTL)
.renewBuffer(RENEW_BUFFER)
.build();
}

@BeforeEach
void beforeEach() throws IOException, InterruptedException {
client = new HashicorpVaultClient(
testHttpClient(),
mapper,
monitor,
getSettings()
);
}
}

public static HashicorpVaultSettings getSettings() throws IOException, InterruptedException {
var execResult = VAULT_CONTAINER.execInContainer(
"vault",
"token",
"create",
"-policy=root",
"-ttl=%d".formatted(CREATION_TTL),
"-format=json");

var jsonParser = Json.createParser(new StringReader(execResult.getStdout()));
jsonParser.next();
var auth = jsonParser.getObjectStream().filter(e -> e.getKey().equals(AUTH_KEY))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow()
.asJsonObject();
var clientToken = auth.getString(CLIENT_TOKEN_KEY);

return HashicorpVaultSettings.Builder.newInstance()
.url(HTTP_URL_FORMAT.formatted(VAULT_CONTAINER.getHost(), VAULT_CONTAINER.getFirstMappedPort()))
.healthCheckPath(HEALTH_CHECK_PATH)
.token(clientToken)
.ttl(TTL)
.renewBuffer(RENEW_BUFFER)
.build();
@ComponentTest
@Testcontainers
@Nested
class Latest extends Tests {
@Container
static final VaultContainer<?> VAULT_CONTAINER = new VaultContainer<>("hashicorp/vault:1.17.3")
.withVaultToken(UUID.randomUUID().toString());

public static HashicorpVaultSettings getSettings() throws IOException, InterruptedException {
var execResult = VAULT_CONTAINER.execInContainer(
"vault",
"token",
"create",
"-policy=root",
"-ttl=%d".formatted(CREATION_TTL),
"-format=json");

var jsonParser = Json.createParser(new StringReader(execResult.getStdout()));
jsonParser.next();
var auth = jsonParser.getObjectStream().filter(e -> e.getKey().equals(AUTH_KEY))
.map(Map.Entry::getValue)
.findFirst()
.orElseThrow()
.asJsonObject();
var clientToken = auth.getString(CLIENT_TOKEN_KEY);

return HashicorpVaultSettings.Builder.newInstance()
.url(HTTP_URL_FORMAT.formatted(VAULT_CONTAINER.getHost(), VAULT_CONTAINER.getFirstMappedPort()))
.healthCheckPath(HEALTH_CHECK_PATH)
.token(clientToken)
.ttl(TTL)
.renewBuffer(RENEW_BUFFER)
.build();
}

@BeforeEach
void beforeEach() throws IOException, InterruptedException {
client = new HashicorpVaultClient(
testHttpClient(),
mapper,
monitor,
getSettings()
);
}
}
}
}

0 comments on commit 84917d2

Please sign in to comment.