From a79898c2b9075fc8fea757f8a5b0ffc86ffbcedc Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 1 Aug 2018 10:21:12 +0200 Subject: [PATCH] Support full pull mode in AppRole authentication through AuthenticationSteps. We now support full pull mode (pull/wrapped) for roleId and secretId leveraging the newly introduced zipWith operator. Closes gh-259. --- .../authentication/AppRoleAuthentication.java | 126 ++++++++---------- .../authentication/AuthenticationSteps.java | 3 + ...leAuthenticationStepsIntegrationTests.java | 13 ++ 3 files changed, 72 insertions(+), 70 deletions(-) diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppRoleAuthentication.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppRoleAuthentication.java index d8138db4..99065819 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppRoleAuthentication.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AppRoleAuthentication.java @@ -102,88 +102,74 @@ public class AppRoleAuthentication implements ClientAuthentication, RoleId roleId = options.getRoleId(); SecretId secretId = options.getSecretId(); - if ((roleId instanceof Wrapped || roleId instanceof Pull) - && (secretId instanceof Wrapped || secretId instanceof Pull)) { - - throw new IllegalArgumentException( - "RoleId and SecretId are both configured to obtain their values from initial Vault request. AuthenticationSteps supports currently only fetching of a single element."); - } - return getAuthenticationSteps(options, roleId, secretId).login( "auth/{mount}/login", options.getPath()); } - private static Node getAuthenticationSteps(AppRoleAuthenticationOptions options, + private static Node> getAuthenticationSteps( + AppRoleAuthenticationOptions options, RoleId roleId, SecretId secretId) { - if (roleId instanceof Pull || roleId instanceof Wrapped) { + Node roleIdSteps = getRoleIdSteps(options, roleId); + Node secretIdSteps = getSecretIdSteps(options, secretId); - Node steps; + return roleIdSteps.zipWith(secretIdSteps).map( + it -> getAppRoleLoginBody(it.getLeft(), it.getRight())); + } - if (roleId instanceof Pull) { - - HttpHeaders headers = createHttpHeaders(((Pull) roleId).getInitialToken()); - - steps = AuthenticationSteps.fromHttpRequest(get( - "auth/{mount}/role/{role}/role-id", options.getPath(), - options.getAppRole()).with(headers).as(VaultResponse.class)); - } - else { - steps = unwrapResponse(((Wrapped) roleId).getInitialToken()); - } - - return steps.map( - vaultResponse -> (String) vaultResponse.getRequiredData().get( - "role_id")).map( - roleIdToken -> { - - return getAppRoleLoginBody( - roleIdToken, - secretId instanceof Provided ? ((Provided) secretId) - .getValue() : null); - }); - } - - if (secretId instanceof Pull || secretId instanceof Wrapped) { - - Node steps; - - if (secretId instanceof Pull) { - HttpHeaders headers = createHttpHeaders(((Pull) secretId) - .getInitialToken()); - - steps = AuthenticationSteps.fromHttpRequest(post( - "auth/{mount}/role/{role}/secret-id", options.getPath(), - options.getAppRole()).with(headers).as(VaultResponse.class)); - } - else { - steps = unwrapResponse(((Wrapped) secretId).getInitialToken()); - } - - return steps.map( - vaultResponse -> (String) vaultResponse.getRequiredData().get( - "secret_id")).map( - secretIdToken -> { - - return getAppRoleLoginBody( - roleId instanceof Provided ? ((Provided) roleId) - .getValue() : null, secretIdToken); - }); - } + private static Node getRoleIdSteps(AppRoleAuthenticationOptions options, + RoleId roleId) { if (roleId instanceof Provided) { - - return AuthenticationSteps.fromSupplier(() -> { - - return getAppRoleLoginBody(((Provided) roleId).getValue(), - secretId instanceof Provided ? ((Provided) secretId).getValue() - : null); - }); + return AuthenticationSteps.fromSupplier(((Provided) roleId)::getValue); } - throw new IllegalArgumentException(String.format( - "Provided RoleId/SecretId setup not supported. RoleId: %s, SecretId: %s", - roleId, secretId)); + if (roleId instanceof Pull) { + + HttpHeaders headers = createHttpHeaders(((Pull) roleId).getInitialToken()); + + return AuthenticationSteps.fromHttpRequest( + get("auth/{mount}/role/{role}/role-id", options.getPath(), + options.getAppRole()).with(headers).as(VaultResponse.class)) + .map(vaultResponse -> (String) vaultResponse.getRequiredData().get( + "role_id")); + } + + if (roleId instanceof Wrapped) { + return unwrapResponse(((Wrapped) roleId).getInitialToken()).map( + vaultResponse -> (String) vaultResponse.getRequiredData().get( + "role_id")); + } + + throw new IllegalArgumentException("Unknown RoleId configuration: " + roleId); + } + + private static Node getSecretIdSteps(AppRoleAuthenticationOptions options, + SecretId secretId) { + + if (secretId instanceof Provided) { + return AuthenticationSteps.fromSupplier(((Provided) secretId)::getValue); + } + + if (secretId instanceof Pull) { + HttpHeaders headers = createHttpHeaders(((Pull) secretId).getInitialToken()); + + return AuthenticationSteps.fromHttpRequest( + post("auth/{mount}/role/{role}/secret-id", options.getPath(), + options.getAppRole()).with(headers).as(VaultResponse.class)) + .map(vaultResponse -> (String) vaultResponse.getRequiredData().get( + "secret_id")); + } + + if (secretId instanceof Wrapped) { + + return unwrapResponse(((Wrapped) secretId).getInitialToken()).map( + vaultResponse -> (String) vaultResponse.getRequiredData().get( + "secret_id")); + } + + throw new IllegalArgumentException("Unknown SecretId configuration: " + secretId); + } private static Node unwrapResponse(VaultToken token) { diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationSteps.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationSteps.java index 14fa26f6..8c79896b 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationSteps.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/AuthenticationSteps.java @@ -29,6 +29,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; import lombok.RequiredArgsConstructor; +import lombok.ToString; import lombok.Value; import lombok.experimental.FieldDefaults; @@ -575,6 +576,8 @@ public class AuthenticationSteps { * @param * @since 2.1 */ + @EqualsAndHashCode + @ToString public static class Pair { private final L left; diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AppRoleAuthenticationStepsIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AppRoleAuthenticationStepsIntegrationTests.java index b1bf3fcf..fbb250f1 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/AppRoleAuthenticationStepsIntegrationTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/AppRoleAuthenticationStepsIntegrationTests.java @@ -74,6 +74,19 @@ public class AppRoleAuthenticationStepsIntegrationTests extends assertThat(executor.login()).isNotNull(); } + @Test + public void shouldAuthenticateWithFullPullMode() { + + AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder() + .appRole("with-secret-id").initialToken(Settings.token()).build(); + + AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor( + AppRoleAuthentication.createAuthenticationSteps(options), prepare() + .getRestTemplate()); + + assertThat(executor.login()).isNotNull(); + } + @Test public void authenticationStepsShouldAuthenticateWithPullSecretId() {