diff --git a/spring-vault-core/pom.xml b/spring-vault-core/pom.xml index 00bac19c..a47ceb9e 100644 --- a/spring-vault-core/pom.xml +++ b/spring-vault-core/pom.xml @@ -100,6 +100,13 @@ 1.16.10 true + + + org.slf4j + slf4j-jcl + 1.7.16 + test + diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2Authentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2Authentication.java index d6316189..d79ab504 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2Authentication.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2Authentication.java @@ -17,7 +17,6 @@ package org.springframework.vault.authentication; import java.util.HashMap; import java.util.Map; -import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; @@ -159,6 +158,6 @@ public class AwsEc2Authentication implements ClientAuthentication { } protected char[] createNonce() { - return UUID.randomUUID().toString().toCharArray(); + return options.getNonce().getValue(); } } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java index 5e6462c5..f26abf1e 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AwsEc2AuthenticationOptions.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-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. @@ -16,6 +16,8 @@ package org.springframework.vault.authentication; import java.net.URI; +import java.util.Arrays; +import java.util.UUID; import org.springframework.util.Assert; @@ -59,15 +61,23 @@ public class AwsEc2AuthenticationOptions { */ private final String role; + /** + * Authentication nonce. + */ + private final Nonce nonce; + private AwsEc2AuthenticationOptions() { - this(DEFAULT_AWS_AUTHENTICATION_PATH, DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI, ""); + this(DEFAULT_AWS_AUTHENTICATION_PATH, DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI, "", + Nonce.generated()); } - private AwsEc2AuthenticationOptions(String path, URI identityDocumentUri, String role) { + private AwsEc2AuthenticationOptions(String path, URI identityDocumentUri, + String role, Nonce nonce) { this.path = path; this.identityDocumentUri = identityDocumentUri; this.role = role; + this.nonce = nonce; } /** @@ -98,6 +108,13 @@ public class AwsEc2AuthenticationOptions { return role; } + /** + * @return the configured {@link Nonce}. + */ + public Nonce getNonce() { + return nonce; + } + /** * Builder for {@link AwsEc2AuthenticationOptionsBuilder}. */ @@ -106,6 +123,7 @@ public class AwsEc2AuthenticationOptions { private String path = DEFAULT_AWS_AUTHENTICATION_PATH; private URI identityDocumentUri = DEFAULT_PKCS7_IDENTITY_DOCUMENT_URI; private String role; + private Nonce nonce = Nonce.generated(); AwsEc2AuthenticationOptionsBuilder() { } @@ -152,6 +170,22 @@ public class AwsEc2AuthenticationOptions { return this; } + /** + * Configure a {@link Nonce} for login requests. Defaults to + * {@link Nonce#generated()}. + * + * @param nonce must not be {@literal null}. + * @return {@code this} {@link AwsEc2AuthenticationOptionsBuilder}. + * @since 1.1 + */ + public AwsEc2AuthenticationOptionsBuilder nonce(Nonce nonce) { + + Assert.notNull(nonce, "Nonce must not be null"); + + this.nonce = nonce; + return this; + } + /** * Build a new {@link AwsEc2AuthenticationOptions} instance. * @@ -161,7 +195,63 @@ public class AwsEc2AuthenticationOptions { Assert.notNull(identityDocumentUri, "IdentityDocumentUri must not be null"); - return new AwsEc2AuthenticationOptions(path, identityDocumentUri, role); + return new AwsEc2AuthenticationOptions(path, identityDocumentUri, role, nonce); + } + } + + /** + * Value object for an authentication nonce. + * + * @since 1.1 + */ + public static class Nonce { + + private final char[] value; + + protected Nonce(char[] value) { + this.value = value; + } + + /** + * Create a new generated {@link Nonce} using {@link UUID}. + * + * @return a new generated {@link Nonce} using {@link UUID}. + */ + public static Nonce generated() { + return new Generated(); + } + + /** + * Create a wrapped {@link Nonce} given a {@code nonce} value. + * + * @return a wrapped {@link Nonce} given for the {@code nonce} value. + */ + public static Nonce provided(char[] nonce) { + + Assert.notNull(nonce, "Nonce must not be null"); + + return new Provided(Arrays.copyOf(nonce, nonce.length)); + } + + /** + * @return the nonce value. + */ + public char[] getValue() { + return value; + } + + static class Generated extends Nonce { + + Generated() { + super(UUID.randomUUID().toString().toCharArray()); + } + } + + static class Provided extends Nonce { + + Provided(char[] nonce) { + super(nonce); + } } } } diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java index 8e619348..3d5da10c 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AwsEc2AuthenticationUnitTests.java @@ -25,6 +25,7 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.vault.VaultException; +import org.springframework.vault.authentication.AwsEc2AuthenticationOptions.Nonce; import org.springframework.vault.client.VaultClients; import org.springframework.vault.client.VaultClients.PrefixAwareUriTemplateHandler; import org.springframework.vault.support.VaultToken; @@ -94,9 +95,20 @@ public class AwsEc2AuthenticationUnitTests { @Test public void shouldLogin() throws Exception { + Nonce nonce = Nonce.provided("foo".toCharArray()); + + AwsEc2AuthenticationOptions authenticationOptions = AwsEc2AuthenticationOptions + .builder().nonce(nonce).build(); + + mockRest.expect( + requestTo("http://169.254.169.254/latest/dynamic/instance-identity/pkcs7")) // + .andExpect(method(HttpMethod.GET)) // + .andRespond(withSuccess().body("value")); + mockRest.expect(requestTo("/auth/aws-ec2/login")) .andExpect(method(HttpMethod.POST)) .andExpect(jsonPath("$.pkcs7").value("value")) + .andExpect(jsonPath("$.nonce").value("foo")) .andRespond( withSuccess() .contentType(MediaType.APPLICATION_JSON) @@ -104,12 +116,8 @@ public class AwsEc2AuthenticationUnitTests { + "\"auth\":{\"client_token\":\"my-token\", \"lease_duration\":20}" + "}")); - AwsEc2Authentication authentication = new AwsEc2Authentication(restTemplate) { - @Override - protected Map getEc2Login() { - return Collections.singletonMap("pkcs7", "value"); - } - }; + AwsEc2Authentication authentication = new AwsEc2Authentication( + authenticationOptions, restTemplate, restTemplate); VaultToken login = authentication.login();