Provide AuthenticationSteps for token authentication with self-lookup.

Closes gh-158.
This commit is contained in:
Mark Paluch
2017-11-14 15:23:28 +01:00
parent 00b6a55689
commit a1cc3a7cb2
6 changed files with 252 additions and 5 deletions

View File

@@ -131,7 +131,10 @@ public class AuthenticationStepsOperator implements VaultTokenSupplier {
String.format(
"Cannot retrieve VaultToken from authentication chain. Got instead %s",
stateObject));
});
})
.onErrorMap(
t -> new VaultException(
"Cannot retrieve VaultToken from authentication chain", t));
}
private static Object doSupplierStep(SupplierStep<Object> supplierStep) {

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.vault.authentication;
import java.time.Duration;
import java.util.Map;
import lombok.experimental.UtilityClass;
@@ -40,15 +41,35 @@ class LoginTokenUtil {
Assert.notNull(auth, "Authentication must not be null");
String token = (String) auth.get("client_token");
return from(token.toCharArray(), auth);
}
/**
* Construct a {@link LoginToken} from an auth response.
*
* @param auth {@link Map} holding a login response.
* @return the {@link LoginToken}
* @since 2.0
*/
static LoginToken from(char[] token, Map<String, Object> auth) {
Assert.notNull(auth, "Authentication must not be null");
Boolean renewable = (Boolean) auth.get("renewable");
Number leaseDuration = (Number) auth.get("lease_duration");
if (leaseDuration == null) {
leaseDuration = (Number) auth.get("ttl");
}
if (renewable != null && renewable) {
return LoginToken.renewable(token, leaseDuration.longValue());
return LoginToken.renewable(token,
Duration.ofSeconds(leaseDuration.longValue()));
}
if (leaseDuration != null) {
return LoginToken.of(token, leaseDuration.longValue());
return LoginToken.of(token, Duration.ofSeconds(leaseDuration.longValue()));
}
return LoginToken.of(token);

View File

@@ -15,9 +15,17 @@
*/
package org.springframework.vault.authentication;
import java.time.Duration;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.vault.authentication.AuthenticationSteps.HttpRequest;
import org.springframework.vault.client.VaultHttpHeaders;
import org.springframework.vault.support.VaultResponse;
import org.springframework.vault.support.VaultToken;
import static org.springframework.vault.authentication.AuthenticationSteps.HttpRequestBuilder.get;
/**
* Static Token-based {@link ClientAuthentication} method.
*
@@ -59,13 +67,27 @@ public class TokenAuthentication implements ClientAuthentication,
* {@link VaultToken}.
*
* @param token must not be {@literal null}.
* @param selfLookup {@literal true} to perform a self-lookup using the given
* {@link VaultToken}. Self-lookup will create a {@link LoginToken} and provide
* renewability and TTL.
* @return {@link AuthenticationSteps} for token authentication.
* @since 2.0
*/
public static AuthenticationSteps createAuthenticationSteps(VaultToken token) {
public static AuthenticationSteps createAuthenticationSteps(VaultToken token,
boolean selfLookup) {
Assert.notNull(token, "VaultToken must not be null");
if (selfLookup) {
HttpRequest<VaultResponse> httpRequest = get("auth/token/lookup-self").with(
VaultHttpHeaders.from(token)).as(VaultResponse.class);
return AuthenticationSteps.fromHttpRequest(httpRequest).login(
response -> LoginTokenUtil.from(token.toCharArray(),
response.getRequiredData()));
}
return AuthenticationSteps.just(token);
}
@@ -76,6 +98,10 @@ public class TokenAuthentication implements ClientAuthentication,
@Override
public AuthenticationSteps getAuthenticationSteps() {
return createAuthenticationSteps(this.token);
return createAuthenticationSteps(this.token, false);
}
private static Duration getLeaseDuration(@Nullable Number ttl) {
return ttl == null ? Duration.ZERO : Duration.ofSeconds(ttl.longValue());
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2017 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.authentication;
import org.springframework.vault.util.IntegrationTestSupport;
/**
* Integration test base class for {@link TokenAuthentication} tests.
*
* @author Mark Paluch
*/
public abstract class TokenAuthenticationIntegrationTestBase extends
IntegrationTestSupport {
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2017 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.authentication;
import java.time.Duration;
import org.junit.Test;
import reactor.test.StepVerifier;
import org.springframework.vault.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.util.Settings;
import org.springframework.vault.util.TestWebClientFactory;
import org.springframework.web.reactive.function.client.WebClient;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link TokenAuthentication} using
* {@link AuthenticationStepsOperator}.
*
* @author Mark Paluch
*/
public class TokenAuthenticationOperatorIntegrationTests extends
TokenAuthenticationIntegrationTestBase {
WebClient webClient = TestWebClientFactory.create(Settings.createSslConfiguration());
@Test
public void shouldSelfLookup() {
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
.ttl(Duration.ofSeconds(60)).renewable().numUses(1).build();
VaultToken token = prepare().getVaultOperations().opsForToken()
.create(tokenRequest).getToken();
AuthenticationStepsOperator operator = new AuthenticationStepsOperator(
TokenAuthentication.createAuthenticationSteps(token, true), webClient);
StepVerifier
.create(operator.getVaultToken())
.consumeNextWith(
actual -> {
assertThat(actual).isInstanceOf(LoginToken.class);
LoginToken loginToken = (LoginToken) actual;
assertThat(loginToken.getLeaseDuration()).isBetween(
Duration.ofSeconds(40), Duration.ofSeconds(60));
assertThat(loginToken.isRenewable()).isTrue();
}).verifyComplete();
}
@Test
public void shouldFailDuringSelfLookup() {
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
.ttl(Duration.ofSeconds(60)).renewable().numUses(1).build();
VaultToken token = prepare().getVaultOperations().opsForToken()
.create(tokenRequest).getToken();
AuthenticationStepsOperator operator = new AuthenticationStepsOperator(
TokenAuthentication.createAuthenticationSteps(token, true), webClient);
// first usage
StepVerifier.create(operator.getVaultToken()).expectNextCount(1).verifyComplete();
StepVerifier.create(operator.getVaultToken()).expectError(VaultException.class)
.verify();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2017 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.authentication;
import java.time.Duration;
import org.junit.Test;
import org.springframework.vault.VaultException;
import org.springframework.vault.support.VaultToken;
import org.springframework.vault.support.VaultTokenRequest;
import org.springframework.vault.util.Settings;
import org.springframework.vault.util.TestRestTemplateFactory;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
/**
* Integration tests for {@link TokenAuthentication} using
* {@link AuthenticationStepsExecutor}.
*
* @author Mark Paluch
*/
public class TokenAuthenticationStepsIntegrationTests extends
TokenAuthenticationIntegrationTestBase {
RestTemplate restTemplate = TestRestTemplateFactory.create(Settings
.createSslConfiguration());
@Test
public void shouldSelfLookup() {
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
.ttl(Duration.ofSeconds(60)).renewable().numUses(1).build();
VaultToken token = prepare().getVaultOperations().opsForToken()
.create(tokenRequest).getToken();
AuthenticationStepsExecutor operator = new AuthenticationStepsExecutor(
TokenAuthentication.createAuthenticationSteps(token, true), restTemplate);
VaultToken login = operator.login();
assertThat(login).isInstanceOf(LoginToken.class);
LoginToken loginToken = (LoginToken) login;
assertThat(loginToken.getLeaseDuration()).isBetween(Duration.ofSeconds(40),
Duration.ofSeconds(60));
assertThat(loginToken.isRenewable()).isTrue();
}
@Test
public void shouldFailDuringSelfLookup() {
VaultTokenRequest tokenRequest = VaultTokenRequest.builder()
.ttl(Duration.ofSeconds(60)).renewable().numUses(1).build();
VaultToken token = prepare().getVaultOperations().opsForToken()
.create(tokenRequest).getToken();
AuthenticationStepsExecutor operator = new AuthenticationStepsExecutor(
TokenAuthentication.createAuthenticationSteps(token, true), restTemplate);
operator.login();
assertThatThrownBy(operator::login).isInstanceOf(VaultException.class);
}
}