split VaultConnector interface into clients per module
All checks were successful
CI / build (11) (push) Successful in 33s
CI / build (17) (push) Successful in 31s
CI / build (true, 21) (push) Successful in 25s

The connector interface has grown quite big and does not even cover all
potential APIs. We now extract functionality into submodules and group
them to handle each area in separate interfaces. Provide fluent access,
strip prefixes from methods and preserve a 1:1 migration path.

Examples:

* connector.unseal() => connector.sys().unseal()
* connector.readSecretVersion() => connector.kv2().readVersion()
* connector.createToken() => connector.token().create()
* connector.lookupAppRole() => connector.appRole().lookup()
* connector.transitHash() => connector.transit().hash()
This commit is contained in:
2025-09-02 15:35:18 +02:00
parent e96ece3385
commit 1072e9b4a9
11 changed files with 1384 additions and 1120 deletions

View File

@@ -109,11 +109,11 @@ Token token = Token.builder()
.withDisplayName("new test token")
.withPolicies("pol1", "pol2")
.build();
vault.createToken(token);
vault.token().create(token);
// Create AppRole credentials
vault.createAppRole("testrole", policyList);
AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
vault.appRole().create("testrole", policyList);
AppRoleSecretResponse secret = vault.appRole().createSecret("testrole");
```
## Links

View File

@@ -3,7 +3,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>1.5.3-SNAPSHOT</version>
<version>2.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* 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 de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.AppRole;
import de.stklcode.jvault.connector.model.AppRoleSecret;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.*;
import java.util.ArrayList;
import java.util.List;
/**
* AppRole client interface.
* Provides methods to interact with Vault's AppRole API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface AppRoleClient {
/**
* Register a new AppRole role from given metamodel.
*
* @param role The role
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean create(final AppRole role) throws VaultConnectorException;
/**
* Register new AppRole role with default policy.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName) throws VaultConnectorException {
return create(roleName, new ArrayList<>());
}
/**
* Register new AppRole role with policies.
*
* @param roleName The role name
* @param policies The policies to associate with
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final List<String> policies) throws VaultConnectorException {
return create(roleName, policies, null);
}
/**
* Register new AppRole role with default policy and custom ID.
*
* @param roleName The role name
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final String roleID) throws VaultConnectorException {
return create(roleName, new ArrayList<>(), roleID);
}
/**
* Register new AppRole role with policies and custom ID.
*
* @param roleName The role name
* @param policies The policies to associate with
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final List<String> policies, final String roleID)
throws VaultConnectorException {
return create(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
}
/**
* Delete AppRole role from Vault.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
*/
boolean delete(final String roleName) throws VaultConnectorException;
/**
* Lookup an AppRole role.
*
* @param roleName The role name
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleResponse lookup(final String roleName) throws VaultConnectorException;
/**
* Retrieve ID for an AppRole role.
*
* @param roleName The role name
* @return The role ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
String getRoleID(final String roleName) throws VaultConnectorException;
/**
* Set custom ID for an AppRole role.
*
* @param roleName The role name
* @param roleID The role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean setRoleID(final String roleName, final String roleID) throws VaultConnectorException;
/**
* Register new random generated AppRole secret.
*
* @param roleName The role name
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createSecret(final String roleName) throws VaultConnectorException {
return createSecret(roleName, new AppRoleSecret());
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secretID A custom secret ID
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createSecret(final String roleName, final String secretID)
throws VaultConnectorException {
return createSecret(roleName, new AppRoleSecret(secretID));
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secret The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse createSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException;
/**
* Lookup an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret ID
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse lookupSecret(final String roleName, final String secretID)
throws VaultConnectorException;
/**
* Destroy an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean destroySecret(final String roleName, final String secretID) throws VaultConnectorException;
/**
* List existing (accessible) AppRole roles.
*
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listRoles() throws VaultConnectorException;
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listSecrets(final String roleName) throws VaultConnectorException;
}

View File

@@ -106,55 +106,11 @@ public class HTTPVaultConnector implements VaultConnector {
authorized = false;
}
@Override
public final SealResponse sealStatus() throws VaultConnectorException {
return request.get(SYS_SEAL_STATUS, emptyMap(), token, SealResponse.class);
}
@Override
public final void seal() throws VaultConnectorException {
request.put(SYS_SEAL, emptyMap(), token);
}
@Override
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
Map<String, String> param = mapOfStrings(
"key", key,
"reset", reset
);
return request.put(SYS_UNSEAL, param, token, SealResponse.class);
}
@Override
public HealthResponse getHealth() throws VaultConnectorException {
return request.get(
SYS_HEALTH,
// Force status code to be 200, so we don't need to modify the request sequence.
Map.of(
"standbycode", "200", // Default: 429.
"sealedcode", "200", // Default: 503.
"uninitcode", "200" // Default: 501.
),
token,
HealthResponse.class
);
}
@Override
public final boolean isAuthorized() {
return authorized && (tokenTTL == 0 || tokenTTL >= System.currentTimeMillis());
}
@Override
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
/* Issue request and parse response */
AuthMethodsResponse amr = request.get(SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
}
@Override
public final TokenResponse authToken(final String token) throws VaultConnectorException {
/* set token */
@@ -202,142 +158,6 @@ public class HTTPVaultConnector implements VaultConnector {
return auth;
}
@Override
public final boolean createAppRole(final AppRole role) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(String.format(AUTH_APPROLE_ROLE, role.getName(), ""), role, token);
/* Set custom ID if provided */
return !(role.getId() != null && !role.getId().isEmpty()) || setAppRoleID(role.getName(), role.getId());
}
@Override
public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(
String.format(AUTH_APPROLE_ROLE, roleName, ""),
emptyMap(),
token,
AppRoleResponse.class
);
}
@Override
public final boolean deleteAppRole(final String roleName) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.deleteWithoutResponse(String.format(AUTH_APPROLE_ROLE, roleName, ""), token);
return true;
}
@Override
public final String getAppRoleID(final String roleName) throws VaultConnectorException {
requireAuth();
/* Issue request, parse response and extract Role ID */
return request.get(
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
emptyMap(),
token,
RawDataResponse.class
).getData().get("role_id").toString();
}
@Override
public final boolean setAppRoleID(final String roleName, final String roleID) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
singletonMap("role_id", roleID),
token
);
return true;
}
@Override
public final AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException {
requireAuth();
if (secret.getId() != null && !secret.getId().isEmpty()) {
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"),
secret,
token,
AppRoleSecretResponse.class
);
} else {
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id"),
secret, token,
AppRoleSecretResponse.class
);
}
}
@Override
public final AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID)
throws VaultConnectorException {
requireAuth();
/* Issue request and parse secret response */
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"),
new AppRoleSecret(secretID),
token,
AppRoleSecretResponse.class
);
}
@Override
public final boolean destroyAppRoleSecret(final String roleName, final String secretID)
throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/destroy"),
new AppRoleSecret(secretID),
token);
return true;
}
@Override
public final List<String> listAppRoles() throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(
AUTH_APPROLE + "/role?list=true",
emptyMap(),
token,
SecretListResponse.class
);
return secrets.getKeys();
}
@Override
public final List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
emptyMap(),
token,
SecretListResponse.class
);
return secrets.getKeys();
}
@Override
public final SecretResponse read(final String key) throws VaultConnectorException {
requireAuth();
@@ -345,66 +165,6 @@ public class HTTPVaultConnector implements VaultConnector {
return request.get(key, emptyMap(), token, PlainSecretResponse.class);
}
@Override
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
Map<String, String> args = mapOfStrings("version", version);
return request.get(mount + SECRET_DATA + key, args, token, MetaSecretResponse.class);
}
@Override
public final MetadataResponse readSecretMetadata(final String mount, final String key)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
return request.get(mount + SECRET_METADATA + key, emptyMap(), token, MetadataResponse.class);
}
@Override
public void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"max_versions", maxVersions,
"cas_required", casRequired
);
write(mount + SECRET_METADATA + key, payload);
}
@Override
public final SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException {
requireAuth();
if (key == null || key.isEmpty()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
// Add CAS value to options map if present.
Map<String, Object> options = mapOf("cas", cas);
/* Issue request and parse metadata response */
return request.post(
mount + SECRET_DATA + key,
Map.of(
"data", data,
"options", options
),
token,
SecretVersionResponse.class
);
}
@Override
public final List<String> list(final String path) throws VaultConnectorException {
requireAuth();
@@ -446,57 +206,6 @@ public class HTTPVaultConnector implements VaultConnector {
request.deleteWithoutResponse(key, token);
}
@Override
public final void deleteLatestSecretVersion(final String mount, final String key) throws VaultConnectorException {
delete(mount + SECRET_DATA + key);
}
@Override
public final void deleteAllSecretVersions(final String mount, final String key) throws VaultConnectorException {
delete(mount + SECRET_METADATA + key);
}
@Override
public final void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_DELETE, key, versions);
}
@Override
public final void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_UNDELETE, key, versions);
}
@Override
public final void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_DESTROY, key, versions);
}
/**
* Common method to bundle secret version operations.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param pathPart Path part to query.
* @param key Secret key.
* @param versions Versions to handle.
* @throws VaultConnectorException on error
* @since 0.8
*/
private void handleSecretVersions(final String mount,
final String pathPart,
final String key,
final int... versions) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and expect empty result */
Map<String, Object> payload = singletonMap("versions", versions);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(mount + pathPart + key, payload, token);
}
@Override
public final void revoke(final String leaseID) throws VaultConnectorException {
requireAuth();
@@ -519,21 +228,28 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final AuthResponse createToken(final Token token) throws VaultConnectorException {
return createTokenInternal(token, AUTH_TOKEN + TOKEN_CREATE);
public KV2ClientImpl kv2() {
return new KV2ClientImpl();
}
@Override
public final AuthResponse createToken(final Token token, final boolean orphan) throws VaultConnectorException {
return createTokenInternal(token, AUTH_TOKEN + TOKEN_CREATE_ORPHAN);
public TokenClientImpl token() {
return new TokenClientImpl();
}
@Override
public final 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, AUTH_TOKEN + TOKEN_CREATE + "/" + role);
public AppRoleClient appRole() {
return new AppRoleClientImpl();
}
@Override
public TransitClientImpl transit() {
return new TransitClientImpl();
}
@Override
public SysClientImpl sys() {
return new SysClientImpl();
}
@Override
@@ -543,124 +259,6 @@ public class HTTPVaultConnector implements VaultConnector {
tokenTTL = 0;
}
/**
* 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 {
requireAuth();
if (token == null) {
throw new InvalidRequestException("Token must be provided.");
}
return request.post(path, token, this.token, AuthResponse.class);
}
@Override
public final TokenResponse lookupToken(final String token) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(
AUTH_TOKEN + TOKEN_LOOKUP,
singletonMap("token", token),
token,
TokenResponse.class
);
}
@Override
public boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
} else if (role == null) {
throw new InvalidRequestException("Role must be provided.");
}
// Issue request and expect code 204 with empty response.
request.postWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, role, token);
return true;
}
@Override
public TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException {
requireAuth();
// Request HTTP response and parse response.
return request.get(AUTH_TOKEN + TOKEN_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
}
@Override
public List<String> listTokenRoles() throws VaultConnectorException {
requireAuth();
return list(AUTH_TOKEN + TOKEN_ROLES);
}
@Override
public boolean deleteTokenRole(final String name) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
}
// Issue request and expect code 204 with empty response.
request.deleteWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, token);
return true;
}
@Override
public final TransitResponse transitEncrypt(final String keyName, final String plaintext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"plaintext", plaintext
);
return request.post(TRANSIT_ENCRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse transitDecrypt(final String keyName, final String ciphertext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"ciphertext", ciphertext
);
return request.post(TRANSIT_DECRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse transitHash(final String algorithm, final String input, final String format)
throws VaultConnectorException {
if (format != null && !"hex".equals(format) && !"base64".equals(format)) {
throw new IllegalArgumentException("Unsupported format " + format);
}
requireAuth();
Map<String, Object> payload = mapOf(
"input", input,
"format", format
);
return request.post(TRANSIT_HASH + algorithm, payload, token, TransitResponse.class);
}
/**
* Check for required authorization.
*
@@ -710,4 +308,459 @@ public class HTTPVaultConnector implements VaultConnector {
return map;
}
/**
* Sub-client for KV v2 methods.
*/
public class KV2ClientImpl implements KV2Client {
@Override
public final SecretResponse readVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
Map<String, String> args = mapOfStrings("version", version);
return request.get(mount + SECRET_DATA + key, args, token, MetaSecretResponse.class);
}
@Override
public final MetadataResponse readMetadata(final String mount, final String key)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
return request.get(mount + SECRET_METADATA + key, emptyMap(), token, MetadataResponse.class);
}
@Override
public void updateMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"max_versions", maxVersions,
"cas_required", casRequired
);
write(mount + SECRET_METADATA + key, payload);
}
@Override
public final SecretVersionResponse writeData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException {
requireAuth();
if (key == null || key.isEmpty()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
// Add CAS value to options map if present.
Map<String, Object> options = mapOf("cas", cas);
/* Issue request and parse metadata response */
return request.post(
mount + SECRET_DATA + key,
Map.of(
"data", data,
"options", options
),
token,
SecretVersionResponse.class
);
}
@Override
public final void deleteLatestVersion(final String mount, final String key) throws VaultConnectorException {
delete(mount + SECRET_DATA + key);
}
@Override
public final void deleteAllVersions(final String mount, final String key) throws VaultConnectorException {
delete(mount + SECRET_METADATA + key);
}
@Override
public final void deleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_DELETE, key, versions);
}
@Override
public final void undeleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_UNDELETE, key, versions);
}
@Override
public final void destroyVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, SECRET_DESTROY, key, versions);
}
/**
* Common method to bundle secret version operations.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param pathPart Path part to query.
* @param key Secret key.
* @param versions Versions to handle.
* @throws VaultConnectorException on error
* @since 0.8
*/
private void handleSecretVersions(final String mount,
final String pathPart,
final String key,
final int... versions) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and expect empty result */
Map<String, Object> payload = singletonMap("versions", versions);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(mount + pathPart + key, payload, token);
}
}
/**
* Sub-client for token methods.
*/
public class TokenClientImpl implements TokenClient {
@Override
public final AuthResponse create(final Token token) throws VaultConnectorException {
return createInternal(token, AUTH_TOKEN + TOKEN_CREATE);
}
@Override
public final AuthResponse create(final Token token, final boolean orphan) throws VaultConnectorException {
return createInternal(token, AUTH_TOKEN + TOKEN_CREATE_ORPHAN);
}
@Override
public final AuthResponse create(final Token token, final String role) throws VaultConnectorException {
if (role == null || role.isEmpty()) {
throw new InvalidRequestException("No role name specified.");
}
return createInternal(token, AUTH_TOKEN + TOKEN_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 createInternal(final Token token, final String path) throws VaultConnectorException {
requireAuth();
if (token == null) {
throw new InvalidRequestException("Token must be provided.");
}
return request.post(path, token, HTTPVaultConnector.this.token, AuthResponse.class);
}
@Override
public final TokenResponse lookup(final String token) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(
AUTH_TOKEN + TOKEN_LOOKUP,
singletonMap("token", token),
token,
TokenResponse.class
);
}
@Override
public boolean createOrUpdateRole(final String name, final TokenRole role) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
} else if (role == null) {
throw new InvalidRequestException("Role must be provided.");
}
// Issue request and expect code 204 with empty response.
request.postWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, role, token);
return true;
}
@Override
public TokenRoleResponse readRole(final String name) throws VaultConnectorException {
requireAuth();
// Request HTTP response and parse response.
return request.get(AUTH_TOKEN + TOKEN_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
}
@Override
public List<String> listRoles() throws VaultConnectorException {
requireAuth();
return list(AUTH_TOKEN + TOKEN_ROLES);
}
@Override
public boolean deleteRole(final String name) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
}
// Issue request and expect code 204 with empty response.
request.deleteWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, token);
return true;
}
}
/**
* Sub-client for AppRole methods.
*/
public class AppRoleClientImpl implements AppRoleClient {
@Override
public final boolean create(final AppRole role) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(String.format(AUTH_APPROLE_ROLE, role.getName(), ""), role, token);
/* Set custom ID if provided */
return !(role.getId() != null && !role.getId().isEmpty()) || setRoleID(role.getName(), role.getId());
}
@Override
public final AppRoleResponse lookup(final String roleName) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(
String.format(AUTH_APPROLE_ROLE, roleName, ""),
emptyMap(),
token,
AppRoleResponse.class
);
}
@Override
public final boolean delete(final String roleName) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.deleteWithoutResponse(String.format(AUTH_APPROLE_ROLE, roleName, ""), token);
return true;
}
@Override
public final String getRoleID(final String roleName) throws VaultConnectorException {
requireAuth();
/* Issue request, parse response and extract Role ID */
return request.get(
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
emptyMap(),
token,
RawDataResponse.class
).getData().get("role_id").toString();
}
@Override
public final boolean setRoleID(final String roleName, final String roleID) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
singletonMap("role_id", roleID),
token
);
return true;
}
@Override
public final AppRoleSecretResponse createSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException {
requireAuth();
if (secret.getId() != null && !secret.getId().isEmpty()) {
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"),
secret,
token,
AppRoleSecretResponse.class
);
} else {
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id"),
secret, token,
AppRoleSecretResponse.class
);
}
}
@Override
public final AppRoleSecretResponse lookupSecret(final String roleName, final String secretID)
throws VaultConnectorException {
requireAuth();
/* Issue request and parse secret response */
return request.post(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"),
new AppRoleSecret(secretID),
token,
AppRoleSecretResponse.class
);
}
@Override
public final boolean destroySecret(final String roleName, final String secretID)
throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/destroy"),
new AppRoleSecret(secretID),
token);
return true;
}
@Override
public final List<String> listRoles() throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(
AUTH_APPROLE + "/role?list=true",
emptyMap(),
token,
SecretListResponse.class
);
return secrets.getKeys();
}
@Override
public final List<String> listSecrets(final String roleName) throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
emptyMap(),
token,
SecretListResponse.class
);
return secrets.getKeys();
}
}
/**
* Sub-client for transit methods.
*/
public class TransitClientImpl implements TransitClient {
@Override
public final TransitResponse encrypt(final String keyName, final String plaintext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"plaintext", plaintext
);
return request.post(TRANSIT_ENCRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse decrypt(final String keyName, final String ciphertext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"ciphertext", ciphertext
);
return request.post(TRANSIT_DECRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse hash(final String algorithm, final String input, final String format)
throws VaultConnectorException {
if (format != null && !"hex".equals(format) && !"base64".equals(format)) {
throw new IllegalArgumentException("Unsupported format " + format);
}
requireAuth();
Map<String, Object> payload = mapOf(
"input", input,
"format", format
);
return request.post(TRANSIT_HASH + algorithm, payload, token, TransitResponse.class);
}
}
/**
* Sub-client for system methods.
*/
public class SysClientImpl implements SysClient {
@Override
public final SealResponse sealStatus() throws VaultConnectorException {
return request.get(SYS_SEAL_STATUS, emptyMap(), token, SealResponse.class);
}
@Override
public final void seal() throws VaultConnectorException {
request.put(SYS_SEAL, emptyMap(), token);
}
@Override
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
Map<String, String> param = mapOfStrings(
"key", key,
"reset", reset
);
return request.put(SYS_UNSEAL, param, token, SealResponse.class);
}
@Override
public HealthResponse getHealth() throws VaultConnectorException {
return request.get(
SYS_HEALTH,
// Force status code to be 200, so we don't need to modify the request sequence.
Map.of(
"standbycode", "200", // Default: 429.
"sealedcode", "200", // Default: 503.
"uninitcode", "200" // Default: 501.
),
token,
HealthResponse.class
);
}
@Override
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
/* Issue request and parse response */
AuthMethodsResponse amr = request.get(SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
}
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* 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 de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.response.MetadataResponse;
import de.stklcode.jvault.connector.model.response.SecretResponse;
import de.stklcode.jvault.connector.model.response.SecretVersionResponse;
import java.util.Map;
/**
* KV v2 client interface.
* Provides methods to interact with Vault's KV v2 API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface KV2Client {
/**
* Retrieve the latest secret data for specific version from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readData(final String mount, final String key) throws VaultConnectorException {
return readVersion(mount, key, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeData(final String mount,
final String key,
final Map<String, Object> data) throws VaultConnectorException {
return writeData(mount, key, data, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretVersionResponse writeData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret response.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretResponse readVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
* <br>
* Path {@code <mount>/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
* @since 0.8
*/
MetadataResponse readMetadata(final String mount, final String key) throws VaultConnectorException;
/**
* Update secret metadata.
* <br>
* Path {@code <mount>/metadata/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteLatestVersion(final String mount, final String key) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteAllVersions(final String mount, final String key) throws VaultConnectorException;
/**
* Delete secret versions from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void undeleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
void destroyVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* 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 de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.*;
import java.util.List;
/**
* Sys client interface.
* Provides methods to interact with Vault's system API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface SysClient {
/**
* Retrieve status of vault seal.
*
* @return Seal status
* @throws VaultConnectorException on error
*/
SealResponse sealStatus() throws VaultConnectorException;
/**
* Seal vault.
*
* @throws VaultConnectorException on error
*/
void seal() throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @param reset Discard previously provided keys (optional)
* @return Response with seal status
* @throws VaultConnectorException on error
*/
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @return Response with seal status
* @throws VaultConnectorException on error
*/
default SealResponse unseal(final String key) throws VaultConnectorException {
return unseal(key, null);
}
/**
* Query server health information.
*
* @return Health information.
* @throws VaultConnectorException on error
* @since 0.7.0
*/
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all available authentication backends.
*
* @return List of backends
* @throws VaultConnectorException on error
*/
List<AuthBackend> getAuthBackends() throws VaultConnectorException;
}

View File

@@ -0,0 +1,125 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* 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 de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.AuthResponse;
import de.stklcode.jvault.connector.model.response.TokenResponse;
import de.stklcode.jvault.connector.model.response.TokenRoleResponse;
import java.util.List;
/**
* Token client interface.
* Provides methods to interact with Vault's token API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface TokenClient {
/**
* Create a new token.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse create(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 create(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 create(final Token token, final String role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
*/
TokenResponse lookup(final String token) throws VaultConnectorException;
/**
* Create a new or update an existing token role.
*
* @param role the role entity (name must be set)
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
default boolean createOrUpdateRole(final TokenRole role) throws VaultConnectorException {
return createOrUpdateRole(role.getName(), role);
}
/**
* Create a new or update an existing token role.
*
* @param name the role name (overrides name possibly set in role entity)
* @param role the role entity
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean createOrUpdateRole(final String name, final TokenRole role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param name the role name
* @return the result response
* @throws VaultConnectorException on error
* @since 0.9
*/
TokenRoleResponse readRole(final String name) throws VaultConnectorException;
/**
* List available token roles from Vault.
*
* @return List of token roles
* @throws VaultConnectorException on error
* @since 0.9
*/
List<String> listRoles() throws VaultConnectorException;
/**
* Delete a token role.
*
* @param name the role name to delete
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean deleteRole(final String name) throws VaultConnectorException;
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* 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 de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.response.TransitResponse;
import java.util.Base64;
/**
* Transit client interface.
* Provides methods to interact with Vault's transit API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface TransitClient {
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Text to encrypt (Base64 encoded)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse encrypt(final String keyName, final String plaintext) throws VaultConnectorException;
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Binary data to encrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse encrypt(final String keyName, final byte[] plaintext)
throws VaultConnectorException {
return encrypt(keyName, Base64.getEncoder().encodeToString(plaintext));
}
/**
* Decrypt ciphertext via transit engine from Vault.
*
* @param keyName Transit key name
* @param ciphertext Text to decrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse decrypt(final String keyName, final String ciphertext) throws VaultConnectorException;
/**
* Hash data in hex format via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse hash(final String algorithm, final String input) throws VaultConnectorException {
return hash(algorithm, input, "hex");
}
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash (Base64 encoded)
* @param format Specifies the output encoding (hex/base64)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse hash(final String algorithm, final String input, final String format)
throws VaultConnectorException;
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse hash(final String algorithm, final byte[] input, final String format)
throws VaultConnectorException {
return hash(algorithm, Base64.getEncoder().encodeToString(input), format);
}
}

View File

@@ -37,59 +37,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
void resetAuth();
/**
* Retrieve status of vault seal.
*
* @return Seal status
* @throws VaultConnectorException on error
*/
SealResponse sealStatus() throws VaultConnectorException;
/**
* Seal vault.
*
* @throws VaultConnectorException on error
*/
void seal() throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @param reset Discard previously provided keys (optional)
* @return Response with seal status
* @throws VaultConnectorException on error
*/
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @return Response with seal status
* @throws VaultConnectorException on error
*/
default SealResponse unseal(final String key) throws VaultConnectorException {
return unseal(key, null);
}
/**
* Query server health information.
*
* @return Health information.
* @throws VaultConnectorException on error
* @since 0.7.0
*/
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all available authentication backends.
*
* @return List of backends
* @throws VaultConnectorException on error
*/
List<AuthBackend> getAuthBackends() throws VaultConnectorException;
/**
* Authorize to Vault using token.
*
@@ -132,187 +79,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException;
/**
* Register a new AppRole role from given metamodel.
*
* @param role The role
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean createAppRole(final AppRole role) throws VaultConnectorException;
/**
* Register new AppRole role with default policy.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean createAppRole(final String roleName) throws VaultConnectorException {
return createAppRole(roleName, new ArrayList<>());
}
/**
* Register new AppRole role with policies.
*
* @param roleName The role name
* @param policies The policies to associate with
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean createAppRole(final String roleName, final List<String> policies) throws VaultConnectorException {
return createAppRole(roleName, policies, null);
}
/**
* Register new AppRole role with default policy and custom ID.
*
* @param roleName The role name
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean createAppRole(final String roleName, final String roleID) throws VaultConnectorException {
return createAppRole(roleName, new ArrayList<>(), roleID);
}
/**
* Register new AppRole role with policies and custom ID.
*
* @param roleName The role name
* @param policies The policies to associate with
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean createAppRole(final String roleName, final List<String> policies, final String roleID)
throws VaultConnectorException {
return createAppRole(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
}
/**
* Delete AppRole role from Vault.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
*/
boolean deleteAppRole(final String roleName) throws VaultConnectorException;
/**
* Lookup an AppRole role.
*
* @param roleName The role name
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException;
/**
* Retrieve ID for an AppRole role.
*
* @param roleName The role name
* @return The role ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
String getAppRoleID(final String roleName) throws VaultConnectorException;
/**
* Set custom ID for an AppRole role.
*
* @param roleName The role name
* @param roleID The role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean setAppRoleID(final String roleName, final String roleID) throws VaultConnectorException;
/**
* Register new random generated AppRole secret.
*
* @param roleName The role name
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createAppRoleSecret(final String roleName) throws VaultConnectorException {
return createAppRoleSecret(roleName, new AppRoleSecret());
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secretID A custom secret ID
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createAppRoleSecret(final String roleName, final String secretID)
throws VaultConnectorException {
return createAppRoleSecret(roleName, new AppRoleSecret(secretID));
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secret The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException;
/**
* Lookup an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret ID
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID)
throws VaultConnectorException;
/**
* Destroy an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean destroyAppRoleSecret(final String roleName, final String secretID) throws VaultConnectorException;
/**
* List existing (accessible) AppRole roles.
*
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listAppRoles() throws VaultConnectorException;
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException;
/**
* Get authorization status.
*
@@ -330,108 +96,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
SecretResponse read(final String key) throws VaultConnectorException;
/**
* Retrieve the latest secret data for specific version from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readSecretData(final String mount, final String key) throws VaultConnectorException {
return readSecretVersion(mount, key, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data) throws VaultConnectorException {
return writeSecretData(mount, key, data, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret response.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
* <br>
* Path {@code <mount>/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
* @since 0.8
*/
MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException;
/**
* Update secret metadata.
* <br>
* Path {@code <mount>/metadata/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException;
/**
* List available nodes from Vault.
*
@@ -487,71 +151,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
void delete(final String key) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteLatestSecretVersion(final String mount, final String key) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteAllSecretVersions(final String mount, final String key) throws VaultConnectorException;
/**
* Delete secret versions from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Revoke given lease immediately.
*
@@ -582,170 +181,44 @@ public interface VaultConnector extends AutoCloseable, Serializable {
SecretResponse renew(final String leaseID, final Integer increment) throws VaultConnectorException;
/**
* Create a new token.
* Get client for KV v2 API.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
* @return KV v2 client
* @since 2.0.0
*/
AuthResponse createToken(final Token token) throws VaultConnectorException;
KV2Client kv2();
/**
* Create a new token.
* Get client for token API.
*
* @param token the token
* @param orphan create orphan token
* @return the result response
* @throws VaultConnectorException on error
* @return Token client
* @since 2.0.0
*/
AuthResponse createToken(final Token token, boolean orphan) throws VaultConnectorException;
TokenClient token();
/**
* Create a new token for specific role.
* Get client for AppRole API.
*
* @param token the token
* @param role the role name
* @return the result response
* @throws VaultConnectorException on error
* @return AppRole client
* @since 2.0.0
*/
AuthResponse createToken(final Token token, final String role) throws VaultConnectorException;
AppRoleClient appRole();
/**
* Lookup token information.
* Get client for transit API.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
* @return Transit client
* @since 2.0.0
*/
TokenResponse lookupToken(final String token) throws VaultConnectorException;
TransitClient transit();
/**
* Create a new or update an existing token role.
* Get client for system API.
*
* @param role the role entity (name must be set)
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
* @return System client
* @since 2.0.0
*/
default boolean createOrUpdateTokenRole(final TokenRole role) throws VaultConnectorException {
return createOrUpdateTokenRole(role.getName(), role);
}
/**
* Create a new or update an existing token role.
*
* @param name the role name (overrides name possibly set in role entity)
* @param role the role entity
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param name the role name
* @return the result response
* @throws VaultConnectorException on error
* @since 0.9
*/
TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException;
/**
* List available token roles from Vault.
*
* @return List of token roles
* @throws VaultConnectorException on error
* @since 0.9
*/
List<String> listTokenRoles() throws VaultConnectorException;
/**
* Delete a token role.
*
* @param name the role name to delete
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean deleteTokenRole(final String name) throws VaultConnectorException;
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Text to encrypt (Base64 encoded)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse transitEncrypt(final String keyName, final String plaintext) throws VaultConnectorException;
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Binary data to encrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse transitEncrypt(final String keyName, final byte[] plaintext)
throws VaultConnectorException {
return transitEncrypt(keyName, Base64.getEncoder().encodeToString(plaintext));
}
/**
* Decrypt ciphertext via transit engine from Vault.
*
* @param keyName Transit key name
* @param ciphertext Text to decrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse transitDecrypt(final String keyName, final String ciphertext) throws VaultConnectorException;
/**
* Hash data in hex format via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse transitHash(final String algorithm, final String input) throws VaultConnectorException {
return transitHash(algorithm, input, "hex");
}
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash (Base64 encoded)
* @param format Specifies the output encoding (hex/base64)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse transitHash(final String algorithm, final String input, final String format)
throws VaultConnectorException;
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse transitHash(final String algorithm, final byte[] input, final String format)
throws VaultConnectorException {
return transitHash(algorithm, Base64.getEncoder().encodeToString(input), format);
}
SysClient sys();
/**
* Read credentials for MySQL backend at default mount point.
@@ -816,4 +289,5 @@ public interface VaultConnector extends AutoCloseable, Serializable {
throws VaultConnectorException {
return (CredentialsResponse) read(mount + "/creds/" + role);
}
}

View File

@@ -95,10 +95,10 @@ class HTTPVaultConnectorIT {
connector = builder.build();
// Unseal Vault and check result.
SealResponse sealStatus = connector.unseal(KEY1);
SealResponse sealStatus = connector.sys().unseal(KEY1);
assumeTrue(sealStatus != null, "Seal status could not be determined after startup");
assumeTrue(sealStatus.isSealed(), "Vault is not sealed after startup");
sealStatus = connector.unseal(KEY2);
sealStatus = connector.sys().unseal(KEY2);
assumeTrue(sealStatus != null, "Seal status could not be determined");
assumeFalse(sealStatus.isSealed(), "Vault is not unsealed");
assumeTrue(sealStatus.isInitialized(), "Vault is not initialized"); // Initialized flag of Vault 0.11.2 (#20).
@@ -337,7 +337,7 @@ class HTTPVaultConnectorIT {
// Try to read accessible path with known value.
SecretResponse res = assertDoesNotThrow(
() -> connector.readSecretData(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readData(MOUNT_KV2, SECRET2_KEY),
"Valid secret path could not be read"
);
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
@@ -346,7 +346,7 @@ class HTTPVaultConnectorIT {
// Try to read different version of same secret.
res = assertDoesNotThrow(
() -> connector.readSecretVersion(MOUNT_KV2, SECRET2_KEY, 1),
() -> connector.kv2().readVersion(MOUNT_KV2, SECRET2_KEY, 1),
"Valid secret version could not be read"
);
assertEquals(1, res.getMetadata().getVersion(), "Unexpected secret version");
@@ -365,7 +365,7 @@ class HTTPVaultConnectorIT {
// First get the current version of the secret.
MetadataResponse res = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading secret metadata failed"
);
int currentVersion = res.getMetadata().getCurrentVersion();
@@ -374,7 +374,7 @@ class HTTPVaultConnectorIT {
Map<String, Object> data = new HashMap<>();
data.put("value", SECRET2_VALUE3);
SecretVersionResponse res2 = assertDoesNotThrow(
() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data),
() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data),
"Writing secret to KV v2 store failed"
);
assertEquals(currentVersion + 1, res2.getMetadata().getVersion(), "Version not updated after writing secret");
@@ -382,7 +382,7 @@ class HTTPVaultConnectorIT {
// Verify the content.
SecretResponse res3 = assertDoesNotThrow(
() -> connector.readSecretData(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readData(MOUNT_KV2, SECRET2_KEY),
"Reading secret from KV v2 store failed"
);
assertEquals(SECRET2_VALUE3, res3.get("value"), "Data not updated correctly");
@@ -391,13 +391,13 @@ class HTTPVaultConnectorIT {
Map<String, Object> data4 = singletonMap("value", SECRET2_VALUE4);
assertThrows(
InvalidResponseException.class,
() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data4, currentVersion2 - 1),
() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data4, currentVersion2 - 1),
"Writing secret to KV v2 with invalid CAS value succeeded"
);
// And finally with a correct CAS value.
Map<String, Object> data5 = singletonMap("value", SECRET2_VALUE4);
assertDoesNotThrow(() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data5, currentVersion2));
assertDoesNotThrow(() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data5, currentVersion2));
}
/**
@@ -412,7 +412,7 @@ class HTTPVaultConnectorIT {
// Read current metadata first.
MetadataResponse res = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading secret metadata failed"
);
Integer maxVersions = res.getMetadata().getMaxVersions();
@@ -420,13 +420,13 @@ class HTTPVaultConnectorIT {
// Now update the metadata.
assertDoesNotThrow(
() -> connector.updateSecretMetadata(MOUNT_KV2, SECRET2_KEY, maxVersions + 1, true),
() -> connector.kv2().updateMetadata(MOUNT_KV2, SECRET2_KEY, maxVersions + 1, true),
"Updating secret metadata failed"
);
// And verify the result.
res = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading secret metadata failed"
);
assertEquals(maxVersions + 1, res.getMetadata().getMaxVersions(), "Unexpected maximum number of versions");
@@ -444,7 +444,7 @@ class HTTPVaultConnectorIT {
// Try to read accessible path with known value.
MetadataResponse res = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Valid secret path could not be read"
);
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
@@ -467,21 +467,21 @@ class HTTPVaultConnectorIT {
// Try to delete non-existing versions.
assertDoesNotThrow(
() -> connector.deleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 5, 42),
() -> connector.kv2().deleteVersions(MOUNT_KV2, SECRET2_KEY, 5, 42),
"Revealed non-existence of secret versions"
);
assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Revealed non-existence of secret versions"
);
// Now delete existing version and verify.
assertDoesNotThrow(
() -> connector.deleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
() -> connector.kv2().deleteVersions(MOUNT_KV2, SECRET2_KEY, 1),
"Deleting existing version failed"
);
MetadataResponse meta = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading deleted secret metadata failed"
);
assertNotNull(
@@ -491,11 +491,11 @@ class HTTPVaultConnectorIT {
// Undelete the just deleted version.
assertDoesNotThrow(
() -> connector.undeleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
() -> connector.kv2().undeleteVersions(MOUNT_KV2, SECRET2_KEY, 1),
"Undeleting existing version failed"
);
meta = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading deleted secret metadata failed"
);
assertNull(
@@ -505,11 +505,11 @@ class HTTPVaultConnectorIT {
// Now destroy it.
assertDoesNotThrow(
() -> connector.destroySecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
() -> connector.kv2().destroyVersions(MOUNT_KV2, SECRET2_KEY, 1),
"Destroying existing version failed"
);
meta = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading destroyed secret metadata failed"
);
assertTrue(
@@ -519,11 +519,11 @@ class HTTPVaultConnectorIT {
// Delete latest version.
assertDoesNotThrow(
() -> connector.deleteLatestSecretVersion(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().deleteLatestVersion(MOUNT_KV2, SECRET2_KEY),
"Deleting latest version failed"
);
meta = assertDoesNotThrow(
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading deleted secret metadata failed"
);
assertNotNull(
@@ -533,12 +533,12 @@ class HTTPVaultConnectorIT {
// Delete all versions.
assertDoesNotThrow(
() -> connector.deleteAllSecretVersions(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().deleteAllVersions(MOUNT_KV2, SECRET2_KEY),
"Deleting latest version failed"
);
assertThrows(
InvalidResponseException.class,
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
"Reading metadata of deleted secret should not succeed"
);
}
@@ -620,21 +620,21 @@ class HTTPVaultConnectorIT {
// Try unauthorized access first.
assumeFalse(connector.isAuthorized());
assertThrows(AuthorizationRequiredException.class, () -> connector.listAppRoles());
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().listRoles());
assertThrows(AuthorizationRequiredException.class, () -> connector.listAppRoleSecrets(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().listSecrets(""));
// Authorize.
authRoot();
assumeTrue(connector.isAuthorized());
// Verify pre-existing rules.
List<String> res = assertDoesNotThrow(() -> connector.listAppRoles(), "Role listing failed");
List<String> res = assertDoesNotThrow(() -> connector.appRole().listRoles(), "Role listing failed");
assertEquals(2, res.size(), "Unexpected number of AppRoles");
assertTrue(res.containsAll(List.of(APPROLE_ROLE_NAME, APPROLE_ROLE2_NAME)), "Pre-configured roles not listed");
// Check secret IDs.
res = assertDoesNotThrow(() -> connector.listAppRoleSecrets(APPROLE_ROLE_NAME), "AppRole secret listing failed");
res = assertDoesNotThrow(() -> connector.appRole().listSecrets(APPROLE_ROLE_NAME), "AppRole secret listing failed");
assertEquals(List.of(APPROLE_SECRET_ACCESSOR), res, "Pre-configured AppRole secret not listed");
}
@@ -647,14 +647,14 @@ class HTTPVaultConnectorIT {
void createAppRoleTest() {
// Try unauthorized access first.
assumeFalse(connector.isAuthorized());
assertThrows(AuthorizationRequiredException.class, () -> connector.createAppRole(new AppRole()));
assertThrows(AuthorizationRequiredException.class, () -> connector.lookupAppRole(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.deleteAppRole(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.getAppRoleID(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.setAppRoleID("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.createAppRoleSecret("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.lookupAppRoleSecret("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.destroyAppRoleSecret("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().create(new AppRole()));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().lookup(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().delete(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().getRoleID(""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().setRoleID("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().createSecret("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().lookupSecret("", ""));
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().destroySecret("", ""));
// Authorize.
authRoot();
@@ -666,23 +666,23 @@ class HTTPVaultConnectorIT {
AppRole role = AppRole.builder(roleName).build();
// Create role.
boolean createRes = assertDoesNotThrow(() -> connector.createAppRole(role), "Role creation failed");
boolean createRes = assertDoesNotThrow(() -> connector.appRole().create(role), "Role creation failed");
assertTrue(createRes, "Role creation failed");
// Lookup role.
AppRoleResponse res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName), "Role lookup failed");
AppRoleResponse res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName), "Role lookup failed");
assertNotNull(res.getRole(), "Role lookup returned no role");
// Lookup role ID.
String roleID = assertDoesNotThrow(() -> connector.getAppRoleID(roleName), "Role ID lookup failed");
String roleID = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName), "Role ID lookup failed");
assertNotEquals("", roleID, "Role ID lookup returned empty ID");
// Set custom role ID.
String roleID2 = "custom-role-id";
assertDoesNotThrow(() -> connector.setAppRoleID(roleName, roleID2), "Setting custom role ID failed");
assertDoesNotThrow(() -> connector.appRole().setRoleID(roleName, roleID2), "Setting custom role ID failed");
// Verify role ID.
String res2 = assertDoesNotThrow(() -> connector.getAppRoleID(roleName), "Role ID lookup failed");
String res2 = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName), "Role ID lookup failed");
assertEquals(roleID2, res2, "Role ID lookup returned wrong ID");
// Update role model with custom flags.
@@ -691,44 +691,44 @@ class HTTPVaultConnectorIT {
.build();
// Create role.
boolean res3 = assertDoesNotThrow(() -> connector.createAppRole(role2), "Role creation failed");
boolean res3 = assertDoesNotThrow(() -> connector.appRole().create(role2), "Role creation failed");
assertTrue(res3, "No result given");
// Lookup updated role.
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName), "Role lookup failed");
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName), "Role lookup failed");
assertNotNull(res.getRole(), "Role lookup returned no role");
assertEquals(321, res.getRole().getTokenPeriod(), "Token period not set for role");
// Create role by name.
String roleName2 = "RoleByName";
assertDoesNotThrow(() -> connector.createAppRole(roleName2), "Creation of role by name failed");
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName2), "Creation of role by name failed");
assertDoesNotThrow(() -> connector.appRole().create(roleName2), "Creation of role by name failed");
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName2), "Creation of role by name failed");
assertNotNull(res.getRole(), "Role lookuo returned not value");
// Create role by name with custom ID.
String roleName3 = "RoleByName";
String roleID3 = "RolyByNameID";
assertDoesNotThrow(() -> connector.createAppRole(roleName3, roleID3), "Creation of role by name failed");
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName3), "Creation of role by name failed");
assertDoesNotThrow(() -> connector.appRole().create(roleName3, roleID3), "Creation of role by name failed");
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName3), "Creation of role by name failed");
assertNotNull(res.getRole(), "Role lookuo returned not value");
res2 = assertDoesNotThrow(() -> connector.getAppRoleID(roleName3), "Creation of role by name failed");
res2 = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName3), "Creation of role by name failed");
assertEquals(roleID3, res2, "Role lookuo returned wrong ID");
// Create role by name with policies.
assertDoesNotThrow(
() -> connector.createAppRole(roleName3, Collections.singletonList("testpolicy")),
() -> connector.appRole().create(roleName3, Collections.singletonList("testpolicy")),
"Creation of role by name failed"
);
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName3), "Creation of role by name failed");
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName3), "Creation of role by name failed");
// Note: As of Vault 0.8.3 default policy is not added automatically, so this test should return 1, not 2.
assertEquals(List.of("testpolicy"), res.getRole().getTokenPolicies(), "Role lookup returned unexpected policies");
// Delete role.
assertDoesNotThrow(() -> connector.deleteAppRole(roleName3), "Deletion of role failed");
assertDoesNotThrow(() -> connector.appRole().delete(roleName3), "Deletion of role failed");
assertThrows(
InvalidResponseException.class,
() -> connector.lookupAppRole(roleName3),
() -> connector.appRole().lookup(roleName3),
"Deleted role could be looked up"
);
}
@@ -745,7 +745,7 @@ class HTTPVaultConnectorIT {
// Create default (random) secret for existing role.
AppRoleSecretResponse res = assertDoesNotThrow(
() -> connector.createAppRoleSecret(APPROLE_ROLE_NAME),
() -> connector.appRole().createSecret(APPROLE_ROLE_NAME),
"AppRole secret creation failed"
);
assertNotNull(res.getSecret(), "No secret returned");
@@ -753,26 +753,26 @@ class HTTPVaultConnectorIT {
// Create secret with custom ID.
String secretID = "customSecretId";
res = assertDoesNotThrow(
() -> connector.createAppRoleSecret(APPROLE_ROLE_NAME, secretID),
() -> connector.appRole().createSecret(APPROLE_ROLE_NAME, secretID),
"AppRole secret creation failed"
);
assertEquals(secretID, res.getSecret().getId(), "Unexpected secret ID returned");
// Lookup secret.
res = assertDoesNotThrow(
() -> connector.lookupAppRoleSecret(APPROLE_ROLE_NAME, secretID),
() -> connector.appRole().lookupSecret(APPROLE_ROLE_NAME, secretID),
"AppRole secret lookup failed"
);
assertNotNull(res.getSecret(), "No secret information returned");
// Destroy secret.
assertDoesNotThrow(
() -> connector.destroyAppRoleSecret(APPROLE_ROLE_NAME, secretID),
() -> connector.appRole().destroySecret(APPROLE_ROLE_NAME, secretID),
"AppRole secret destruction failed"
);
assertThrows(
InvalidResponseException.class,
() -> connector.lookupAppRoleSecret(APPROLE_ROLE_NAME, secretID),
() -> connector.appRole().lookupSecret(APPROLE_ROLE_NAME, secretID),
"Destroyed AppRole secret successfully read"
);
}
@@ -825,7 +825,7 @@ class HTTPVaultConnectorIT {
.build();
// Create token.
AuthResponse res = assertDoesNotThrow(() -> connector.createToken(token), "Token creation failed");
AuthResponse res = assertDoesNotThrow(() -> connector.token().create(token), "Token creation failed");
assertNotNull(res, "No result given");
assertEquals("test-id", res.getAuth().getClientToken(), "Invalid token ID returned");
assertEquals(List.of("root"), res.getAuth().getPolicies(), "Expected inherited root policy");
@@ -847,7 +847,7 @@ class HTTPVaultConnectorIT {
.withoutDefaultPolicy()
.withMeta("foo", "bar")
.build();
res = assertDoesNotThrow(() -> connector.createToken(token2), "Token creation failed");
res = assertDoesNotThrow(() -> connector.token().create(token2), "Token creation failed");
assertEquals("test-id2", res.getAuth().getClientToken(), "Invalid token ID returned");
assertEquals(List.of("testpolicy"), res.getAuth().getPolicies(), "Invalid policies returned");
assertNotNull(res.getAuth().getMetadata(), "Metadata not given");
@@ -866,7 +866,7 @@ class HTTPVaultConnectorIT {
.build();
InvalidResponseException e = assertThrows(
InvalidResponseException.class,
() -> connector.createToken(token3),
() -> connector.token().create(token3),
"Overwriting token should fail as of Vault 0.8.0"
);
assertEquals(400, e.getStatusCode());
@@ -880,7 +880,7 @@ class HTTPVaultConnectorIT {
.withoutDefaultPolicy()
.withType(Token.Type.BATCH)
.build();
res = assertDoesNotThrow(() -> connector.createToken(token4), "Token creation failed");
res = assertDoesNotThrow(() -> connector.token().create(token4), "Token creation failed");
assertTrue(
// Expecting batch token. "hvb." Prefix as of Vault 1.10, "b." before.
res.getAuth().getClientToken().startsWith("b.") || res.getAuth().getClientToken().startsWith("hvb."),
@@ -908,12 +908,12 @@ class HTTPVaultConnectorIT {
.withId("my-token")
.withType(Token.Type.SERVICE)
.build();
assertDoesNotThrow(() -> connector.createToken(token), "Token creation failed");
assertDoesNotThrow(() -> connector.token().create(token), "Token creation failed");
authRoot();
assumeTrue(connector.isAuthorized());
TokenResponse res = assertDoesNotThrow(() -> connector.lookupToken("my-token"), "Token creation failed");
TokenResponse res = assertDoesNotThrow(() -> connector.token().lookup("my-token"), "Token creation failed");
assertEquals(token.getId(), res.getData().getId(), "Unexpected token ID");
assertEquals(1, res.getData().getPolicies().size(), "Unexpected number of policies");
assertTrue(res.getData().getPolicies().contains("root"), "Unexpected policy");
@@ -936,14 +936,14 @@ class HTTPVaultConnectorIT {
final TokenRole role = TokenRole.builder().build();
boolean creationRes = assertDoesNotThrow(
() -> connector.createOrUpdateTokenRole(roleName, role),
() -> connector.token().createOrUpdateRole(roleName, role),
"Token role creation failed"
);
assertTrue(creationRes, "Token role creation failed");
// Read the role.
TokenRoleResponse res = assertDoesNotThrow(
() -> connector.readTokenRole(roleName),
() -> connector.token().readRole(roleName),
"Reading token role failed"
);
assertNotNull(res, "Token role response must not be null");
@@ -963,12 +963,12 @@ class HTTPVaultConnectorIT {
.build();
creationRes = assertDoesNotThrow(
() -> connector.createOrUpdateTokenRole(role2),
() -> connector.token().createOrUpdateRole(role2),
"Token role update failed"
);
assertTrue(creationRes, "Token role update failed");
res = assertDoesNotThrow(() -> connector.readTokenRole(roleName), "Reading token role failed");
res = assertDoesNotThrow(() -> connector.token().readRole(roleName), "Reading token role failed");
assertNotNull(res, "Token role response must not be null");
assertNotNull(res.getData(), "Token role must not be null");
assertEquals(roleName, res.getData().getName(), "Token role name not as expected");
@@ -977,15 +977,15 @@ class HTTPVaultConnectorIT {
assertEquals(42, res.getData().getTokenNumUses(), "Unexpected number of token uses after update");
// List roles.
List<String> listRes = assertDoesNotThrow(() -> connector.listTokenRoles(), "Listing token roles failed");
List<String> listRes = assertDoesNotThrow(() -> connector.token().listRoles(), "Listing token roles failed");
assertNotNull(listRes, "Token role list must not be null");
assertEquals(List.of(roleName), listRes, "Unexpected token role list");
// Delete the role.
creationRes = assertDoesNotThrow(() -> connector.deleteTokenRole(roleName), "Token role deletion failed");
creationRes = assertDoesNotThrow(() -> connector.token().deleteRole(roleName), "Token role deletion failed");
assertTrue(creationRes, "Token role deletion failed");
assertThrows(InvalidResponseException.class, () -> connector.readTokenRole(roleName), "Reading nonexistent token role should fail");
assertThrows(InvalidResponseException.class, () -> connector.listTokenRoles(), "Listing nonexistent token roles should fail");
assertThrows(InvalidResponseException.class, () -> connector.token().readRole(roleName), "Reading nonexistent token role should fail");
assertThrows(InvalidResponseException.class, () -> connector.token().listRoles(), "Listing nonexistent token roles should fail");
}
}
@@ -1000,14 +1000,14 @@ class HTTPVaultConnectorIT {
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitEncrypt("my-key", "dGVzdCBtZQ=="),
() -> connector.transit().encrypt("my-key", "dGVzdCBtZQ=="),
"Failed to encrypt via transit"
);
assertNotNull(transitResponse.getCiphertext());
assertTrue(transitResponse.getCiphertext().startsWith("vault:v1:"));
transitResponse = assertDoesNotThrow(
() -> connector.transitEncrypt("my-key", "test me".getBytes(UTF_8)),
() -> connector.transit().encrypt("my-key", "test me".getBytes(UTF_8)),
"Failed to encrypt binary data via transit"
);
assertNotNull(transitResponse.getCiphertext());
@@ -1022,7 +1022,7 @@ class HTTPVaultConnectorIT {
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitDecrypt("my-key", "vault:v1:1mhLVkBAR2nrFtIkJF/qg57DWfRj0FWgR6tvkGO8XOnL6sw="),
() -> connector.transit().decrypt("my-key", "vault:v1:1mhLVkBAR2nrFtIkJF/qg57DWfRj0FWgR6tvkGO8XOnL6sw="),
"Failed to decrypt via transit"
);
@@ -1036,21 +1036,21 @@ class HTTPVaultConnectorIT {
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitHash("sha2-512", "dGVzdCBtZQ=="),
() -> connector.transit().hash("sha2-512", "dGVzdCBtZQ=="),
"Failed to hash via transit"
);
assertEquals("7677af0ee4effaa9f35e9b1e82d182f79516ab8321786baa23002de7c06851059492dd37d5fc3791f17d81d4b58198d24a6fd8bbd62c42c1c30b371da500f193", transitResponse.getSum());
TransitResponse transitResponseBase64 = assertDoesNotThrow(
() -> connector.transitHash("sha2-256", "dGVzdCBtZQ==", "base64"),
() -> connector.transit().hash("sha2-256", "dGVzdCBtZQ==", "base64"),
"Failed to hash via transit with base64 output"
);
assertEquals("5DfYkW7cvGLkfy36cXhqmZcygEy9HpnFNB4WWXKOl1M=", transitResponseBase64.getSum());
transitResponseBase64 = assertDoesNotThrow(
() -> connector.transitHash("sha2-256", "test me".getBytes(UTF_8), "base64"),
() -> connector.transit().hash("sha2-256", "test me".getBytes(UTF_8), "base64"),
"Failed to hash binary data via transit"
);
@@ -1072,7 +1072,7 @@ class HTTPVaultConnectorIT {
assumeTrue(connector.isAuthorized());
List<AuthBackend> supportedBackends = assertDoesNotThrow(
() -> connector.getAuthBackends(),
() -> connector.sys().getAuthBackends(),
"Could not list supported auth backends"
);
@@ -1132,22 +1132,22 @@ class HTTPVaultConnectorIT {
@Test
@DisplayName("Seal test")
void sealTest() throws VaultConnectorException {
SealResponse sealStatus = connector.sealStatus();
SealResponse sealStatus = connector.sys().sealStatus();
assumeFalse(sealStatus.isSealed());
// Unauthorized sealing should fail.
assertThrows(VaultConnectorException.class, connector::seal, "Unauthorized sealing succeeded");
assertThrows(VaultConnectorException.class, () -> connector.sys().seal(), "Unauthorized sealing succeeded");
assertFalse(sealStatus.isSealed(), "Vault sealed, although sealing failed");
// Root user should be able to seal.
authRoot();
assumeTrue(connector.isAuthorized());
assertDoesNotThrow(connector::seal, "Sealing failed");
sealStatus = connector.sealStatus();
assertDoesNotThrow(() -> connector.sys().seal(), "Sealing failed");
sealStatus = connector.sys().sealStatus();
assertTrue(sealStatus.isSealed(), "Vault not sealed");
sealStatus = connector.unseal(KEY2);
sealStatus = connector.sys().unseal(KEY2);
assertTrue(sealStatus.isSealed(), "Vault unsealed with only 1 key");
sealStatus = connector.unseal(KEY3);
sealStatus = connector.sys().unseal(KEY3);
assertFalse(sealStatus.isSealed(), "Vault not unsealed");
}
@@ -1157,7 +1157,7 @@ class HTTPVaultConnectorIT {
@Test
@DisplayName("Health test")
void healthTest() {
HealthResponse res = assertDoesNotThrow(connector::getHealth, "Retrieving health status failed");
HealthResponse res = assertDoesNotThrow(() -> connector.sys().getHealth(), "Retrieving health status failed");
assertNotNull(res, "Health response should be set");
assertEquals(VAULT_VERSION, res.getVersion(), "Unexpected version");
assertTrue(res.isInitialized(), "Unexpected init status");
@@ -1166,11 +1166,11 @@ class HTTPVaultConnectorIT {
// No seal vault and verify correct status.
authRoot();
assertDoesNotThrow(connector::seal, "Unexpected exception on sealing");
SealResponse sealStatus = assertDoesNotThrow(connector::sealStatus);
assertDoesNotThrow(() -> connector.sys().seal(), "Unexpected exception on sealing");
SealResponse sealStatus = assertDoesNotThrow(() -> connector.sys().sealStatus());
assumeTrue(sealStatus.isSealed());
connector.resetAuth(); // Should work unauthenticated
res = assertDoesNotThrow(connector::getHealth, "Retrieving health status failed when sealed");
res = assertDoesNotThrow(() -> connector.sys().getHealth(), "Retrieving health status failed when sealed");
assertTrue(res.isSealed(), "Unexpected seal status");
}

View File

@@ -54,51 +54,51 @@ class HTTPVaultConnectorTest {
*/
@Test
void requestExceptionTest(WireMockRuntimeInfo wireMock) throws IOException, URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build();
try (var connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build()) {
// Test invalid response code.
final int responseCode = 400;
mockHttpResponse(responseCode, "", "application/json");
VaultConnectorException e = assertThrows(
InvalidResponseException.class,
() -> connector.sys().getHealth(),
"Querying health status succeeded on invalid instance"
);
assertEquals("Invalid response code", e.getMessage(), "Unexpected exception message");
assertEquals(responseCode, ((InvalidResponseException) e).getStatusCode(), "Unexpected status code in exception");
assertNull(((InvalidResponseException) e).getResponse(), "Response message where none was expected");
// Test invalid response code.
final int responseCode = 400;
mockHttpResponse(responseCode, "", "application/json");
VaultConnectorException e = assertThrows(
InvalidResponseException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
);
assertEquals("Invalid response code", e.getMessage(), "Unexpected exception message");
assertEquals(responseCode, ((InvalidResponseException) e).getStatusCode(), "Unexpected status code in exception");
assertNull(((InvalidResponseException) e).getResponse(), "Response message where none was expected");
// Simulate permission denied response.
mockHttpResponse(responseCode, "{\"errors\":[\"permission denied\"]}", "application/json");
assertThrows(
PermissionDeniedException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
);
// Simulate permission denied response.
mockHttpResponse(responseCode, "{\"errors\":[\"permission denied\"]}", "application/json");
assertThrows(
PermissionDeniedException.class,
() -> connector.sys().getHealth(),
"Querying health status succeeded on invalid instance"
);
}
// Test exception thrown during request.
try (ServerSocket s = new ServerSocket(0)) {
connector = HTTPVaultConnector.builder("http://localst:" + s.getLocalPort() + "/").withTimeout(250).build();
try (ServerSocket s = new ServerSocket(0);
var connector = HTTPVaultConnector.builder("http://localst:" + s.getLocalPort() + "/").withTimeout(250).build()) {
var e = assertThrows(
ConnectionException.class,
() -> connector.sys().getHealth(),
"Querying health status succeeded on invalid instance"
);
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
assertInstanceOf(IOException.class, e.getCause(), "Unexpected cause");
}
e = assertThrows(
ConnectionException.class,
connector::getHealth,
"Querying health status succeeded on invalid instance"
);
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
assertInstanceOf(IOException.class, e.getCause(), "Unexpected cause");
// Now simulate a failing request that succeeds on second try.
connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withNumberOfRetries(1).withTimeout(250).build();
stubFor(
WireMock.any(anyUrl())
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(200).withBody("{}").withHeader("Content-Type", "application/json"))
);
assertDoesNotThrow(connector::getHealth, "Request failed unexpectedly");
try (var connector3 = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withNumberOfRetries(1).withTimeout(250).build()) {
stubFor(
WireMock.any(anyUrl())
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(200).withBody("{}").withHeader("Content-Type", "application/json"))
);
assertDoesNotThrow(() -> connector3.sys().getHealth(), "Request failed unexpectedly");
}
}
/**
@@ -160,7 +160,7 @@ class HTTPVaultConnectorTest {
}
ConnectionException e = assertThrows(
ConnectionException.class,
connector::sealStatus,
() -> connector.sys().sealStatus(),
"Querying seal status succeeded on invalid instance"
);
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
@@ -178,7 +178,7 @@ class HTTPVaultConnectorTest {
}
ConnectionException e = assertThrows(
ConnectionException.class,
connector::getHealth,
() -> connector.sys().getHealth(),
"Querying health status succeeded on invalid instance"
);
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
@@ -196,21 +196,21 @@ class HTTPVaultConnectorTest {
mockHttpResponse(200, "invalid", "application/json");
// Now test the methods.
assertParseError(connector::sealStatus, "sealStatus() succeeded on invalid instance");
assertParseError(() -> connector.unseal("key"), "unseal() succeeded on invalid instance");
assertParseError(connector::getHealth, "getHealth() succeeded on invalid instance");
assertParseError(connector::getAuthBackends, "getAuthBackends() succeeded on invalid instance");
assertParseError(() -> connector.sys().sealStatus(), "sys().sealStatus() succeeded on invalid instance");
assertParseError(() -> connector.sys().unseal("key"), "sys().unseal() succeeded on invalid instance");
assertParseError(() -> connector.sys().getHealth(), "sys().getHealth() succeeded on invalid instance");
assertParseError(() -> connector.sys().getAuthBackends(), "sys().getAuthBackends() succeeded on invalid instance");
assertParseError(() -> connector.authToken("token"), "authToken() succeeded on invalid instance");
assertParseError(() -> connector.lookupAppRole("roleName"), "lookupAppRole() succeeded on invalid instance");
assertParseError(() -> connector.getAppRoleID("roleName"), "getAppRoleID() succeeded on invalid instance");
assertParseError(() -> connector.createAppRoleSecret("roleName"), "createAppRoleSecret() succeeded on invalid instance");
assertParseError(() -> connector.lookupAppRoleSecret("roleName", "secretID"), "lookupAppRoleSecret() succeeded on invalid instance");
assertParseError(connector::listAppRoles, "listAppRoles() succeeded on invalid instance");
assertParseError(() -> connector.listAppRoleSecrets("roleName"), "listAppRoleSecrets() succeeded on invalid instance");
assertParseError(() -> connector.appRole().lookup("roleName"), "appRole().lookup() succeeded on invalid instance");
assertParseError(() -> connector.appRole().getRoleID("roleName"), "appRole().getRoleID() succeeded on invalid instance");
assertParseError(() -> connector.appRole().createSecret("roleName"), "appRole().createSecret() succeeded on invalid instance");
assertParseError(() -> connector.appRole().lookupSecret("roleName", "secretID"), "appRole().lookupSecret() succeeded on invalid instance");
assertParseError(() -> connector.appRole().listRoles(), "appRole().listRoles() succeeded on invalid instance");
assertParseError(() -> connector.appRole().listSecrets("roleName"), "appRole().listSecrets() succeeded on invalid instance");
assertParseError(() -> connector.read("key"), "read() succeeded on invalid instance");
assertParseError(() -> connector.list("path"), "list() succeeded on invalid instance");
assertParseError(() -> connector.renew("leaseID"), "renew() succeeded on invalid instance");
assertParseError(() -> connector.lookupToken("token"), "lookupToken() succeeded on invalid instance");
assertParseError(() -> connector.token().lookup("token"), "token().lookup() succeeded on invalid instance");
}
private void assertParseError(Executable executable, String message) {
@@ -232,32 +232,32 @@ class HTTPVaultConnectorTest {
// Now test the methods expecting a 204.
assertThrows(
InvalidResponseException.class,
() -> connector.createAppRole("appID", Collections.singletonList("policy")),
"createAppRole() with 200 response succeeded"
() -> connector.appRole().create("appID", Collections.singletonList("policy")),
"appRole().create() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.deleteAppRole("roleName"),
"deleteAppRole() with 200 response succeeded"
() -> connector.delete("roleName"),
"appRole().delete() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.setAppRoleID("roleName", "roleID"),
"setAppRoleID() with 200 response succeeded"
() -> connector.appRole().setRoleID("roleName", "roleID"),
"appRole().setRoleID() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.destroyAppRoleSecret("roleName", "secretID"),
"destroyAppRoleSecret() with 200 response succeeded"
() -> connector.appRole().destroySecret("roleName", "secretID"),
"appRole().destroySecret() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.destroyAppRoleSecret("roleName", "secretUD"),
"destroyAppRoleSecret() with 200 response succeeded"
() -> connector.appRole().destroySecret("roleName", "secretUD"),
"appRole().destroySecret() with 200 response succeeded"
);
assertThrows(