Compare commits

9 Commits

Author SHA1 Message Date
0067bf3f7f deps: update jackson to 3.0.0-rc9
All checks were successful
CI / build (17) (push) Successful in 42s
CI / build (true, 21) (push) Successful in 41s
Migrate packages from com.fasterxml.jackson to tools.jackson, adjust
exception handling and remove JSR310 module and replace with new
JavaTimeFeature flags.
2025-09-09 13:39:55 +02:00
ac23b15e5c test: update equalsverifier to 4.1
All checks were successful
CI / build (17) (push) Successful in 38s
CI / build (true, 21) (push) Successful in 37s
2025-09-09 13:21:41 +02:00
2d86b219a4 add java.io.Serial annotations to serialVersionUID fields 2025-09-09 13:21:37 +02:00
150970bfc3 require Java 17 or later (#100) 2025-09-09 13:21:12 +02:00
b2dbf4d617 prepare for next development iteration
All checks were successful
CI / build (11) (push) Successful in 41s
CI / build (17) (push) Successful in 40s
CI / build (true, 21) (push) Successful in 32s
2025-09-09 11:50:06 +02:00
635cf19e54 prepare release v1.5.3
All checks were successful
CI / build-with-it (11, 1.2.0) (push) Successful in 56s
CI / build-with-it (11, 1.20.3) (push) Successful in 1m12s
CI / build-with-it (17, 1.2.0) (push) Successful in 53s
CI / build-with-it (17, 1.20.3) (push) Successful in 1m6s
CI / build-with-it (21, 1.2.0) (push) Successful in 53s
CI / build-with-it (true, 21, 1.20.3) (push) Successful in 1m1s
2025-09-09 11:47:52 +02:00
f5e40ca032 test: run IT against Vault 1.20.3
All checks were successful
CI / build-with-it (11, 1.2.0) (push) Successful in 54s
CI / build-with-it (11, 1.20.3) (push) Successful in 1m8s
CI / build-with-it (17, 1.2.0) (push) Successful in 50s
CI / build-with-it (17, 1.20.3) (push) Successful in 1m3s
CI / build-with-it (21, 1.2.0) (push) Successful in 49s
CI / build-with-it (true, 21, 1.20.3) (push) Successful in 58s
2025-09-09 11:39:32 +02:00
15f514f877 add token_bound_cidrs field to AppRoleSecret model (#110)
All checks were successful
CI / build-with-it (11, 1.2.0) (push) Successful in 54s
CI / build-with-it (11, 1.20.0) (push) Successful in 1m9s
CI / build-with-it (17, 1.2.0) (push) Successful in 49s
CI / build-with-it (17, 1.20.0) (push) Successful in 1m9s
CI / build-with-it (21, 1.2.0) (push) Successful in 51s
CI / build-with-it (true, 21, 1.20.0) (push) Successful in 56s
2025-09-08 10:25:39 +02:00
f79ed98986 encode user-provided URL parts (#109)
All checks were successful
CI / build-with-it (11, 1.2.0) (push) Successful in 50s
CI / build-with-it (11, 1.20.0) (push) Successful in 1m4s
CI / build-with-it (17, 1.2.0) (push) Successful in 46s
CI / build-with-it (17, 1.20.0) (push) Successful in 1m2s
CI / build-with-it (21, 1.2.0) (push) Successful in 46s
CI / build-with-it (true, 21, 1.20.0) (push) Successful in 54s
In various methods we use user-provided values like role names or lease
ids as parts of the API request path.

Apply URL encoding to these paths that are not expected to contain any
path separators or query args.
2025-09-05 09:46:48 +02:00
62 changed files with 1341 additions and 1449 deletions

View File

@@ -14,11 +14,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
jdk: [ 11, 17, 21 ] jdk: [ 17, 21 ]
vault: [ '1.2.0', '1.20.0' ] vault: [ '1.2.0', '1.20.3' ]
include: include:
- jdk: 21 - jdk: 21
vault: '1.20.0' vault: '1.20.3'
analysis: true analysis: true
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
matrix: matrix:
jdk: [ 11, 17, 21 ] jdk: [ 17, 21 ]
include: include:
- jdk: 21 - jdk: 21
analysis: true analysis: true

View File

@@ -1,18 +1,25 @@
## unreleased ## 1.5.3 (2025-09-09)
### Dependencies ### Dependencies
* Updated Jackson to 2.20.0 (#106) * Updated Jackson to 2.20.0 (#106)
### Improvements ### Improvements
* Extract API paths into a utility class (#108) * Extract API paths into a utility class (#108)
* Encode user-provided URL parts (#109)
* Add `token_bound_cidrs` field to `AppRoleSecret` model (#110)
### Fix ### Fix
* Prevent potential off-by-1 error in internal `mapOf()` helper (#107) * Prevent potential off-by-1 error in internal `mapOf()` helper (#107)
## 1.5.2 (2025-07-16) ## 1.5.2 (2025-07-16)
### Breaking
* Requires Java 17 or later (#100)
* Required Jackson 3
### Dependencies ### Dependencies
* Updated Jackson to 2.19.1 (#101) * Updated Jackson to 3.0.0
### Fix ### Fix
* Use `Long` for numeric TTL fields (#103) (#104) * Use `Long` for numeric TTL fields (#103) (#104)

View File

@@ -40,7 +40,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
<dependency> <dependency>
<groupId>de.stklcode.jvault</groupId> <groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId> <artifactId>jvault-connector</artifactId>
<version>1.5.2</version> <version>1.5.3</version>
</dependency> </dependency>
``` ```
@@ -109,11 +109,11 @@ Token token = Token.builder()
.withDisplayName("new test token") .withDisplayName("new test token")
.withPolicies("pol1", "pol2") .withPolicies("pol1", "pol2")
.build(); .build();
vault.token().create(token); vault.createToken(token);
// Create AppRole credentials // Create AppRole credentials
vault.appRole().create("testrole", policyList); vault.createAppRole("testrole", policyList);
AppRoleSecretResponse secret = vault.appRole().createSecret("testrole"); AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
``` ```
## Links ## Links

22
pom.xml
View File

@@ -47,14 +47,9 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>2.20.0</version> <version>3.0.0-rc9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.20.0</version>
</dependency> </dependency>
<dependency> <dependency>
@@ -90,7 +85,7 @@
<dependency> <dependency>
<groupId>nl.jqno.equalsverifier</groupId> <groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId> <artifactId>equalsverifier</artifactId>
<version>3.19.4</version> <version>4.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -109,7 +104,7 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version> <version>3.14.0</version>
<configuration> <configuration>
<release>11</release> <release>17</release>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
@@ -129,8 +124,7 @@
<configuration> <configuration>
<argLine> <argLine>
@{argLine} @{argLine}
--add-opens --add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=tools.jackson.databind
de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.databind
</argLine> </argLine>
</configuration> </configuration>
</plugin> </plugin>
@@ -178,7 +172,7 @@
<plugin> <plugin>
<groupId>org.sonarsource.scanner.maven</groupId> <groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId> <artifactId>sonar-maven-plugin</artifactId>
<version> 5.2.0.4988</version> <version>5.2.0.4988</version>
</plugin> </plugin>
</plugins> </plugins>
</pluginManagement> </pluginManagement>
@@ -200,7 +194,7 @@
<version>[3.6.3,)</version> <version>[3.6.3,)</version>
</requireMavenVersion> </requireMavenVersion>
<requireJavaVersion> <requireJavaVersion>
<version>[11,)</version> <version>[17,)</version>
</requireJavaVersion> </requireJavaVersion>
</rules> </rules>
</configuration> </configuration>
@@ -246,7 +240,7 @@
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.3</version> <version>3.11.3</version>
<configuration> <configuration>
<source>11</source> <source>17</source>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>

View File

@@ -1,217 +0,0 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.AppRole;
import de.stklcode.jvault.connector.model.AppRoleSecret;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.*;
import java.util.ArrayList;
import java.util.List;
/**
* AppRole client interface.
* Provides methods to interact with Vault's AppRole API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface AppRoleClient {
/**
* Register a new AppRole role from given metamodel.
*
* @param role The role
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean create(final AppRole role) throws VaultConnectorException;
/**
* Register new AppRole role with default policy.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName) throws VaultConnectorException {
return create(roleName, new ArrayList<>());
}
/**
* Register new AppRole role with policies.
*
* @param roleName The role name
* @param policies The policies to associate with
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final List<String> policies) throws VaultConnectorException {
return create(roleName, policies, null);
}
/**
* Register new AppRole role with default policy and custom ID.
*
* @param roleName The role name
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final String roleID) throws VaultConnectorException {
return create(roleName, new ArrayList<>(), roleID);
}
/**
* Register new AppRole role with policies and custom ID.
*
* @param roleName The role name
* @param policies The policies to associate with
* @param roleID A custom role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default boolean create(final String roleName, final List<String> policies, final String roleID)
throws VaultConnectorException {
return create(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
}
/**
* Delete AppRole role from Vault.
*
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
*/
boolean delete(final String roleName) throws VaultConnectorException;
/**
* Lookup an AppRole role.
*
* @param roleName The role name
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleResponse lookup(final String roleName) throws VaultConnectorException;
/**
* Retrieve ID for an AppRole role.
*
* @param roleName The role name
* @return The role ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
String getRoleID(final String roleName) throws VaultConnectorException;
/**
* Set custom ID for an AppRole role.
*
* @param roleName The role name
* @param roleID The role ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean setRoleID(final String roleName, final String roleID) throws VaultConnectorException;
/**
* Register new random generated AppRole secret.
*
* @param roleName The role name
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createSecret(final String roleName) throws VaultConnectorException {
return createSecret(roleName, new AppRoleSecret());
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secretID A custom secret ID
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
default AppRoleSecretResponse createSecret(final String roleName, final String secretID)
throws VaultConnectorException {
return createSecret(roleName, new AppRoleSecret(secretID));
}
/**
* Register new AppRole secret with custom ID.
*
* @param roleName The role name
* @param secret The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse createSecret(final String roleName, final AppRoleSecret secret)
throws VaultConnectorException;
/**
* Lookup an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret ID
* @return Result of the lookup
* @throws VaultConnectorException on error
* @since 0.4.0
*/
AppRoleSecretResponse lookupSecret(final String roleName, final String secretID)
throws VaultConnectorException;
/**
* Destroy an AppRole secret.
*
* @param roleName The role name
* @param secretID The secret meta object
* @return The secret ID
* @throws VaultConnectorException on error
* @since 0.4.0
*/
boolean destroySecret(final String roleName, final String secretID) throws VaultConnectorException;
/**
* List existing (accessible) AppRole roles.
*
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listRoles() throws VaultConnectorException;
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
*/
List<String> listSecrets(final String roleName) throws VaultConnectorException;
}

View File

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

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.response.MetadataResponse;
import de.stklcode.jvault.connector.model.response.SecretResponse;
import de.stklcode.jvault.connector.model.response.SecretVersionResponse;
import java.util.Map;
/**
* KV v2 client interface.
* Provides methods to interact with Vault's KV v2 API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface KV2Client {
/**
* Retrieve the latest secret data for specific version from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readData(final String mount, final String key) throws VaultConnectorException {
return readVersion(mount, key, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeData(final String mount,
final String key,
final Map<String, Object> data) throws VaultConnectorException {
return writeData(mount, key, data, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretVersionResponse writeData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret response.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretResponse readVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
* <br>
* Path {@code <mount>/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
* @since 0.8
*/
MetadataResponse readMetadata(final String mount, final String key) throws VaultConnectorException;
/**
* Update secret metadata.
* <br>
* Path {@code <mount>/metadata/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteLatestVersion(final String mount, final String key) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteAllVersions(final String mount, final String key) throws VaultConnectorException;
/**
* Delete secret versions from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void undeleteVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
void destroyVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
}

View File

@@ -1,88 +0,0 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.*;
import java.util.List;
/**
* Sys client interface.
* Provides methods to interact with Vault's system API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface SysClient {
/**
* Retrieve status of vault seal.
*
* @return Seal status
* @throws VaultConnectorException on error
*/
SealResponse sealStatus() throws VaultConnectorException;
/**
* Seal vault.
*
* @throws VaultConnectorException on error
*/
void seal() throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @param reset Discard previously provided keys (optional)
* @return Response with seal status
* @throws VaultConnectorException on error
*/
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @return Response with seal status
* @throws VaultConnectorException on error
*/
default SealResponse unseal(final String key) throws VaultConnectorException {
return unseal(key, null);
}
/**
* Query server health information.
*
* @return Health information.
* @throws VaultConnectorException on error
* @since 0.7.0
*/
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all available authentication backends.
*
* @return List of backends
* @throws VaultConnectorException on error
*/
List<AuthBackend> getAuthBackends() throws VaultConnectorException;
}

View File

@@ -1,125 +0,0 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.AuthResponse;
import de.stklcode.jvault.connector.model.response.TokenResponse;
import de.stklcode.jvault.connector.model.response.TokenRoleResponse;
import java.util.List;
/**
* Token client interface.
* Provides methods to interact with Vault's token API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface TokenClient {
/**
* Create a new token.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse create(final Token token) throws VaultConnectorException;
/**
* Create a new token.
*
* @param token the token
* @param orphan create orphan token
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse create(final Token token, boolean orphan) throws VaultConnectorException;
/**
* Create a new token for specific role.
*
* @param token the token
* @param role the role name
* @return the result response
* @throws VaultConnectorException on error
*/
AuthResponse create(final Token token, final String role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param token the token
* @return the result response
* @throws VaultConnectorException on error
*/
TokenResponse lookup(final String token) throws VaultConnectorException;
/**
* Create a new or update an existing token role.
*
* @param role the role entity (name must be set)
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
default boolean createOrUpdateRole(final TokenRole role) throws VaultConnectorException {
return createOrUpdateRole(role.getName(), role);
}
/**
* Create a new or update an existing token role.
*
* @param name the role name (overrides name possibly set in role entity)
* @param role the role entity
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean createOrUpdateRole(final String name, final TokenRole role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param name the role name
* @return the result response
* @throws VaultConnectorException on error
* @since 0.9
*/
TokenRoleResponse readRole(final String name) throws VaultConnectorException;
/**
* List available token roles from Vault.
*
* @return List of token roles
* @throws VaultConnectorException on error
* @since 0.9
*/
List<String> listRoles() throws VaultConnectorException;
/**
* Delete a token role.
*
* @param name the role name to delete
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean deleteRole(final String name) throws VaultConnectorException;
}

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.response.TransitResponse;
import java.util.Base64;
/**
* Transit client interface.
* Provides methods to interact with Vault's transit API.
*
* @since 2.0.0 extracted from {@link VaultConnector}
*/
public interface TransitClient {
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Text to encrypt (Base64 encoded)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse encrypt(final String keyName, final String plaintext) throws VaultConnectorException;
/**
* Encrypt plaintext via transit engine from Vault.
*
* @param keyName Transit key name
* @param plaintext Binary data to encrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse encrypt(final String keyName, final byte[] plaintext)
throws VaultConnectorException {
return encrypt(keyName, Base64.getEncoder().encodeToString(plaintext));
}
/**
* Decrypt ciphertext via transit engine from Vault.
*
* @param keyName Transit key name
* @param ciphertext Text to decrypt
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse decrypt(final String keyName, final String ciphertext) throws VaultConnectorException;
/**
* Hash data in hex format via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse hash(final String algorithm, final String input) throws VaultConnectorException {
return hash(algorithm, input, "hex");
}
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash (Base64 encoded)
* @param format Specifies the output encoding (hex/base64)
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
TransitResponse hash(final String algorithm, final String input, final String format)
throws VaultConnectorException;
/**
* Hash data via transit engine from Vault.
*
* @param algorithm Specifies the hash algorithm to use
* @param input Data to hash
* @return Transit response
* @throws VaultConnectorException on error
* @since 1.5.0
*/
default TransitResponse hash(final String algorithm, final byte[] input, final String format)
throws VaultConnectorException {
return hash(algorithm, Base64.getEncoder().encodeToString(input), format);
}
}

View File

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

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown trying to do a request without any authorization handles. * Exception thrown trying to do a request without any authorization handles.
* *
@@ -23,5 +25,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public class AuthorizationRequiredException extends VaultConnectorException { public class AuthorizationRequiredException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = 2629577936657393880L; private static final long serialVersionUID = 2629577936657393880L;
} }

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown on problems with connection to Vault backend. * Exception thrown on problems with connection to Vault backend.
* *
@@ -23,6 +25,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public class ConnectionException extends VaultConnectorException { public class ConnectionException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = 3005430116002990418L; private static final long serialVersionUID = 3005430116002990418L;
/** /**

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown when trying to send malformed request. * Exception thrown when trying to send malformed request.
* *
@@ -23,6 +25,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public class InvalidRequestException extends VaultConnectorException { public class InvalidRequestException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = -6712239648281809159L; private static final long serialVersionUID = -6712239648281809159L;
/** /**

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown when response from vault returned with erroneous status code or payload could not be parsed * Exception thrown when response from vault returned with erroneous status code or payload could not be parsed
* to entity class. * to entity class.
@@ -24,6 +26,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public final class InvalidResponseException extends VaultConnectorException { public final class InvalidResponseException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = 2003151038614163479L; private static final long serialVersionUID = 2003151038614163479L;
private final Integer statusCode; private final Integer statusCode;

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown when trying to access a path the current user/token does not have permission to access. * Exception thrown when trying to access a path the current user/token does not have permission to access.
* *
@@ -23,6 +25,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public class PermissionDeniedException extends VaultConnectorException { public class PermissionDeniedException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = -7149134015090750776L; private static final long serialVersionUID = -7149134015090750776L;
/** /**

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Exception thrown on errors with TLS connection. * Exception thrown on errors with TLS connection.
* *
@@ -23,6 +25,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.4.0 * @since 0.4.0
*/ */
public class TlsException extends VaultConnectorException { public class TlsException extends VaultConnectorException {
@Serial
private static final long serialVersionUID = -5139276834988258086L; private static final long serialVersionUID = -5139276834988258086L;
/** /**

View File

@@ -16,6 +16,8 @@
package de.stklcode.jvault.connector.exception; package de.stklcode.jvault.connector.exception;
import java.io.Serial;
/** /**
* Abstract Exception class for Vault Connector internal exceptions. * Abstract Exception class for Vault Connector internal exceptions.
* *
@@ -23,6 +25,7 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1 * @since 0.1
*/ */
public abstract class VaultConnectorException extends Exception { public abstract class VaultConnectorException extends Exception {
@Serial
private static final long serialVersionUID = -2612477894310906036L; private static final long serialVersionUID = -2612477894310906036L;
/** /**

View File

@@ -1,12 +1,11 @@
package de.stklcode.jvault.connector.internal; package de.stklcode.jvault.connector.internal;
import com.fasterxml.jackson.core.JsonProcessingException; import tools.jackson.core.JacksonException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.stklcode.jvault.connector.exception.*; import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.model.response.ErrorResponse; import de.stklcode.jvault.connector.model.response.ErrorResponse;
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.json.JsonMapper;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.TrustManagerFactory;
@@ -25,6 +24,7 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration; import java.time.Duration;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@@ -66,9 +66,8 @@ public final class RequestHelper implements Serializable {
this.tlsVersion = tlsVersion; this.tlsVersion = tlsVersion;
this.trustedCaCert = trustedCaCert; this.trustedCaCert = trustedCaCert;
this.jsonMapper = JsonMapper.builder() this.jsonMapper = JsonMapper.builder()
.addModule(new JavaTimeModule()) .enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .disable(DateTimeFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.build(); .build();
} }
@@ -89,7 +88,7 @@ public final class RequestHelper implements Serializable {
// Generate JSON from payload. // Generate JSON from payload.
try { try {
req.POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8)); req.POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
} catch (JsonProcessingException e) { } catch (JacksonException e) {
throw new InvalidRequestException(Error.PARSE_RESPONSE, e); throw new InvalidRequestException(Error.PARSE_RESPONSE, e);
} }
@@ -120,7 +119,7 @@ public final class RequestHelper implements Serializable {
try { try {
String response = post(path, payload, token); String response = post(path, payload, token);
return jsonMapper.readValue(response, target); return jsonMapper.readValue(response, target);
} catch (IOException e) { } catch (JacksonException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e); throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} }
} }
@@ -159,7 +158,7 @@ public final class RequestHelper implements Serializable {
// Generate JSON from payload. // Generate JSON from payload.
try { try {
req.PUT(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8)); req.PUT(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
} catch (JsonProcessingException e) { } catch (JacksonException e) {
throw new InvalidRequestException("Payload serialization failed", e); throw new InvalidRequestException("Payload serialization failed", e);
} }
@@ -190,7 +189,7 @@ public final class RequestHelper implements Serializable {
try { try {
String response = put(path, payload, token); String response = put(path, payload, token);
return jsonMapper.readValue(response, target); return jsonMapper.readValue(response, target);
} catch (IOException e) { } catch (JacksonException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e); throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} }
} }
@@ -263,9 +262,9 @@ public final class RequestHelper implements Serializable {
if (!payload.isEmpty()) { if (!payload.isEmpty()) {
uriBuilder.append("?").append( uriBuilder.append("?").append(
payload.entrySet().stream().map(par -> payload.entrySet().stream()
URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8) .map(par -> encode(par.getKey()) + "=" + encode(par.getValue()))
).collect(Collectors.joining("&")) .collect(Collectors.joining("&"))
); );
} }
@@ -302,11 +301,22 @@ public final class RequestHelper implements Serializable {
try { try {
String response = get(path, payload, token); String response = get(path, payload, token);
return jsonMapper.readValue(response, target); return jsonMapper.readValue(response, target);
} catch (IOException e) { } catch (JacksonException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e); throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
} }
} }
/**
* Encode URL part.
*
* @param part Path part to URL-encode and insert into the template
* @return Encoded URL part
* @since 1.5.3
*/
public static String encode(final String part) {
return URLEncoder.encode(Objects.requireNonNullElse(part, ""), UTF_8);
}
/** /**
* Execute prepared HTTP request and return result. * Execute prepared HTTP request and return result.
* *
@@ -443,7 +453,7 @@ public final class RequestHelper implements Serializable {
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString()); throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString());
} }
} }
} catch (IOException ignored) { } catch (IOException | JacksonException ignored) {
// Exception ignored. // Exception ignored.
} }
} }

View File

@@ -40,8 +40,8 @@ public final class VaultApiPath {
// Auth paths // Auth paths
public static final String AUTH_TOKEN = AUTH + "/token"; public static final String AUTH_TOKEN = AUTH + "/token";
public static final String AUTH_USERPASS_LOGIN = AUTH + "/userpass/login/"; public static final String AUTH_USERPASS_LOGIN = AUTH + "/userpass/login/";
public static final String AUTH_APPROLE = AUTH + "/approle"; public static final String AUTH_APPROLE = AUTH + "/approle/";
public static final String AUTH_APPROLE_ROLE = AUTH_APPROLE + "/role/%s%s"; public static final String AUTH_APPROLE_ROLE = AUTH_APPROLE + "role/";
// Token operations // Token operations
public static final String TOKEN_LOOKUP = "/lookup"; public static final String TOKEN_LOOKUP = "/lookup";
@@ -57,9 +57,6 @@ public final class VaultApiPath {
public static final String SECRET_UNDELETE = "/undelete/"; public static final String SECRET_UNDELETE = "/undelete/";
public static final String SECRET_DESTROY = "/destroy/"; public static final String SECRET_DESTROY = "/destroy/";
// Generic paths
public static final String LOGIN = "/login";
// Transit engine paths // Transit engine paths
public static final String TRANSIT_ENCRYPT = TRANSIT + "/encrypt/"; public static final String TRANSIT_ENCRYPT = TRANSIT + "/encrypt/";
public static final String TRANSIT_DECRYPT = TRANSIT + "/decrypt/"; public static final String TRANSIT_DECRYPT = TRANSIT + "/decrypt/";

View File

@@ -18,6 +18,8 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.*;
import java.io.Serial;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -32,6 +34,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRole implements Serializable { public final class AppRole implements Serializable {
@Serial
private static final long serialVersionUID = 1546673231280751679L; private static final long serialVersionUID = 1546673231280751679L;
@JsonProperty("role_name") @JsonProperty("role_name")

View File

@@ -18,6 +18,7 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.annotation.*;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -32,7 +33,8 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleSecret implements Serializable { public final class AppRoleSecret implements Serializable {
private static final long serialVersionUID = -3401074170145792641L; @Serial
private static final long serialVersionUID = 3079272087137299819L;
@JsonProperty("secret_id") @JsonProperty("secret_id")
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
@@ -47,6 +49,8 @@ public final class AppRoleSecret implements Serializable {
private List<String> cidrList; private List<String> cidrList;
private List<String> tokenBoundCidrs;
@JsonProperty(value = "creation_time", access = JsonProperty.Access.WRITE_ONLY) @JsonProperty(value = "creation_time", access = JsonProperty.Access.WRITE_ONLY)
private String creationTime; private String creationTime;
@@ -137,6 +141,36 @@ public final class AppRoleSecret implements Serializable {
return String.join(",", cidrList); return String.join(",", cidrList);
} }
/**
* @return list of bound CIDR subnets of associated tokens
* @since 1.5.3
*/
public List<String> getTokenBoundCidrs() {
return tokenBoundCidrs;
}
/**
* @param boundCidrList list of subnets in CIDR notation to bind role to
* @since 1.5.3
*/
@JsonSetter("token_bound_cidrs")
public void setTokenBoundCidrs(final List<String> boundCidrList) {
this.tokenBoundCidrs = boundCidrList;
}
/**
* @return list of subnets in CIDR notation as comma-separated {@link String}
* @since 1.5.3
*/
@JsonGetter("token_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getTokenBoundCidrsString() {
if (tokenBoundCidrs == null || tokenBoundCidrs.isEmpty()) {
return "";
}
return String.join(",", tokenBoundCidrs);
}
/** /**
* @return Creation time * @return Creation time
*/ */
@@ -184,6 +218,7 @@ public final class AppRoleSecret implements Serializable {
Objects.equals(accessor, that.accessor) && Objects.equals(accessor, that.accessor) &&
Objects.equals(metadata, that.metadata) && Objects.equals(metadata, that.metadata) &&
Objects.equals(cidrList, that.cidrList) && Objects.equals(cidrList, that.cidrList) &&
Objects.equals(tokenBoundCidrs, that.tokenBoundCidrs) &&
Objects.equals(creationTime, that.creationTime) && Objects.equals(creationTime, that.creationTime) &&
Objects.equals(expirationTime, that.expirationTime) && Objects.equals(expirationTime, that.expirationTime) &&
Objects.equals(lastUpdatedTime, that.lastUpdatedTime) && Objects.equals(lastUpdatedTime, that.lastUpdatedTime) &&
@@ -193,7 +228,7 @@ public final class AppRoleSecret implements Serializable {
@Override @Override
public int hashCode() { public int hashCode() {
return Objects.hash(id, accessor, metadata, cidrList, creationTime, expirationTime, lastUpdatedTime, numUses, return Objects.hash(id, accessor, metadata, cidrList, tokenBoundCidrs, creationTime, expirationTime,
ttl); lastUpdatedTime, numUses, ttl);
} }
} }

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
@@ -32,6 +33,7 @@ import java.util.*;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class Token implements Serializable { public final class Token implements Serializable {
@Serial
private static final long serialVersionUID = 7003016071684507115L; private static final long serialVersionUID = 7003016071684507115L;
@JsonProperty("id") @JsonProperty("id")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenRole implements Serializable { public final class TokenRole implements Serializable {
@Serial
private static final long serialVersionUID = -4856948364869438439L; private static final long serialVersionUID = -4856948364869438439L;
@JsonProperty("name") @JsonProperty("name")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.AppRole; import de.stklcode.jvault.connector.model.AppRole;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleResponse extends VaultDataResponse { public final class AppRoleResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -6536422219633829177L; private static final long serialVersionUID = -6536422219633829177L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.AppRoleSecret; import de.stklcode.jvault.connector.model.AppRoleSecret;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleSecretResponse extends VaultDataResponse { public final class AppRoleSecretResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -2484103304072370585L; private static final long serialVersionUID = -2484103304072370585L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod; import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import java.io.Serial;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -32,6 +33,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethodsResponse extends VaultDataResponse { public final class AuthMethodsResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -1802724129533405375L; private static final long serialVersionUID = -1802724129533405375L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -19,6 +19,8 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import de.stklcode.jvault.connector.model.response.embedded.AuthData; import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import java.io.Serial;
/** /**
* Vault response for authentication providing auth info in {@link AuthData} field. * Vault response for authentication providing auth info in {@link AuthData} field.
* *
@@ -27,5 +29,6 @@ import de.stklcode.jvault.connector.model.response.embedded.AuthData;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthResponse extends VaultDataResponse { public final class AuthResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 1628851361067456715L; private static final long serialVersionUID = 1628851361067456715L;
} }

View File

@@ -18,6 +18,8 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.io.Serial;
/** /**
* Vault response from credentials lookup. Simple wrapper for data objects containing username and password fields. * Vault response from credentials lookup. Simple wrapper for data objects containing username and password fields.
* *
@@ -26,6 +28,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class CredentialsResponse extends PlainSecretResponse { public final class CredentialsResponse extends PlainSecretResponse {
@Serial
private static final long serialVersionUID = -1439692963299045425L; private static final long serialVersionUID = -1439692963299045425L;
/** /**

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class ErrorResponse implements VaultResponse { public final class ErrorResponse implements VaultResponse {
@Serial
private static final long serialVersionUID = -6227368087842549149L; private static final long serialVersionUID = -6227368087842549149L;
@JsonProperty("errors") @JsonProperty("errors")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -29,6 +30,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class HealthResponse implements VaultResponse { public final class HealthResponse implements VaultResponse {
@Serial
private static final long serialVersionUID = 8675155916902904516L; private static final long serialVersionUID = 8675155916902904516L;
@JsonProperty("cluster_id") @JsonProperty("cluster_id")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -29,6 +30,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class HelpResponse implements VaultResponse { public final class HelpResponse implements VaultResponse {
@Serial
private static final long serialVersionUID = -1152070966642848490L; private static final long serialVersionUID = -1152070966642848490L;
@JsonProperty("help") @JsonProperty("help")

View File

@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.SecretWrapper; import de.stklcode.jvault.connector.model.response.embedded.SecretWrapper;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MetaSecretResponse extends SecretResponse { public class MetaSecretResponse extends SecretResponse {
@Serial
private static final long serialVersionUID = -1076542846391240162L; private static final long serialVersionUID = -1076542846391240162L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata; import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
@@ -31,6 +32,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MetadataResponse extends VaultDataResponse { public class MetadataResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -3679762333630984679L; private static final long serialVersionUID = -3679762333630984679L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
@@ -33,6 +34,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class PlainSecretResponse extends SecretResponse { public class PlainSecretResponse extends SecretResponse {
@Serial
private static final long serialVersionUID = 3010138542437913023L; private static final long serialVersionUID = 3010138542437913023L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -31,6 +32,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class RawDataResponse extends VaultDataResponse { public final class RawDataResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -319727427792124071L; private static final long serialVersionUID = -319727427792124071L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Objects; import java.util.Objects;
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class SealResponse implements VaultResponse { public final class SealResponse implements VaultResponse {
@Serial
private static final long serialVersionUID = -6000309255473305787L; private static final long serialVersionUID = -6000309255473305787L;
@JsonProperty("type") @JsonProperty("type")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.SecretListWrapper; import de.stklcode.jvault.connector.model.response.embedded.SecretListWrapper;
import java.io.Serial;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -32,8 +33,9 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretListResponse extends VaultDataResponse { public final class SecretListResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 8597121175002967213L; private static final long serialVersionUID = 8597121175002967213L;
@JsonProperty("data") @JsonProperty("data")
private SecretListWrapper data; private SecretListWrapper data;

View File

@@ -17,14 +17,13 @@
package de.stklcode.jvault.connector.model.response; package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.stklcode.jvault.connector.exception.InvalidResponseException; import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.cfg.DateTimeFeature;
import tools.jackson.databind.json.JsonMapper;
import java.io.IOException; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
@@ -37,6 +36,7 @@ import java.util.Map;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public abstract class SecretResponse extends VaultDataResponse { public abstract class SecretResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 5198088815871692951L; private static final long serialVersionUID = 5198088815871692951L;
/** /**
@@ -86,9 +86,8 @@ public abstract class SecretResponse extends VaultDataResponse {
return type.cast(rawValue); return type.cast(rawValue);
} else { } else {
var om = JsonMapper.builder() var om = JsonMapper.builder()
.addModule(new JavaTimeModule()) .enable(DateTimeFeature.WRITE_DATES_AS_TIMESTAMPS)
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) .disable(DateTimeFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.build(); .build();
if (rawValue instanceof String) { if (rawValue instanceof String) {
@@ -97,7 +96,7 @@ public abstract class SecretResponse extends VaultDataResponse {
return om.readValue(om.writeValueAsString(rawValue), type); return om.readValue(om.writeValueAsString(rawValue), type);
} }
} }
} catch (IOException e) { } catch (JacksonException e) {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage()); throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());
} }
} }

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata; import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SecretVersionResponse extends VaultDataResponse { public class SecretVersionResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 2748635005258576174L; private static final long serialVersionUID = 2748635005258576174L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.TokenData; import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenResponse extends VaultDataResponse { public final class TokenResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = -4341114947980033457L; private static final long serialVersionUID = -4341114947980033457L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.TokenRole; import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.embedded.TokenData; import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.Serial;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -31,6 +32,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenRoleResponse extends VaultDataResponse { public final class TokenRoleResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 5265363857731948626L; private static final long serialVersionUID = 5265363857731948626L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -18,6 +18,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonSetter;
import java.io.Serial;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -29,6 +30,7 @@ import java.util.Objects;
*/ */
public class TransitResponse extends VaultDataResponse { public class TransitResponse extends VaultDataResponse {
@Serial
private static final long serialVersionUID = 6873804240772242771L; private static final long serialVersionUID = 6873804240772242771L;
private String ciphertext; private String ciphertext;

View File

@@ -20,6 +20,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.AuthData; import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import de.stklcode.jvault.connector.model.response.embedded.WrapInfo; import de.stklcode.jvault.connector.model.response.embedded.WrapInfo;
import java.io.Serial;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -30,6 +31,7 @@ import java.util.Objects;
* @since 0.1 * @since 0.1
*/ */
public abstract class VaultDataResponse implements VaultResponse { public abstract class VaultDataResponse implements VaultResponse {
@Serial
private static final long serialVersionUID = 4787715235558510045L; private static final long serialVersionUID = 4787715235558510045L;
@JsonProperty("request_id") @JsonProperty("request_id")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -33,6 +34,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthData implements Serializable { public final class AuthData implements Serializable {
@Serial
private static final long serialVersionUID = 5969334512309655317L; private static final long serialVersionUID = 5969334512309655317L;
@JsonProperty("client_token") @JsonProperty("client_token")

View File

@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter; import com.fasterxml.jackson.annotation.JsonSetter;
import de.stklcode.jvault.connector.model.AuthBackend; import de.stklcode.jvault.connector.model.AuthBackend;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethod implements Serializable { public final class AuthMethod implements Serializable {
@Serial
private static final long serialVersionUID = -439987082190917691L; private static final long serialVersionUID = -439987082190917691L;
private AuthBackend type; private AuthBackend type;

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -31,6 +32,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaConstraintAny implements Serializable { public final class MfaConstraintAny implements Serializable {
@Serial
private static final long serialVersionUID = 1226126781813149627L; private static final long serialVersionUID = 1226126781813149627L;
@JsonProperty("any") @JsonProperty("any")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
@@ -30,6 +31,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaMethodId implements Serializable { public final class MfaMethodId implements Serializable {
@Serial
private static final long serialVersionUID = 691298070242998814L; private static final long serialVersionUID = 691298070242998814L;
@JsonProperty("type") @JsonProperty("type")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -31,6 +32,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaRequirement implements Serializable { public final class MfaRequirement implements Serializable {
@Serial
private static final long serialVersionUID = -2516941512455319638L; private static final long serialVersionUID = -2516941512455319638L;
@JsonProperty("mfa_request_id") @JsonProperty("mfa_request_id")

View File

@@ -3,6 +3,8 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -15,6 +17,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class MountConfig implements Serializable { public class MountConfig implements Serializable {
@Serial
private static final long serialVersionUID = 7241631159224756605L; private static final long serialVersionUID = 7241631159224756605L;
@JsonProperty("default_lease_ttl") @JsonProperty("default_lease_ttl")

View File

@@ -3,6 +3,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -16,7 +17,9 @@ import java.util.Objects;
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SecretListWrapper implements Serializable { public class SecretListWrapper implements Serializable {
@Serial
private static final long serialVersionUID = -8777605197063766125L; private static final long serialVersionUID = -8777605197063766125L;
@JsonProperty("keys") @JsonProperty("keys")
private List<String> keys; private List<String> keys;

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.HashMap; import java.util.HashMap;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretMetadata implements Serializable { public final class SecretMetadata implements Serializable {
@Serial
private static final long serialVersionUID = -905059942871916214L; private static final long serialVersionUID = -905059942871916214L;
@JsonProperty("created_time") @JsonProperty("created_time")

View File

@@ -3,6 +3,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
@@ -15,6 +16,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class SecretWrapper implements Serializable { public class SecretWrapper implements Serializable {
@Serial
private static final long serialVersionUID = 8600413181758893378L; private static final long serialVersionUID = 8600413181758893378L;
@JsonProperty("data") @JsonProperty("data")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.List; import java.util.List;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenData implements Serializable { public final class TokenData implements Serializable {
@Serial
private static final long serialVersionUID = -4168046151053509784L; private static final long serialVersionUID = -4168046151053509784L;
@JsonProperty("accessor") @JsonProperty("accessor")

View File

@@ -3,6 +3,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects; import java.util.Objects;
@@ -14,6 +15,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class UserLockoutConfig implements Serializable { public class UserLockoutConfig implements Serializable {
@Serial
private static final long serialVersionUID = -8051060041593140550L; private static final long serialVersionUID = -8051060041593140550L;
@JsonProperty("lockout_threshold") @JsonProperty("lockout_threshold")

View File

@@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.HashMap; import java.util.HashMap;
@@ -34,6 +35,7 @@ import java.util.Objects;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public final class VersionMetadata implements Serializable { public final class VersionMetadata implements Serializable {
@Serial
private static final long serialVersionUID = 8495687554714216478L; private static final long serialVersionUID = 8495687554714216478L;
@JsonProperty("created_time") @JsonProperty("created_time")

View File

@@ -18,6 +18,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Objects; import java.util.Objects;
@@ -29,6 +30,7 @@ import java.util.Objects;
* @since 1.1 * @since 1.1
*/ */
public class WrapInfo implements Serializable { public class WrapInfo implements Serializable {
@Serial
private static final long serialVersionUID = 4864973237090355607L; private static final long serialVersionUID = 4864973237090355607L;
@JsonProperty("token") @JsonProperty("token")

View File

@@ -26,12 +26,11 @@ module de.stklcode.jvault.connector {
exports de.stklcode.jvault.connector.model.response; exports de.stklcode.jvault.connector.model.response;
exports de.stklcode.jvault.connector.model.response.embedded; exports de.stklcode.jvault.connector.model.response.embedded;
opens de.stklcode.jvault.connector.model to com.fasterxml.jackson.databind; opens de.stklcode.jvault.connector.model to tools.jackson.databind;
opens de.stklcode.jvault.connector.model.response to com.fasterxml.jackson.databind; opens de.stklcode.jvault.connector.model.response to tools.jackson.databind;
opens de.stklcode.jvault.connector.model.response.embedded to com.fasterxml.jackson.databind; opens de.stklcode.jvault.connector.model.response.embedded to tools.jackson.databind;
requires java.net.http; requires java.net.http;
requires com.fasterxml.jackson.annotation; requires com.fasterxml.jackson.annotation;
requires com.fasterxml.jackson.databind; requires tools.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
} }

View File

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

View File

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

View File

@@ -39,6 +39,7 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
"number", 1337 "number", 1337
); );
private static final List<String> TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24"); private static final List<String> TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24");
private static final List<String> TEST_TOKEN_CIDR = List.of("192.0.2.0/24", "198.51.100.0/24");
AppRoleSecretTest() { AppRoleSecretTest() {
super(AppRoleSecret.class); super(AppRoleSecret.class);
@@ -61,6 +62,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
assertNull(secret.getMetadata()); assertNull(secret.getMetadata());
assertNull(secret.getCidrList()); assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString()); assertEquals("", secret.getCidrListString());
assertNull(secret.getTokenBoundCidrs());
assertEquals("", secret.getTokenBoundCidrsString());
assertNull(secret.getCreationTime()); assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime()); assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime()); assertNull(secret.getLastUpdatedTime());
@@ -74,6 +77,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
assertNull(secret.getMetadata()); assertNull(secret.getMetadata());
assertNull(secret.getCidrList()); assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString()); assertEquals("", secret.getCidrListString());
assertNull(secret.getTokenBoundCidrs());
assertEquals("", secret.getTokenBoundCidrsString());
assertNull(secret.getCreationTime()); assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime()); assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime()); assertNull(secret.getLastUpdatedTime());
@@ -87,6 +92,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
assertEquals(TEST_META, secret.getMetadata()); assertEquals(TEST_META, secret.getMetadata());
assertEquals(TEST_CIDR, secret.getCidrList()); assertEquals(TEST_CIDR, secret.getCidrList());
assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString()); assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString());
assertNull(secret.getTokenBoundCidrs());
assertEquals("", secret.getTokenBoundCidrsString());
assertNull(secret.getCreationTime()); assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime()); assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime()); assertNull(secret.getLastUpdatedTime());
@@ -108,6 +115,15 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
secret.setCidrList(null); secret.setCidrList(null);
assertNull(secret.getCidrList()); assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString()); assertEquals("", secret.getCidrListString());
assertNull(secret.getTokenBoundCidrs());
assertEquals("", secret.getTokenBoundCidrsString());
secret.setTokenBoundCidrs(TEST_TOKEN_CIDR);
assertEquals(TEST_TOKEN_CIDR, secret.getTokenBoundCidrs());
assertEquals(String.join(",", TEST_TOKEN_CIDR), secret.getTokenBoundCidrsString());
secret.setTokenBoundCidrs(null);
assertNull(secret.getTokenBoundCidrs());
assertEquals("", secret.getTokenBoundCidrsString());
} }
/** /**
@@ -159,7 +175,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
// Those fields should be deserialized from JSON though. // Those fields should be deserialized from JSON though.
String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," + String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," + "\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"cidr_list\":[\"192.0.2.0/24\",\"198.51.100.0/24\"]," +
"\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," + "\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
"\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}"; "\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}";
secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed"); secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed");
@@ -181,6 +198,7 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
private static String commaSeparatedToList(String json) { private static String commaSeparatedToList(String json) {
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]") return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]")
.replaceAll("\"token_bound_cidrs\":\"([^\"]*)\"", "\"token_bound_cidrs\":[$1]")
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\""); .replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
} }
} }