diff --git a/README.md b/README.md
index d7569f9..de8891b 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/pom.xml b/pom.xml
index ab5fd50..7d59b43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
de.stklcode.jvault
jvault-connector
- 1.5.3-SNAPSHOT
+ 2.0.0-SNAPSHOT
jar
diff --git a/src/main/java/de/stklcode/jvault/connector/AppRoleClient.java b/src/main/java/de/stklcode/jvault/connector/AppRoleClient.java
new file mode 100644
index 0000000..58dbfa9
--- /dev/null
+++ b/src/main/java/de/stklcode/jvault/connector/AppRoleClient.java
@@ -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 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 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 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 listSecrets(final String roleName) throws VaultConnectorException;
+}
diff --git a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java
index 33c23e8..e65264b 100644
--- a/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java
+++ b/src/main/java/de/stklcode/jvault/connector/HTTPVaultConnector.java
@@ -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 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 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 listAppRoles() throws VaultConnectorException {
- requireAuth();
-
- SecretListResponse secrets = request.get(
- AUTH_APPROLE + "/role?list=true",
- emptyMap(),
- token,
- SecretListResponse.class
- );
-
- return secrets.getKeys();
- }
-
- @Override
- public final List 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 listRoles() throws VaultConnectorException {
+ requireAuth();
+
+ SecretListResponse secrets = request.get(
+ AUTH_APPROLE + "/role?list=true",
+ emptyMap(),
+ token,
+ SecretListResponse.class
+ );
+
+ return secrets.getKeys();
+ }
+
+ @Override
+ public final List 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 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 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 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 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 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());
+ }
+ }
}
diff --git a/src/main/java/de/stklcode/jvault/connector/KV2Client.java b/src/main/java/de/stklcode/jvault/connector/KV2Client.java
new file mode 100644
index 0000000..e832957
--- /dev/null
+++ b/src/main/java/de/stklcode/jvault/connector/KV2Client.java
@@ -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.
+ *
+ * Path {@code /data/} 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.
+ *
+ * Path {@code /data/} 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 data) throws VaultConnectorException {
+ return writeData(mount, key, data, null);
+ }
+
+ /**
+ * Write secret to Vault.
+ *
+ * Path {@code /data/} 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 data,
+ final Integer cas) throws VaultConnectorException;
+
+ /**
+ * Retrieve secret data from Vault.
+ *
+ * Path {@code /data/} 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.
+ *
+ * Path {@code /metadata/} 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.
+ *
+ * Path {@code /metadata/} 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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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;
+}
diff --git a/src/main/java/de/stklcode/jvault/connector/SysClient.java b/src/main/java/de/stklcode/jvault/connector/SysClient.java
new file mode 100644
index 0000000..18538ca
--- /dev/null
+++ b/src/main/java/de/stklcode/jvault/connector/SysClient.java
@@ -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 getAuthBackends() throws VaultConnectorException;
+
+}
diff --git a/src/main/java/de/stklcode/jvault/connector/TokenClient.java b/src/main/java/de/stklcode/jvault/connector/TokenClient.java
new file mode 100644
index 0000000..74beaa7
--- /dev/null
+++ b/src/main/java/de/stklcode/jvault/connector/TokenClient.java
@@ -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 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;
+}
diff --git a/src/main/java/de/stklcode/jvault/connector/TransitClient.java b/src/main/java/de/stklcode/jvault/connector/TransitClient.java
new file mode 100644
index 0000000..5b8f329
--- /dev/null
+++ b/src/main/java/de/stklcode/jvault/connector/TransitClient.java
@@ -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);
+ }
+}
diff --git a/src/main/java/de/stklcode/jvault/connector/VaultConnector.java b/src/main/java/de/stklcode/jvault/connector/VaultConnector.java
index 6d883e3..86cc950 100644
--- a/src/main/java/de/stklcode/jvault/connector/VaultConnector.java
+++ b/src/main/java/de/stklcode/jvault/connector/VaultConnector.java
@@ -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 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 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 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 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 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.
- *
- * Path {@code /data/} 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.
- *
- * Path {@code /data/} 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 data) throws VaultConnectorException {
- return writeSecretData(mount, key, data, null);
- }
-
- /**
- * Write secret to Vault.
- *
- * Path {@code /data/} 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 data,
- final Integer cas) throws VaultConnectorException;
-
- /**
- * Retrieve secret data from Vault.
- *
- * Path {@code /data/} 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.
- *
- * Path {@code /metadata/} 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.
- *
- * Path {@code /metadata/} 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.
- *
- * 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.
- *
- * 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.
- *
- * 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 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);
}
+
}
diff --git a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorIT.java b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorIT.java
index 7423556..8540dc2 100644
--- a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorIT.java
+++ b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorIT.java
@@ -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 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 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 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 res = assertDoesNotThrow(() -> connector.listAppRoles(), "Role listing failed");
+ List 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 listRes = assertDoesNotThrow(() -> connector.listTokenRoles(), "Listing token roles failed");
+ List 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 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");
}
diff --git a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java
index a072a4f..4bfb4dc 100644
--- a/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java
+++ b/src/test/java/de/stklcode/jvault/connector/HTTPVaultConnectorTest.java
@@ -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(