Introduce VaultWrappingTemplate to abstract wrapping operations.
We now provide a Template API to interact with Vault wrapping endpoints introduced in Vault 0.6.2. Object body = …; WrappedMetadata metadata = wrappingOperations.wrap(body, Duration.ofSeconds(100)); VaultResponse response = wrappingOperations.read(metadata.getToken()); Closes gh-208.
This commit is contained in:
@@ -102,6 +102,13 @@ public interface VaultOperations {
|
||||
*/
|
||||
VaultTransitOperations opsForTransit(String path);
|
||||
|
||||
/**
|
||||
* @return the operations interface to interact with the Vault system/wrapping
|
||||
* endpoints.
|
||||
* @since 2.1
|
||||
*/
|
||||
VaultWrappingOperations opsForWrapping();
|
||||
|
||||
/**
|
||||
* Read from a Vault path. Reading data using this method is suitable for API
|
||||
* calls/secret backends that do not require a request body.
|
||||
|
||||
@@ -256,6 +256,11 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
|
||||
return new VaultTransitTemplate(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultWrappingOperations opsForWrapping() {
|
||||
return new VaultWrappingTemplate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaultResponse read(String path) {
|
||||
|
||||
@@ -267,9 +272,9 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@Nullable
|
||||
public <T> VaultResponseSupport<T> read(final String path, final Class<T> responseType) {
|
||||
public <T> VaultResponseSupport<T> read(String path, Class<T> responseType) {
|
||||
|
||||
final ParameterizedTypeReference<VaultResponseSupport<T>> ref = VaultResponses
|
||||
ParameterizedTypeReference<VaultResponseSupport<T>> ref = VaultResponses
|
||||
.getTypeReference(responseType);
|
||||
|
||||
try {
|
||||
@@ -320,7 +325,7 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(final String path) {
|
||||
public void delete(String path) {
|
||||
|
||||
Assert.hasText(path, "Path must not be empty");
|
||||
|
||||
@@ -364,7 +369,7 @@ public class VaultTemplate implements InitializingBean, VaultOperations, Disposa
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private <T> T doRead(final String path, final Class<T> responseType) {
|
||||
private <T> T doRead(String path, Class<T> responseType) {
|
||||
|
||||
return doWithSession(restOperations -> {
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.vault.core;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultResponseSupport;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.vault.support.WrappedMetadata;
|
||||
|
||||
/**
|
||||
* Interface that specifies wrapping-related operations.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public interface VaultWrappingOperations {
|
||||
|
||||
/**
|
||||
* Looks up {@link WrappedMetadata metadata} for the given token containing a wrapped
|
||||
* response.
|
||||
*
|
||||
* @param token must not be {@literal null}.
|
||||
* @return the {@link WrappedMetadata} the {@code token} or {@literal null} if the
|
||||
* token was invalid or expired.
|
||||
*/
|
||||
@Nullable
|
||||
WrappedMetadata lookup(VaultToken token);
|
||||
|
||||
/**
|
||||
* Read a wrapped secret.
|
||||
*
|
||||
* @param token must not be {@literal null}.
|
||||
* @return the data or {@literal null} if the token was invalid or expired.
|
||||
*/
|
||||
@Nullable
|
||||
VaultResponse read(VaultToken token);
|
||||
|
||||
/**
|
||||
* Read a wrapped secret of type {@link Class responseType}.
|
||||
*
|
||||
* @param token must not be {@literal null}.
|
||||
* @param responseType must not be {@literal null}.
|
||||
* @return the data or {@literal null} if the token was invalid or expired.
|
||||
*/
|
||||
@Nullable
|
||||
<T> VaultResponseSupport<T> read(VaultToken token, Class<T> responseType);
|
||||
|
||||
/**
|
||||
* Rewraps a response-wrapped token. The new token will use the same creation TTL as
|
||||
* the original token and contain the same response. The old token will be
|
||||
* invalidated. This can be used for long-term storage of a secret in a
|
||||
* response-wrapped token when rotation is a requirement. Rewrapping with an invalid
|
||||
* token throws {@link VaultException}.
|
||||
*
|
||||
* @param token must not be {@literal null}.
|
||||
* @return the {@link WrappedMetadata} for this wrapping operation.
|
||||
*/
|
||||
WrappedMetadata rewrap(VaultToken token);
|
||||
|
||||
/**
|
||||
* Wraps the given user-supplied data inside a response-wrapped token.
|
||||
*
|
||||
* @param body must not be {@literal null}.
|
||||
* @param ttl must not be {@literal null}.
|
||||
* @return the {@link WrappedMetadata} for this wrapping operation.
|
||||
*/
|
||||
WrappedMetadata wrap(Object body, Duration ttl);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.vault.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.client.VaultHttpHeaders;
|
||||
import org.springframework.vault.client.VaultResponses;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultResponseSupport;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.vault.support.WrappedMetadata;
|
||||
import org.springframework.web.client.HttpStatusCodeException;
|
||||
|
||||
/**
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
public class VaultWrappingTemplate implements VaultWrappingOperations {
|
||||
|
||||
private final VaultOperations vaultOperations;
|
||||
|
||||
/**
|
||||
* Create a new {@link VaultWrappingTemplate} given {@link VaultOperations}.
|
||||
*
|
||||
* @param vaultOperations must not be {@literal null}.
|
||||
*/
|
||||
public VaultWrappingTemplate(VaultOperations vaultOperations) {
|
||||
|
||||
Assert.notNull(vaultOperations, "VaultOperations must not be null");
|
||||
|
||||
this.vaultOperations = vaultOperations;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public WrappedMetadata lookup(VaultToken token) {
|
||||
|
||||
Assert.notNull(token, "token VaultToken not be null");
|
||||
|
||||
VaultResponse response = null;
|
||||
try {
|
||||
response = vaultOperations.write("sys/wrapping/lookup",
|
||||
Collections.singletonMap("token", token.getToken()));
|
||||
}
|
||||
catch (VaultException e) {
|
||||
|
||||
if (e.getMessage() != null && e.getMessage().contains("does not exist")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return getWrappedMetadata(response.getData(), token);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public VaultResponse read(VaultToken token) {
|
||||
|
||||
return vaultOperations.doWithVault(restOperations -> {
|
||||
|
||||
HttpHeaders headers = VaultHttpHeaders.from(token);
|
||||
try {
|
||||
return restOperations.exchange("sys/wrapping/unwrap", HttpMethod.POST,
|
||||
new HttpEntity<>(headers), VaultResponse.class).getBody();
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
|
||||
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (e.getStatusCode() == HttpStatus.BAD_REQUEST
|
||||
&& e.getResponseBodyAsString().contains("does not exist")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw VaultResponses.buildException(e, "sys/wrapping/unwrap");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T> VaultResponseSupport<T> read(VaultToken token, Class<T> responseType) {
|
||||
|
||||
ParameterizedTypeReference<VaultResponseSupport<T>> ref = VaultResponses
|
||||
.getTypeReference(responseType);
|
||||
|
||||
return vaultOperations.doWithVault(restOperations -> {
|
||||
|
||||
HttpHeaders headers = VaultHttpHeaders.from(token);
|
||||
try {
|
||||
return restOperations.exchange("sys/wrapping/unwrap", HttpMethod.POST,
|
||||
new HttpEntity<>(headers), ref).getBody();
|
||||
}
|
||||
catch (HttpStatusCodeException e) {
|
||||
|
||||
if (e.getStatusCode() == HttpStatus.NOT_FOUND) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (e.getStatusCode() == HttpStatus.BAD_REQUEST
|
||||
&& e.getResponseBodyAsString().contains("does not exist")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw VaultResponses.buildException(e, "sys/wrapping/unwrap");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedMetadata rewrap(VaultToken token) {
|
||||
|
||||
Assert.notNull(token, "token VaultToken not be null");
|
||||
|
||||
VaultResponse response = vaultOperations.write("sys/wrapping/rewrap",
|
||||
Collections.singletonMap("token", token.getToken()));
|
||||
|
||||
Map<String, String> wrapInfo = response.getWrapInfo();
|
||||
|
||||
return getWrappedMetadata(wrapInfo, VaultToken.of(wrapInfo.get("token")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public WrappedMetadata wrap(Object body, Duration duration) {
|
||||
|
||||
Assert.notNull(body, "Body must not be null");
|
||||
Assert.notNull(duration, "TTL duration must not be null");
|
||||
|
||||
VaultResponse response = vaultOperations.doWithSession(restOperations -> {
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.add("X-Vault-Wrap-TTL", Long.toString(duration.getSeconds()));
|
||||
|
||||
return restOperations.exchange("sys/wrapping/wrap", HttpMethod.POST,
|
||||
new HttpEntity<>(body, headers), VaultResponse.class).getBody();
|
||||
});
|
||||
|
||||
Map<String, String> wrapInfo = response.getWrapInfo();
|
||||
|
||||
return getWrappedMetadata(wrapInfo, VaultToken.of(wrapInfo.get("token")));
|
||||
}
|
||||
|
||||
private static WrappedMetadata getWrappedMetadata(Map<String, ?> wrapInfo,
|
||||
VaultToken token) {
|
||||
|
||||
TemporalAccessor creation_time = getDate(wrapInfo, "creation_time");
|
||||
String path = (String) wrapInfo.get("creation_path");
|
||||
Duration ttl = getTtl(wrapInfo);
|
||||
|
||||
return new WrappedMetadata(token, ttl, Instant.from(creation_time), path);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TemporalAccessor getDate(Map<String, ?> responseMetadata, String key) {
|
||||
|
||||
String date = (String) ((Map) responseMetadata).getOrDefault(key, "");
|
||||
|
||||
return StringUtils.hasText(date) ? DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
||||
.parse(date) : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Duration getTtl(Map<String, ?> wrapInfo) {
|
||||
|
||||
Object creationTtl = wrapInfo.get("ttl");
|
||||
|
||||
if (creationTtl == null) {
|
||||
creationTtl = wrapInfo.get("creation_ttl");
|
||||
}
|
||||
|
||||
if (creationTtl instanceof String) {
|
||||
creationTtl = Integer.parseInt((String) creationTtl);
|
||||
}
|
||||
|
||||
Duration ttl = null;
|
||||
|
||||
if (creationTtl instanceof Integer) {
|
||||
ttl = Duration.ofSeconds((Integer) creationTtl);
|
||||
|
||||
}
|
||||
return ttl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.vault.support;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Value object representing wrapped secret metadata.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @since 2.1
|
||||
*/
|
||||
public class WrappedMetadata {
|
||||
|
||||
private final VaultToken token;
|
||||
|
||||
private final Instant creationTime;
|
||||
|
||||
private final String path;
|
||||
|
||||
private final Duration ttl;
|
||||
|
||||
/**
|
||||
* Creates a new {@link WrappedMetadata}.
|
||||
* @param token must not be {@literal null}.
|
||||
* @param ttl must not be {@literal null}.
|
||||
* @param creationTime must not be {@literal null}.
|
||||
* @param path must not be {@literal null}.
|
||||
*/
|
||||
public WrappedMetadata(VaultToken token, Duration ttl, Instant creationTime,
|
||||
String path) {
|
||||
|
||||
Assert.notNull(token, "VaultToken must not be null");
|
||||
Assert.notNull(ttl, "TTL duration must not be null");
|
||||
Assert.notNull(creationTime, "Creation time must not be null");
|
||||
Assert.notNull(path, "Path must not be null");
|
||||
|
||||
this.token = token;
|
||||
this.creationTime = creationTime;
|
||||
this.path = path;
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
public VaultToken getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public Instant getCreationTime() {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Duration getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2018 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
|
||||
*
|
||||
* http://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.vault.core;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
import org.springframework.vault.VaultException;
|
||||
import org.springframework.vault.support.VaultResponse;
|
||||
import org.springframework.vault.support.VaultResponseSupport;
|
||||
import org.springframework.vault.support.VaultToken;
|
||||
import org.springframework.vault.support.WrappedMetadata;
|
||||
import org.springframework.vault.util.IntegrationTestSupport;
|
||||
import org.springframework.vault.util.Version;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link VaultWrappingTemplate} through
|
||||
* {@link VaultWrappingOperations}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class)
|
||||
public class VaultWrappingTemplateIntegrationTests extends IntegrationTestSupport {
|
||||
|
||||
private static final Version WRAPPING_ENDPOINT_INTRODUCED_IN_VERSION = Version
|
||||
.parse("0.6.2");
|
||||
|
||||
@Autowired
|
||||
private VaultOperations vaultOperations;
|
||||
|
||||
private VaultWrappingOperations wrappingOperations;
|
||||
|
||||
private Version vaultVersion;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
|
||||
wrappingOperations = vaultOperations.opsForWrapping();
|
||||
|
||||
vaultVersion = prepare().getVersion();
|
||||
|
||||
assumeTrue(vaultVersion
|
||||
.isGreaterThanOrEqualTo(WRAPPING_ENDPOINT_INTRODUCED_IN_VERSION));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateWrappedSecret() {
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
WrappedMetadata metadata = wrappingOperations.wrap(map, Duration.ofSeconds(100));
|
||||
|
||||
assertThat(metadata.getPath()).isEqualTo("sys/wrapping/wrap");
|
||||
assertThat(metadata.getTtl()).isEqualTo(Duration.ofSeconds(100));
|
||||
assertThat(metadata.getToken()).isNotNull();
|
||||
assertThat(metadata.getCreationTime()).isBefore(Instant.now().plusSeconds(60))
|
||||
.isAfter(Instant.now().minusSeconds(60));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLookupWrappedSecret() {
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
WrappedMetadata metadata = wrappingOperations.wrap(map, Duration.ofSeconds(100));
|
||||
|
||||
WrappedMetadata lookup = wrappingOperations.lookup(metadata.getToken());
|
||||
|
||||
assertThat(lookup.getPath()).isEqualTo("sys/wrapping/wrap");
|
||||
assertThat(lookup.getTtl()).isEqualTo(Duration.ofSeconds(100));
|
||||
assertThat(lookup.getToken()).isNotNull();
|
||||
assertThat(lookup.getCreationTime()).isBefore(Instant.now().plusSeconds(60))
|
||||
.isAfter(Instant.now().minusSeconds(60));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadWrappedSecret() {
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
WrappedMetadata metadata = wrappingOperations.wrap(map, Duration.ofSeconds(100));
|
||||
VaultResponse response = wrappingOperations.read(metadata.getToken());
|
||||
|
||||
assertThat(response.getData())
|
||||
.isEqualTo(Collections.singletonMap("key", "value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReadWrappedTypedSecret() {
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
WrappedMetadata metadata = wrappingOperations.wrap(map, Duration.ofSeconds(100));
|
||||
VaultResponseSupport<Secret> response = wrappingOperations.read(
|
||||
metadata.getToken(), Secret.class);
|
||||
|
||||
assertThat(response.getData()).isEqualTo(new Secret("value"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnNullForNonExistentSecret() {
|
||||
|
||||
assertThat(wrappingOperations.read(VaultToken.of("foo"))).isNull();
|
||||
assertThat(wrappingOperations.read(VaultToken.of("foo"), Map.class)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLookupAbsentSecret() {
|
||||
|
||||
WrappedMetadata lookup = wrappingOperations.lookup(VaultToken.of("foo"));
|
||||
|
||||
assertThat(lookup).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldRewrapSecret() {
|
||||
|
||||
Map<String, String> map = Collections.singletonMap("key", "value");
|
||||
|
||||
WrappedMetadata metadata = wrappingOperations.wrap(map, Duration.ofSeconds(100));
|
||||
|
||||
WrappedMetadata rewrap = wrappingOperations.rewrap(metadata.getToken());
|
||||
|
||||
assertThat(rewrap.getPath()).isEqualTo("sys/wrapping/wrap");
|
||||
assertThat(rewrap.getTtl()).isEqualTo(Duration.ofSeconds(100));
|
||||
assertThat(rewrap.getToken()).isNotEqualTo(metadata.getToken());
|
||||
assertThat(rewrap.getCreationTime()).isBefore(Instant.now().plusSeconds(60))
|
||||
.isAfter(Instant.now().minusSeconds(60));
|
||||
}
|
||||
|
||||
@Test(expected = VaultException.class)
|
||||
public void shouldRewrapAbsentSecret() {
|
||||
wrappingOperations.rewrap(VaultToken.of("foo"));
|
||||
}
|
||||
|
||||
@Value
|
||||
@EqualsAndHashCode
|
||||
static class Secret {
|
||||
final String key;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user