#4 token creation implemented

This commit is contained in:
Stefan Kalscheuer 2016-10-21 18:08:06 +02:00
parent 53a459eda1
commit 9618a50646
3 changed files with 159 additions and 9 deletions

View File

@ -54,7 +54,10 @@ public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_UNSEAL = "sys/unseal";
private static final String PATH_INIT = "sys/init";
private static final String PATH_AUTH = "sys/auth";
private static final String PATH_TOKEN_LOOKUP = "auth/token/lookup";
private static final String PATH_TOKEN = "auth/token";
private static final String PATH_LOOKUP = "/lookup";
private static final String PATH_CREATE = "/create";
private static final String PATH_CREATE_ORPHAN = "/create-orphan";
private static final String PATH_AUTH_USERPASS = "auth/userpass/login/";
private static final String PATH_AUTH_APPID = "auth/app-id/";
private static final String PATH_SECRET = "secret";
@ -193,7 +196,7 @@ public class HTTPVaultConnector implements VaultConnector {
this.token = token;
this.tokenTTL = 0;
try {
String response = requestPost(PATH_TOKEN_LOOKUP, new HashMap<>());
String response = requestPost(PATH_TOKEN + PATH_LOOKUP, new HashMap<>());
TokenResponse res = jsonMapper.readValue(response, TokenResponse.class);
authorized = true;
return res;
@ -305,6 +308,9 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public boolean writeSecret(final String key, final String value) throws VaultConnectorException {
if (!isAuthorized())
throw new AuthorizationRequiredException();
if (key == null || key.isEmpty())
throw new InvalidRequestException("Secret path must not be empty.");
@ -320,7 +326,12 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and expect empty result */
String response = requestDelete(PATH_SECRET + "/" + key);
return response.equals("");
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
return true;
}
@Override
@ -330,7 +341,12 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and expect empty result */
String response = requestPut(PATH_REVOKE + leaseID, new HashMap<>());
return response.equals("");
/* Response should be code 204 without content */
if (!response.equals(""))
throw new InvalidResponseException("Received response where non was expected.");
return true;
}
@Override
@ -340,9 +356,44 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public TokenResponse createToken(final Token token) throws VaultConnectorException {
/* TODO */
return null;
public AuthResponse createToken(final Token token) throws VaultConnectorException {
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE);
}
@Override
public AuthResponse createToken(final Token token, boolean orphan) throws VaultConnectorException {
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE_ORPHAN);
}
@Override
public AuthResponse createToken(final Token token, final String role) throws VaultConnectorException {
if (role == null || role.isEmpty())
throw new InvalidRequestException("No role name specified.");
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE + "/" + role);
}
/**
* Create token.
* Centralized method to handle different token creation requests.
*
* @param token the token
* @param path request path
* @return the response
* @throws VaultConnectorException on error
*/
private AuthResponse createTokenInternal(final Token token, final String path) throws VaultConnectorException {
if (!isAuthorized())
throw new AuthorizationRequiredException();
if (token == null)
throw new InvalidRequestException("Token must be provided.");
String response = requestPost(path, token);
try {
return jsonMapper.readValue(response, AuthResponse.class);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response", e);
}
}
@ -354,7 +405,7 @@ public class HTTPVaultConnector implements VaultConnector {
* @return HTTP response
* @throws VaultConnectorException on connection error
*/
private String requestPost(final String path, final Map payload) throws VaultConnectorException {
private String requestPost(final String path, final Object payload) throws VaultConnectorException {
/* Initialize post */
HttpPost post = new HttpPost(baseURL + path);
/* generate JSON from payload */

View File

@ -217,5 +217,25 @@ public interface VaultConnector {
* @return the result response
* @throws VaultConnectorException on error
*/
TokenResponse createToken(final Token token) throws VaultConnectorException;
AuthResponse createToken(final Token token) throws VaultConnectorException;
/**
* Create a new token.
*
* @param token the token
* @param orphan create orphan token
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse createToken(final Token token, boolean orphan) throws VaultConnectorException;
/**
* Create a new token for specific role.
*
* @param token the token
* @param role the role name
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse createToken(final Token token, final String role) throws VaultConnectorException;
}

View File

@ -17,6 +17,8 @@
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenBuilder;
import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.test.Credentials;
import de.stklcode.jvault.connector.test.VaultConfiguration;
@ -33,6 +35,8 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.*;
@ -373,6 +377,81 @@ public class HTTPVaultConnectorTest {
}
}
/**
* Test revocation of secrets.
*/
@Test
public void createTokenTest() {
authRoot();
assumeTrue(connector.isAuthorized());
/* Create token */
Token token = new TokenBuilder()
.withId("test-id")
.withDisplayName("test name")
.build();
/* Create token */
try {
AuthResponse res = connector.createToken(token);
assertThat("No result given.", res, is(notNullValue()));
assertThat("Token creation returned warnings.", res.getWarnings(), is(nullValue()));
assertThat("Invalid token ID returned.", res.getAuth().getClientToken(), is("test-id"));
assertThat("Invalid number of policies returned.", res.getAuth().getPolicies(), hasSize(1));
assertThat("Root policy not inherited.", res.getAuth().getPolicies(), contains("root"));
assertThat("Metadata unexpected.", res.getAuth().getMetadata(), is(nullValue()));
assertThat("Root token should not be renewable", res.getAuth().isRenewable(), is(false));
} catch (VaultConnectorException e) {
fail("Secret written to inaccessible path.");
}
/* Create token with attributes */
token = new TokenBuilder()
.withId("test-id2")
.withDisplayName("test name 2")
.withPolicies(Collections.singletonList("testpolicy"))
.withoutDefaultPolicy()
.withMeta("foo", "bar")
.build();
try {
AuthResponse res = connector.createToken(token);
assertThat("Invalid token ID returned.", res.getAuth().getClientToken(), is("test-id2"));
assertThat("Invalid number of policies returned.", res.getAuth().getPolicies(), hasSize(1));
assertThat("Root policy not inherited.", res.getAuth().getPolicies(), contains("testpolicy"));
assertThat("Metadata not given.", res.getAuth().getMetadata(), is(notNullValue()));
assertThat("Metadata not correct.", res.getAuth().getMetadata().get("foo"), is("bar"));
assertThat("Token should be renewable", res.getAuth().isRenewable(), is(true));
} catch (VaultConnectorException e) {
fail("Secret written to inaccessible path.");
}
/* Overwrite token */
token = new TokenBuilder()
.withId("test-id2")
.withDisplayName("test name 3")
.withPolicies(Arrays.asList("pol1", "pol2"))
.withDefaultPolicy()
.withMeta("test", "success")
.withMeta("key", "value")
.withTtl(1234)
.build();
try {
AuthResponse res = connector.createToken(token);
assertThat("Invalid token ID returned.", res.getAuth().getClientToken(), is("test-id2"));
assertThat("Invalid number of policies returned.", res.getAuth().getPolicies(), hasSize(3));
assertThat("Policies not returned as expected.", res.getAuth().getPolicies(), contains("default", "pol1", "pol2"));
assertThat("Old policy not overwritten.", res.getAuth().getPolicies(), not(contains("testpolicy")));
assertThat("Metadata not given.", res.getAuth().getMetadata(), is(notNullValue()));
assertThat("Metadata not correct.", res.getAuth().getMetadata().get("test"), is("success"));
assertThat("Metadata not correct.", res.getAuth().getMetadata().get("key"), is("value"));
assertThat("Old metadata not overwritten.", res.getAuth().getMetadata().get("foo"), is(nullValue()));
assertThat("TTL not set correctly", res.getAuth().getLeaseDuration(), is(1234));
assertThat("Token should be renewable", res.getAuth().isRenewable(), is(true));
} catch (VaultConnectorException e) {
fail("Secret written to inaccessible path.");
}
}
/**
* Initialize Vault with resource datastore and generated configuration.
* @return Vault Configuration