30 Commits

Author SHA1 Message Date
7bcd57691c prepare release of v1.2.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 17:44:00 +01:00
531111cb2b test against Vault 1.15.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-11 17:41:31 +01:00
151b58dc82 model: add "build_date" to SealResponse (#73)
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-03 15:11:10 +01:00
65fb01617d model: extend AuthMethod model and embedded config (#72)
Introduce MountConfig and UserLockoutConfig models and add some missing
fields to AuthMethod.
2023-12-03 15:09:45 +01:00
097cb5415a re-generate serial version of AuthData class
All checks were successful
continuous-integration/drone/push Build is passing
Fixes: 2dff8930b7
2023-12-03 15:09:12 +01:00
2dff8930b7 model: add MFA requirement data to auth response (#71)
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-03 14:44:15 +01:00
d82554974c test against Vault 1.15.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-12-03 10:39:09 +01:00
8352d04c52 deps: update test dependencies
* commons-io 2.15.1
* dependency-check-maven 9.0.2
* equalsverifier 3.15.4
* mockito-core 5.8.0
2023-12-03 10:33:44 +01:00
56d5345fcb ci: run coverage analysis with JDK 21
All checks were successful
continuous-integration/drone/push Build is passing
The JDK 21 build with Vault 1.11 seems to be the only job with full
coverage on conditional code.
2023-11-26 11:36:23 +01:00
52876ef3a4 close HTTPClient when running with JDK21 or newer (#70)
The Java HTTP client implements AutoCloseable since JDK 21. Closing the
client ensures that asynchronous operations and streams are properly
terminated.

As we support Java 11, we add any old school "finally" wrapper and
conditionally close the client when running on a modern platform.
2023-11-26 11:33:28 +01:00
62f2249a4d remove redundant java.base requirement from module-info.java (#69)
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-26 11:08:06 +01:00
10965b01d6 Merge branch 'main' into develop
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-26 11:04:17 +01:00
0f302af3e7 deps: update dependency-check-maven plugin to 9.0.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-26 10:57:53 +01:00
02ae647002 deps: update jackson to 2.16.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-11-16 18:35:15 +01:00
1d9e1458be deps: update test dependencies and Maven plugins 2023-11-16 18:25:37 +01:00
683052a804 add basic .editorconfig
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 18:24:22 +02:00
cc290f4def deps: update test dependencies and Maven plugins
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-23 18:18:24 +02:00
2722a125e5 deps: update jackson to 2.15.3 2023-10-23 18:17:41 +02:00
5e5143168e ci: build and test with JDK 21
All checks were successful
continuous-integration/drone/push Build is passing
2023-10-15 17:10:38 +02:00
08e8c9f090 test against Vault 1.15.0
Some checks failed
continuous-integration/drone/push Build is failing
2023-09-27 20:18:34 +02:00
c06cb9b0fb deps: update test dependencies
All checks were successful
continuous-integration/drone/push Build is passing
* Wiremock 3
* Mockito 5.5
2023-09-08 20:41:37 +02:00
505d420fd6 Merge branch 'main' into develop
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-19 11:47:05 +02:00
be23129441 prepare release of v1.1.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-19 11:10:57 +02:00
199dfd30ca update test dependencies and plugins
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-19 11:06:43 +02:00
b72298f2a8 bump Vault versions in integration tests to 1.14.0 and 1.11.12
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-23 17:43:22 +02:00
226b6ad6c4 split lines above 120 characters
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-17 15:17:06 +02:00
d81fc4e5af reorder members and elements to comply with language recommendations 2023-06-17 15:23:49 +02:00
1195b447a2 fix regression from redundant String mapping in SecretResponse getter
All checks were successful
continuous-integration/drone/push Build is passing
Mapping a JSON string into String using a JSON parser will fail, so we
should use the string directly instead of applying double conversion.

Fixes: f3e1f01e38
2023-06-16 18:18:55 +02:00
f3e1f01e38 fix type conversion in SecretResponse getter method (#67)
Some checks failed
continuous-integration/drone/push Build is failing
Converting the payload using toString() is not an appropriate way to
feed a JSON parser. We now use JSON roundtrip for type mapping and
introduce shortcuts of the type already matches the target type.
2023-06-16 17:43:30 +02:00
7e5d193d1b parse timestamps as ZonedDateTime internally
All checks were successful
continuous-integration/drone/push Build is passing
Timestamps have been stored with their String representation from the
API with convenience methods to convert them into ZonedDateTime.
We now use the Jackson JavaTimeModule to parse them directly and swap
the real and convenience getters.
2023-06-15 17:50:47 +02:00
35 changed files with 1255 additions and 280 deletions

View File

@ -3,7 +3,7 @@ name: default
steps:
- name: compile
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
commands:
- mvn -B clean compile
when:
@ -14,7 +14,7 @@ steps:
- fix/*
- release/*
- name: unit-tests
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
commands:
- mvn -B test
when:
@ -25,7 +25,7 @@ steps:
- name: setup-vault
image: alpine:latest
environment:
VAULT_VERSION: 1.13.3
VAULT_VERSION: 1.15.4
commands:
- wget -q -O vault_$${VAULT_VERSION}_linux_amd64.zip https://releases.hashicorp.com/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_linux_amd64.zip
- wget -q -O - https://releases.hashicorp.com/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_SHA256SUMS | grep linux_amd64 | sha256sum -c
@ -38,9 +38,9 @@ steps:
- main
- release/*
- name: unit-integration-tests
image: maven:3-eclipse-temurin-17
image: maven:3-eclipse-temurin-21
environment:
VAULT_VERSION: 1.13.3
VAULT_VERSION: 1.15.4
commands:
- export PATH=.bin:$${PATH}
- mvn -B -P integration-test verify

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 120
tab_width = 4
trim_trailing_whitespace = true
[{*.yaml,*.yml}]
indent_size = 2

View File

@ -5,11 +5,11 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
jdk: [ 11, 17, 20 ]
vault: [ '1.2.0', '1.11.11', '1.13.3' ]
jdk: [ 11, 17, 21 ]
vault: [ '1.2.0', '1.11.12', '1.15.4' ]
include:
- jdk: 17
vault: '1.11.11'
- jdk: 21
vault: '1.11.12'
analysis: true
steps:
- name: Checkout

View File

@ -1,3 +1,28 @@
## 1.2.0 (2023-12-11)
### Deprecations
* `get...TimeString()` methods on various model classes are now deprecated
### Improvements
* Parse timestamps as `ZonedDateTime` instead of `String` representation
* Remove redundant `java.base` requirement from _module-info.java_ (#69)
* Close Java HTTP Client when running on Java 21 or later (#70)
* Add MFA requirements tu `AuthResponse` (#71)
* Extend `AuthMethod` data model (#72)
### Dependencies
* Updated Jackson to 2.16.0
## 1.1.5 (2023-08-19)
### Fix
* Fixed JSON type conversion in `SecretResponse#get(String, Class)` (#67)
### Test
* Tested against Vault 1.2 to 1.15
## 1.1.4 (2023-06-15)
### Fix
@ -165,7 +190,7 @@ Old builders will be removed in 1.0
* Added `entity_id`, `token_policies`, `token_type` and `orphan` flags to auth response
* Added `entity_id`, `expire_time`, `explicit_max_ttl`, `issue_time`, `renewable` and `type` flags to token data
* Added `explicit_max_ttl`, `period` and `entity_alias` flags to _Token_ model (#41)
* Added `enable_local_secret_ids`, `token_bound_cidrs`, `token_explicit_max_ttl`, `token_no_default_policy`,
* Added `enable_local_secret_ids`, `token_bound_cidrs`, `token_explicit_max_ttl`, `token_no_default_policy`,
`token_num_uses`, `token_period` and `token_type` flags to _AppRole_ model
* Minor dependency updates
@ -185,14 +210,14 @@ Old builders will be removed in 1.0
## 0.8.2 (2019-10-20)
### Fixes
* Fixed token lookup (#31)
* Fixed token lookup (#31)
### Improvements
* Updated dependencies
## 0.8.1 (2019-08-16)
### Fixes
* Removed compile dependency to JUnit library (#30)
* Removed compile dependency to JUnit library (#30)
### Improvements
* Updated dependencies
@ -284,7 +309,7 @@ Old builders will be removed in 1.0
### Fixes
* `SecretResponse` does not throw NPE on `get(key)` and `getData()`
### Test
### Test
* Tested against Vault 0.7.2

View File

@ -1,8 +1,8 @@
# Java Vault Connector
# Java Vault Connector
[![CI Status](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml/badge.svg)](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml)
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=de.stklcode.jvault%3Ajvault-connector&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.stklcode.jvault%3Ajvault-connector)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/main/LICENSE.txt)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/main/LICENSE.txt)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.jvault/jvault-connector.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.jvault%22%20AND%20a%3A%22jvault-connector%22)
![Logo](https://raw.githubusercontent.com/stklcode/jvaultconnector/main/assets/logo.png)
@ -32,7 +32,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* SQL secret handling
* KV v1 and v2 support
* Connector Factory with builder pattern
* Tested against Vault 1.2 to 1.13
* Tested against Vault 1.2 to 1.15
## Maven Artifact
@ -40,7 +40,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>1.1.4</version>
<version>1.2.0</version>
</dependency>
```
@ -61,7 +61,7 @@ VaultConnector vault = HTTPVaultConnector.builder("https://example.com:8200/v1/"
.withTrustedCA(Paths.get("/path/to/CA.pem"))
.build();
// Initialization from environment variables
// Initialization from environment variables
VaultConnector vault = HTTPVaultConnector.builder()
.fromEnv()
.build();

140
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>1.1.4</version>
<version>1.2.0</version>
<packaging>jar</packaging>
@ -21,16 +21,11 @@
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<argLine></argLine>
</properties>
<developers>
<developer>
<name>Stefan Kalscheuer</name>
<email>stefan@stklcode.de</email>
<timezone>+1</timezone>
<timezone>Europe/Berlin</timezone>
</developer>
</developers>
@ -45,6 +40,67 @@
<url>https://github.com/stklcode/jvaultconnector/issues</url>
</issueManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<argLine></argLine>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.16.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.wiremock</groupId>
<artifactId>wiremock</artifactId>
<version>3.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.15.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
@ -60,7 +116,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -70,7 +126,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<version>3.2.2</version>
<configuration>
<argLine>
@{argLine}
@ -108,7 +164,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.2</version>
<version>3.2.2</version>
<configuration>
<argLine>
@{argLine}
@ -119,74 +175,24 @@
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.model.response=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.model.response.embedded=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.databind
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.datatype.jsr310
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
<version>0.8.11</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
<version>3.10.0.2594</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.3.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.35.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.14.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>sources</id>
@ -221,7 +227,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.5.0</version>
<version>3.6.2</version>
<configuration>
<source>11</source>
</configuration>
@ -316,7 +322,11 @@
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.2.1</version>
<version>9.0.4</version>
<configuration>
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
<nvdDatafeedUrl>${env.NVD_DATAFEED_URL}</nvdDatafeedUrl>
</configuration>
<executions>
<execution>
<goals>

View File

@ -75,6 +75,24 @@ public class HTTPVaultConnector implements VaultConnector {
private String token; // Current token.
private long tokenTTL = 0; // Expiration time for current token.
/**
* Create connector using a {@link HTTPVaultConnectorBuilder}.
*
* @param builder The builder.
*/
HTTPVaultConnector(final HTTPVaultConnectorBuilder builder) {
this.request = new RequestHelper(
((builder.isWithTLS()) ? "https" : "http") + "://" +
builder.getHost() +
((builder.getPort() != null) ? ":" + builder.getPort() : "") +
builder.getPrefix(),
builder.getNumberOfRetries(),
builder.getTimeout(),
builder.getTlsVersion(),
builder.getTrustedCA()
);
}
/**
* Get a new builder for a connector.
*
@ -108,24 +126,6 @@ public class HTTPVaultConnector implements VaultConnector {
return new HTTPVaultConnectorBuilder().withBaseURL(baseURL);
}
/**
* Create connector using a {@link HTTPVaultConnectorBuilder}.
*
* @param builder The builder.
*/
HTTPVaultConnector(final HTTPVaultConnectorBuilder builder) {
this.request = new RequestHelper(
((builder.isWithTLS()) ? "https" : "http") + "://" +
builder.getHost() +
((builder.getPort() != null) ? ":" + builder.getPort() : "") +
builder.getPrefix(),
builder.getNumberOfRetries(),
builder.getTimeout(),
builder.getTlsVersion(),
builder.getTrustedCA()
);
}
@Override
public final void resetAuth() {
token = null;
@ -419,7 +419,8 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException {
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);
@ -428,7 +429,8 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException {
public final MetadataResponse readSecretMetadata(final String mount, final String key)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
@ -436,7 +438,10 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException {
public void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
@ -448,7 +453,10 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data, final Integer cas) throws VaultConnectorException {
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()) {
@ -480,7 +488,8 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final void write(final String key, final Map<String, Object> data, final Map<String, Object> options) throws VaultConnectorException {
public final void write(final String key, final Map<String, Object> data, final Map<String, Object> options)
throws VaultConnectorException {
requireAuth();
if (key == null || key.isEmpty()) {
@ -521,17 +530,20 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final void deleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_DELETE, key, versions);
}
@Override
public final void undeleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_UNDELETE, key, versions);
}
@Override
public final void destroySecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_DESTROY, key, versions);
}
@ -545,7 +557,10 @@ public class HTTPVaultConnector implements VaultConnector {
* @throws VaultConnectorException on error
* @since 0.8
*/
private void handleSecretVersions(final String mount, final String pathPart, final String key, final int... versions) throws VaultConnectorException {
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 */
@ -698,7 +713,7 @@ public class HTTPVaultConnector implements VaultConnector {
*/
private static Map<String, String> mapOfStrings(Object... keyValues) {
Map<String, String> map = new HashMap<>(keyValues.length / 2, 1);
for (int i = 0; i < keyValues.length -1; i = i + 2) {
for (int i = 0; i < keyValues.length - 1; i = i + 2) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (key instanceof String && val != null) {

View File

@ -422,7 +422,9 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data) throws VaultConnectorException {
default SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data) throws VaultConnectorException {
return writeSecretData(mount, key, data, null);
}
@ -440,7 +442,10 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @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;
SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
@ -455,7 +460,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException;
SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
@ -484,7 +490,10 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException;
void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException;
/**
* List available nodes from Vault.
@ -529,7 +538,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8 {@code options} parameter added
*/
void write(final String key, final Map<String, Object> data, final Map<String, Object> options) throws VaultConnectorException;
void write(final String key, final Map<String, Object> data, final Map<String, Object> options)
throws VaultConnectorException;
/**
* Delete key from Vault.
@ -576,7 +586,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
@ -588,7 +599,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
void undeleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
@ -600,7 +612,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8
*/
void destroySecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Revoke given lease immediately.

View File

@ -1,7 +1,10 @@
package de.stklcode.jvault.connector.internal;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.model.response.ErrorResponse;
@ -62,7 +65,10 @@ public final class RequestHelper implements Serializable {
this.timeout = timeout;
this.tlsVersion = tlsVersion;
this.trustedCaCert = trustedCaCert;
this.jsonMapper = new ObjectMapper();
this.jsonMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
}
/**
@ -127,7 +133,8 @@ public final class RequestHelper implements Serializable {
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public void postWithoutResponse(final String path, final Object payload, final String token) throws VaultConnectorException {
public void postWithoutResponse(final String path, final Object payload, final String token)
throws VaultConnectorException {
if (!post(path, payload, token).isEmpty()) {
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
@ -143,7 +150,8 @@ public final class RequestHelper implements Serializable {
* @throws VaultConnectorException on connection error
* @since 0.8 Added {@code token} parameter.
*/
public String put(final String path, final Map<String, String> payload, final String token) throws VaultConnectorException {
public String put(final String path, final Map<String, String> payload, final String token)
throws VaultConnectorException {
// Initialize PUT.
var req = HttpRequest.newBuilder(URI.create(baseURL + path));
@ -254,8 +262,8 @@ public final class RequestHelper implements Serializable {
if (!payload.isEmpty()) {
uriBuilder.append("?").append(
payload.entrySet().stream().map(
par -> URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8)
payload.entrySet().stream().map(par ->
URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8)
).collect(Collectors.joining("&"))
);
}
@ -355,6 +363,15 @@ public final class RequestHelper implements Serializable {
}
} catch (CompletionException e) {
throw new ConnectionException(Error.CONNECTION, e.getCause());
} finally {
if (client instanceof AutoCloseable) {
// Close the client, which is supported since JDK21.
try {
((AutoCloseable) client).close();
} catch (Exception ignored) {
// Ignore
}
}
}
}
@ -386,7 +403,8 @@ public final class RequestHelper implements Serializable {
}
return sslContext;
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException e) {
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException |
KeyManagementException e) {
throw new TlsException(Error.INIT_SSL_CONTEXT, e);
}
}

View File

@ -34,17 +34,6 @@ import java.util.Objects;
public final class AppRole implements Serializable {
private static final long serialVersionUID = -6248529625864573990L;
/**
* Get {@link Builder} instance.
*
* @param name Role name.
* @return AppRole Builder.
* @since 0.8
*/
public static Builder builder(final String name) {
return new Builder(name);
}
@JsonProperty("role_name")
private String name;
@ -134,6 +123,17 @@ public final class AppRole implements Serializable {
this.tokenType = builder.tokenType != null ? builder.tokenType.value() : null;
}
/**
* Get {@link Builder} instance.
*
* @param name Role name.
* @return AppRole Builder.
* @since 0.8
*/
public static Builder builder(final String name) {
return new Builder(name);
}
/**
* @return the role name
*/

View File

@ -34,16 +34,6 @@ import java.util.*;
public final class Token implements Serializable {
private static final long serialVersionUID = 5208508683665365287L;
/**
* Get {@link Builder} instance.
*
* @return Token Builder.
* @since 0.8
*/
public static Builder builder() {
return new Builder();
}
@JsonProperty("id")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String id;
@ -123,6 +113,16 @@ public final class Token implements Serializable {
this.entityAlias = builder.entityAlias;
}
/**
* Get {@link Builder} instance.
*
* @return Token Builder.
* @since 0.8
*/
public static Builder builder() {
return new Builder();
}
/**
* @return Token ID
*/

View File

@ -36,15 +36,6 @@ import java.util.Objects;
public final class TokenRole implements Serializable {
private static final long serialVersionUID = -3505215215838576321L;
/**
* Get {@link Builder} instance.
*
* @return Token Role Builder.
*/
public static Builder builder() {
return new Builder();
}
@JsonProperty("name")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String name;
@ -129,6 +120,15 @@ public final class TokenRole implements Serializable {
this.tokenType = builder.tokenType != null ? builder.tokenType.value() : null;
}
/**
* Get {@link Builder} instance.
*
* @return Token Role Builder.
*/
public static Builder builder() {
return new Builder();
}
/**
* @return Token Role name
*/
@ -264,7 +264,7 @@ public final class TokenRole implements Serializable {
@Override
public int hashCode() {
return Objects.hash(name, allowedPolicies, allowedPoliciesGlob, disallowedPolicies, disallowedPoliciesGlob,
orphan, renewable, pathSuffix, allowedEntityAliases, tokenBoundCidrs, tokenExplicitMaxTtl,
orphan, renewable, pathSuffix, allowedEntityAliases, tokenBoundCidrs, tokenExplicitMaxTtl,
tokenNoDefaultPolicy, tokenNumUses, tokenPeriod, tokenType);
}

View File

@ -41,7 +41,7 @@ public class MetaSecretResponse extends SecretResponse {
@Override
public final Map<String, Serializable> getData() {
if (secret != null) {
if (secret != null) {
return secret.getData();
} else {
return Collections.emptyMap();
@ -50,7 +50,7 @@ public class MetaSecretResponse extends SecretResponse {
@Override
public final VersionMetadata getMetadata() {
if (secret != null) {
if (secret != null) {
return secret.getMetadata();
} else {
return null;

View File

@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.time.ZonedDateTime;
import java.util.Objects;
/**
@ -29,7 +30,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SealResponse implements VaultResponse {
private static final long serialVersionUID = -3661916639367542617L;
private static final long serialVersionUID = -6000309255473305787L;
@JsonProperty("type")
private String type;
@ -52,6 +53,9 @@ public final class SealResponse implements VaultResponse {
@JsonProperty("version")
private String version;
@JsonProperty("build_date")
private ZonedDateTime buildDate;
@JsonProperty("nonce")
private String nonce;
@ -122,6 +126,14 @@ public final class SealResponse implements VaultResponse {
return version;
}
/**
* @return Vault build date.
* @since 1.2
*/
public ZonedDateTime getBuildDate() {
return buildDate;
}
/**
* @return A random nonce.
* @since 0.8
@ -185,6 +197,7 @@ public final class SealResponse implements VaultResponse {
Objects.equals(numberOfShares, that.numberOfShares) &&
Objects.equals(progress, that.progress) &&
Objects.equals(version, that.version) &&
Objects.equals(buildDate, that.buildDate) &&
Objects.equals(nonce, that.nonce) &&
Objects.equals(clusterName, that.clusterName) &&
Objects.equals(clusterId, that.clusterId) &&
@ -195,7 +208,7 @@ public final class SealResponse implements VaultResponse {
@Override
public int hashCode() {
return Objects.hash(type, sealed, initialized, threshold, numberOfShares, progress, version, nonce,
return Objects.hash(type, sealed, initialized, threshold, numberOfShares, progress, version, buildDate, nonce,
clusterName, clusterId, migration, recoverySeal, storageType);
}
}

View File

@ -17,7 +17,10 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
@ -79,8 +82,20 @@ public abstract class SecretResponse extends VaultDataResponse {
Object rawValue = get(key);
if (rawValue == null) {
return null;
} else if (type.isInstance(rawValue)) {
return type.cast(rawValue);
} else {
var om = new ObjectMapper()
.registerModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
if (rawValue instanceof String) {
return om.readValue((String) rawValue, type);
} else {
return om.readValue(om.writeValueAsString(rawValue), type);
}
}
return new ObjectMapper().readValue(rawValue.toString(), type);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());
}

View File

@ -33,7 +33,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthData implements Serializable {
private static final long serialVersionUID = -6962244199229885869L;
private static final long serialVersionUID = 3067695351664603536L;
@JsonProperty("client_token")
private String clientToken;
@ -65,6 +65,9 @@ public final class AuthData implements Serializable {
@JsonProperty("orphan")
private boolean orphan;
@JsonProperty("mfa_requirement")
private MfaRequirement mfaRequirement;
/**
* @return Client token
*/
@ -139,6 +142,14 @@ public final class AuthData implements Serializable {
return orphan;
}
/**
* @return multi-factor requirement
* @since 1.2
*/
public MfaRequirement getMfaRequirement() {
return mfaRequirement;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -157,12 +168,13 @@ public final class AuthData implements Serializable {
Objects.equals(metadata, authData.metadata) &&
Objects.equals(leaseDuration, authData.leaseDuration) &&
Objects.equals(entityId, authData.entityId) &&
Objects.equals(tokenType, authData.tokenType);
Objects.equals(tokenType, authData.tokenType) &&
Objects.equals(mfaRequirement, authData.mfaRequirement);
}
@Override
public int hashCode() {
return Objects.hash(clientToken, accessor, policies, tokenPolicies, metadata, leaseDuration, renewable,
entityId, tokenType, orphan);
entityId, tokenType, orphan, mfaRequirement);
}
}

View File

@ -34,7 +34,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethod implements Serializable {
private static final long serialVersionUID = -2718660627880077335L;
private static final long serialVersionUID = -439987082190917691L;
private AuthBackend type;
private String rawType;
@ -42,11 +42,14 @@ public final class AuthMethod implements Serializable {
@JsonProperty("accessor")
private String accessor;
@JsonProperty("deprecation_status")
private String deprecationStatus;
@JsonProperty("description")
private String description;
@JsonProperty("config")
private Map<String, String> config;
private MountConfig config;
@JsonProperty("external_entropy_access")
private boolean externalEntropyAccess;
@ -54,6 +57,18 @@ public final class AuthMethod implements Serializable {
@JsonProperty("local")
private boolean local;
@JsonProperty("options")
private Map<String, String> options;
@JsonProperty("plugin_version")
private String pluginVersion;
@JsonProperty("running_plugin_version")
private String runningPluginVersion;
@JsonProperty("running_sha256")
private String runningSha256;
@JsonProperty("seal_wrap")
private boolean sealWrap;
@ -91,6 +106,14 @@ public final class AuthMethod implements Serializable {
return accessor;
}
/**
* @return Deprecation status
* @since 1.2
*/
public String getDeprecationStatus() {
return deprecationStatus;
}
/**
* @return Description
*/
@ -100,8 +123,10 @@ public final class AuthMethod implements Serializable {
/**
* @return Configuration data
* @since 0.2
* @since 1.2 Returns {@link MountConfig} instead of {@link Map}
*/
public Map<String, String> getConfig() {
public MountConfig getConfig() {
return config;
}
@ -120,6 +145,38 @@ public final class AuthMethod implements Serializable {
return local;
}
/**
* @return Options
* @since 1.2
*/
public Map<String, String> getOptions() {
return options;
}
/**
* @return Plugin version
* @since 1.2
*/
public String getPluginVersion() {
return pluginVersion;
}
/**
* @return Running plugin version
* @since 1.2
*/
public String getRunningPluginVersion() {
return runningPluginVersion;
}
/**
* @return Running SHA256
* @since 1.2
*/
public String getRunningSha256() {
return runningSha256;
}
/**
* @return Seal wrapping enabled
* @since 1.1
@ -150,13 +207,19 @@ public final class AuthMethod implements Serializable {
sealWrap == that.sealWrap &&
Objects.equals(rawType, that.rawType) &&
Objects.equals(accessor, that.accessor) &&
Objects.equals(deprecationStatus, that.deprecationStatus) &&
Objects.equals(description, that.description) &&
Objects.equals(config, that.config) &&
Objects.equals(options, that.options) &&
Objects.equals(pluginVersion, that.pluginVersion) &&
Objects.equals(runningPluginVersion, that.runningPluginVersion) &&
Objects.equals(runningSha256, that.runningSha256) &&
Objects.equals(uuid, that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(type, rawType, accessor, description, config, externalEntropyAccess, local, sealWrap, uuid);
return Objects.hash(type, rawType, accessor, deprecationStatus, description, config, externalEntropyAccess,
local, options, pluginVersion, runningPluginVersion, runningSha256, sealWrap, uuid);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2016-2023 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.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* Embedded multi-factor-authentication (MFA) constraint "any".
*
* @author Stefan Kalscheuer
* @since 1.2
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaConstraintAny implements Serializable {
private static final long serialVersionUID = 1226126781813149627L;
@JsonProperty("any")
private List<MfaMethodId> any;
/**
* @return List of "any" MFA methods
*/
public List<MfaMethodId> getAny() {
return any;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MfaConstraintAny mfaRequirement = (MfaConstraintAny) o;
return Objects.equals(any, mfaRequirement.any);
}
@Override
public int hashCode() {
return Objects.hash(any);
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2016-2023 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.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Objects;
/**
* Embedded multi-factor-authentication (MFA) requirement.
*
* @author Stefan Kalscheuer
* @since 1.2
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaMethodId implements Serializable {
private static final long serialVersionUID = 691298070242998814L;
@JsonProperty("type")
private String type;
@JsonProperty("id")
private String id;
@JsonProperty("uses_passcode")
private Boolean usesPasscode;
@JsonProperty("name")
private String name;
/**
* @return MFA method type
*/
public String getType() {
return type;
}
/**
* @return MFA method id
*/
public String getId() {
return id;
}
/**
* @return MFA uses passcode id
*/
public Boolean getUsesPasscode() {
return usesPasscode;
}
/**
* @return MFA method name
*/
public String getName() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MfaMethodId mfaMethodId = (MfaMethodId) o;
return Objects.equals(type, mfaMethodId.type) &&
Objects.equals(id, mfaMethodId.id) &&
Objects.equals(usesPasscode, mfaMethodId.usesPasscode) &&
Objects.equals(name, mfaMethodId.name);
}
@Override
public int hashCode() {
return Objects.hash(type, id, usesPasscode, name);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2016-2023 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.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* Embedded multi-factor-authentication (MFA) requirement.
*
* @author Stefan Kalscheuer
* @since 1.2
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class MfaRequirement implements Serializable {
private static final long serialVersionUID = -2516941512455319638L;
@JsonProperty("mfa_request_id")
private String mfaRequestId;
@JsonProperty("mfa_constraints")
private Map<String, MfaConstraintAny> mfaConstraints;
/**
* @return MFA request ID
*/
public String getMfaRequestId() {
return mfaRequestId;
}
/**
* @return MFA constraints
*/
public Map<String, MfaConstraintAny> getMfaConstraints() {
return mfaConstraints;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MfaRequirement mfaRequirement = (MfaRequirement) o;
return Objects.equals(mfaRequestId, mfaRequirement.mfaRequestId) &&
Objects.equals(mfaConstraints, mfaRequirement.mfaConstraints);
}
@Override
public int hashCode() {
return Objects.hash(mfaRequestId, mfaConstraints);
}
}

View File

@ -0,0 +1,168 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* Embedded mount config output.
*
* @author Stefan Kalscheuer
* @since 1.2
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MountConfig implements Serializable {
private static final long serialVersionUID = -8653909672663717792L;
@JsonProperty("default_lease_ttl")
private Integer defaultLeaseTtl;
@JsonProperty("max_lease_ttl")
private Integer maxLeaseTtl;
@JsonProperty("force_no_cache")
private Boolean forceNoCache;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("audit_non_hmac_request_keys")
private List<String> auditNonHmacRequestKeys;
@JsonProperty("audit_non_hmac_response_keys")
private List<String> auditNonHmacResponseKeys;
@JsonProperty("listing_visibility")
private String listingVisibility;
@JsonProperty("passthrough_request_headers")
private List<String> passthroughRequestHeaders;
@JsonProperty("allowed_response_headers")
private List<String> allowedResponseHeaders;
@JsonProperty("allowed_managed_keys")
private List<String> allowedManagedKeys;
@JsonProperty("delegated_auth_accessors")
private List<String> delegatedAuthAccessors;
@JsonProperty("user_lockout_config")
private UserLockoutConfig userLockoutConfig;
/**
* @return Default lease TTL
*/
public Integer getDefaultLeaseTtl() {
return defaultLeaseTtl;
}
/**
* @return Maximum lease TTL
*/
public Integer getMaxLeaseTtl() {
return maxLeaseTtl;
}
/**
* @return Force no cache?
*/
public Boolean getForceNoCache() {
return forceNoCache;
}
/**
* @return Token type
*/
public String getTokenType() {
return tokenType;
}
/**
* @return Audit non HMAC request keys
*/
public List<String> getAuditNonHmacRequestKeys() {
return auditNonHmacRequestKeys;
}
/**
* @return Audit non HMAC response keys
*/
public List<String> getAuditNonHmacResponseKeys() {
return auditNonHmacResponseKeys;
}
/**
* @return Listing visibility
*/
public String getListingVisibility() {
return listingVisibility;
}
/**
* @return Passthrough request headers
*/
public List<String> getPassthroughRequestHeaders() {
return passthroughRequestHeaders;
}
/**
* @return Allowed response headers
*/
public List<String> getAllowedResponseHeaders() {
return allowedResponseHeaders;
}
/**
* @return Allowed managed keys
*/
public List<String> getAllowedManagedKeys() {
return allowedManagedKeys;
}
/**
* @return Delegated auth accessors
*/
public List<String> getDelegatedAuthAccessors() {
return delegatedAuthAccessors;
}
/**
* @return User lockout config
*/
public UserLockoutConfig getUserLockoutConfig() {
return userLockoutConfig;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
MountConfig that = (MountConfig) o;
return Objects.equals(defaultLeaseTtl, that.defaultLeaseTtl) &&
Objects.equals(maxLeaseTtl, that.maxLeaseTtl) &&
Objects.equals(forceNoCache, that.forceNoCache) &&
Objects.equals(tokenType, that.tokenType) &&
Objects.equals(auditNonHmacRequestKeys, that.auditNonHmacRequestKeys) &&
Objects.equals(auditNonHmacResponseKeys, that.auditNonHmacResponseKeys) &&
Objects.equals(listingVisibility, that.listingVisibility) &&
Objects.equals(passthroughRequestHeaders, that.passthroughRequestHeaders) &&
Objects.equals(allowedResponseHeaders, that.allowedResponseHeaders) &&
Objects.equals(allowedManagedKeys, that.allowedManagedKeys) &&
Objects.equals(delegatedAuthAccessors, that.delegatedAuthAccessors) &&
Objects.equals(userLockoutConfig, that.userLockoutConfig);
}
@Override
public int hashCode() {
return Objects.hash(defaultLeaseTtl, maxLeaseTtl, forceNoCache, tokenType, auditNonHmacRequestKeys,
auditNonHmacResponseKeys, listingVisibility, passthroughRequestHeaders, allowedResponseHeaders,
allowedManagedKeys, delegatedAuthAccessors, userLockoutConfig);
}
}

View File

@ -22,7 +22,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Map;
import java.util.Objects;
@ -35,12 +34,13 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretMetadata implements Serializable {
private static final long serialVersionUID = 1684891108903409038L;
private static final long serialVersionUID = -4967896264361344676L;
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
@JsonProperty("created_time")
private String createdTimeString;
private ZonedDateTime createdTime;
@JsonProperty("current_version")
private Integer currentVersion;
@ -52,31 +52,29 @@ public final class SecretMetadata implements Serializable {
private Integer oldestVersion;
@JsonProperty("updated_time")
private String updatedTime;
private ZonedDateTime updatedTime;
@JsonProperty("versions")
private Map<Integer, VersionMetadata> versions;
/**
* @return Time of secret creation as raw string representation.
* @deprecated Method left for backwards compatibility only. Use {@link #getCreatedTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getCreatedTimeString() {
return createdTimeString;
if (createdTime != null) {
return TIME_FORMAT.format(createdTime);
}
return null;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
return createdTime;
}
/**
@ -102,26 +100,24 @@ public final class SecretMetadata implements Serializable {
/**
* @return Time of secret update as raw string representation.
* @deprecated Method left for backwards compatibility only. Use {@link #getUpdatedTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getUpdatedTimeString() {
return updatedTime;
}
/**
* @return Time of secret update..
*/
public ZonedDateTime getUpdatedTime() {
if (updatedTime != null && !updatedTime.isEmpty()) {
try {
return ZonedDateTime.parse(updatedTime, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
if (updatedTime != null) {
return TIME_FORMAT.format(updatedTime);
}
return null;
}
/**
* @return Time of secret update.
*/
public ZonedDateTime getUpdatedTime() {
return updatedTime;
}
/**
* @return Version of the entry.
*/
@ -137,7 +133,7 @@ public final class SecretMetadata implements Serializable {
return false;
}
SecretMetadata that = (SecretMetadata) o;
return Objects.equals(createdTimeString, that.createdTimeString) &&
return Objects.equals(createdTime, that.createdTime) &&
Objects.equals(currentVersion, that.currentVersion) &&
Objects.equals(maxVersions, that.maxVersions) &&
Objects.equals(oldestVersion, that.oldestVersion) &&
@ -147,6 +143,6 @@ public final class SecretMetadata implements Serializable {
@Override
public int hashCode() {
return Objects.hash(createdTimeString, currentVersion, maxVersions, oldestVersion, updatedTime, versions);
return Objects.hash(createdTime, currentVersion, maxVersions, oldestVersion, updatedTime, versions);
}
}

View File

@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -34,7 +35,10 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenData implements Serializable {
private static final long serialVersionUID = 2915180734313753649L;
private static final long serialVersionUID = -5749716740973138916L;
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
@JsonProperty("accessor")
private String accessor;
@ -52,7 +56,7 @@ public final class TokenData implements Serializable {
private String entityId;
@JsonProperty("expire_time")
private String expireTime;
private ZonedDateTime expireTime;
@JsonProperty("explicit_max_ttl")
private Integer explicitMaxTtl;
@ -61,7 +65,7 @@ public final class TokenData implements Serializable {
private String id;
@JsonProperty("issue_time")
private String issueTime;
private ZonedDateTime issueTime;
@JsonProperty("meta")
private Map<String, Object> meta;
@ -126,9 +130,15 @@ public final class TokenData implements Serializable {
/**
* @return Expire time as raw string value
* @since 0.9
* @deprecated Method left for backwards compatibility only. Use {@link #getExpireTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getExpireTimeString() {
return expireTime;
if (expireTime != null) {
return TIME_FORMAT.format(expireTime);
}
return null;
}
/**
@ -136,11 +146,7 @@ public final class TokenData implements Serializable {
* @since 0.9
*/
public ZonedDateTime getExpireTime() {
if (expireTime == null) {
return null;
} else {
return ZonedDateTime.parse(expireTime);
}
return expireTime;
}
/**
@ -161,9 +167,15 @@ public final class TokenData implements Serializable {
/**
* @return Issue time as raw string value
* @since 0.9
* @deprecated Method left for backwards compatibility only. Use {@link #getIssueTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getIssueTimeString() {
return issueTime;
if (issueTime != null) {
return TIME_FORMAT.format(issueTime);
}
return null;
}
/**
@ -171,11 +183,7 @@ public final class TokenData implements Serializable {
* @since 0.9
*/
public ZonedDateTime getIssueTime() {
if (issueTime == null) {
return null;
} else {
return ZonedDateTime.parse(issueTime);
}
return issueTime;
}
/**

View File

@ -0,0 +1,77 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Objects;
/**
* Embedded user lockout config output.
*
* @author Stefan Kalscheuer
* @since 1.2
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class UserLockoutConfig implements Serializable {
private static final long serialVersionUID = -8051060041593140550L;
@JsonProperty("lockout_threshold")
private Integer lockoutThreshold;
@JsonProperty("lockout_duration")
private Integer lockoutDuration;
@JsonProperty("lockout_counter_reset_duration")
private Integer lockoutCounterResetDuration;
@JsonProperty("lockout_disable")
private Boolean lockoutDisable;
/**
* @return Lockout threshold
*/
public Integer getLockoutThreshold() {
return lockoutThreshold;
}
/**
* @return Lockout duration
*/
public Integer getLockoutDuration() {
return lockoutDuration;
}
/**
* @return Lockout counter reset duration
*/
public Integer getLockoutCounterResetDuration() {
return lockoutCounterResetDuration;
}
/**
* @return Lockout disabled?
*/
public Boolean getLockoutDisable() {
return lockoutDisable;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
UserLockoutConfig that = (UserLockoutConfig) o;
return Objects.equals(lockoutThreshold, that.lockoutThreshold) &&
Objects.equals(lockoutDuration, that.lockoutDuration) &&
Objects.equals(lockoutCounterResetDuration, that.lockoutCounterResetDuration) &&
Objects.equals(lockoutDisable, that.lockoutDisable);
}
@Override
public int hashCode() {
return Objects.hash(lockoutThreshold, lockoutDuration, lockoutCounterResetDuration, lockoutDisable);
}
}

View File

@ -22,7 +22,6 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Objects;
/**
@ -34,15 +33,16 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class VersionMetadata implements Serializable {
private static final long serialVersionUID = -5286693953873839611L;
private static final long serialVersionUID = -6815731513868586713L;
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
@JsonProperty("created_time")
private String createdTimeString;
private ZonedDateTime createdTime;
@JsonProperty("deletion_time")
private String deletionTimeString;
private ZonedDateTime deletionTime;
@JsonProperty("destroyed")
private boolean destroyed;
@ -52,46 +52,42 @@ public final class VersionMetadata implements Serializable {
/**
* @return Time of secret creation as raw string representation.
* @deprecated Method left for backwards compatibility only. Use {@link #getCreatedTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getCreatedTimeString() {
return createdTimeString;
if (createdTime != null) {
return TIME_FORMAT.format(createdTime);
}
return null;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
return createdTime;
}
/**
* @return Time for secret deletion as raw string representation.
* @deprecated Method left for backwards compatibility only. Use {@link #getDeletionTime()} instead.
*/
@Deprecated(since = "1.2", forRemoval = true)
public String getDeletionTimeString() {
return deletionTimeString;
if (deletionTime != null) {
return TIME_FORMAT.format(deletionTime);
}
return null;
}
/**
* @return Time for secret deletion.
*/
public ZonedDateTime getDeletionTime() {
if (deletionTimeString != null && !deletionTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(deletionTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
return deletionTime;
}
/**
@ -117,13 +113,13 @@ public final class VersionMetadata implements Serializable {
}
VersionMetadata that = (VersionMetadata) o;
return destroyed == that.destroyed &&
Objects.equals(createdTimeString, that.createdTimeString) &&
Objects.equals(deletionTimeString, that.deletionTimeString) &&
Objects.equals(createdTime, that.createdTime) &&
Objects.equals(deletionTime, that.deletionTime) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(createdTimeString, deletionTimeString, destroyed, version);
return Objects.hash(createdTime, deletionTime, destroyed, version);
}
}

View File

@ -19,6 +19,7 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.Objects;
/**
@ -28,7 +29,7 @@ import java.util.Objects;
* @since 1.1
*/
public class WrapInfo implements Serializable {
private static final long serialVersionUID = -7764500642913116581L;
private static final long serialVersionUID = 4864973237090355607L;
@JsonProperty("token")
private String token;
@ -37,7 +38,7 @@ public class WrapInfo implements Serializable {
private Integer ttl;
@JsonProperty("creation_time")
private String creationTime;
private ZonedDateTime creationTime;
@JsonProperty("creation_path")
private String creationPath;
@ -59,7 +60,7 @@ public class WrapInfo implements Serializable {
/**
* @return Creation time
*/
public String getCreationTime() {
public ZonedDateTime getCreationTime() {
return creationTime;
}

View File

@ -30,7 +30,7 @@ module de.stklcode.jvault.connector {
opens de.stklcode.jvault.connector.model.response to com.fasterxml.jackson.databind;
opens de.stklcode.jvault.connector.model.response.embedded to com.fasterxml.jackson.databind;
requires java.base;
requires java.net.http;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
}

View File

@ -51,7 +51,7 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
* @since 0.1
*/
class HTTPVaultConnectorIT {
private static String VAULT_VERSION = "1.13.3"; // The vault version this test is supposed to run against.
private static String VAULT_VERSION = "1.15.4"; // The vault version this test is supposed to run against.
private static final String KEY1 = "E38bkCm0VhUvpdCKGQpcohhD9XmcHJ/2hreOSY019Lho";
private static final String KEY2 = "O5OHwDleY3IiPdgw61cgHlhsrEm6tVJkrxhF6QAnILd1";
private static final String KEY3 = "mw7Bm3nbt/UWa/juDjjL2EPQ04kiJ0saC5JEXwJvXYsB";

View File

@ -1,6 +1,9 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
@ -26,7 +29,10 @@ public abstract class AbstractModelTest<T> {
*/
protected AbstractModelTest(Class<T> modelClass) {
this.modelClass = modelClass;
this.objectMapper = new ObjectMapper();
this.objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE);
}
/**

View File

@ -46,7 +46,10 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
private static final String TK_ACCESSOR = "auth_token_ac0dd95a";
private static final String TK_DESCR = "token based credentials";
private static final Integer TK_LEASE_TTL = 0;
private static final Boolean TK_FORCE_NO_CACHE = false;
private static final Integer TK_MAX_LEASE_TTL = 0;
private static final String TK_TOKEN_TYPE = "default-service";
private static final String TK_RUNNING_PLUGIN_VERSION = "v1.15.3+builtin.vault";
private static final String RES_JSON = "{\n" +
" \"data\": {" +
@ -62,9 +65,15 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
" \"" + TK_PATH + "\": {\n" +
" \"config\": {\n" +
" \"default_lease_ttl\": " + TK_LEASE_TTL + ",\n" +
" \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + "\n" +
" \"force_no_cache\": " + TK_FORCE_NO_CACHE + ",\n" +
" \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + ",\n" +
" \"token_type\": \"" + TK_TOKEN_TYPE + "\"\n" +
" },\n" +
" \"description\": \"" + TK_DESCR + "\",\n" +
" \"options\": null,\n" +
" \"plugin_version\": \"\",\n" +
" \"running_plugin_version\": \"" + TK_RUNNING_PLUGIN_VERSION + "\",\n" +
" \"running_sha256\": \"\",\n" +
" \"type\": \"" + TK_TYPE + "\",\n" +
" \"uuid\": \"" + TK_UUID + "\",\n" +
" \"accessor\": \"" + TK_ACCESSOR + "\",\n" +
@ -137,15 +146,16 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
assertTrue(method.isLocal(), "Unexpected local flag for Token");
assertFalse(method.isExternalEntropyAccess(), "Unexpected external entropy flag for Token");
assertFalse(method.isSealWrap(), "Unexpected seal wrap flag for GitHub");
assertEquals("", method.getPluginVersion(), "Unexpected plugin version");
assertEquals(TK_RUNNING_PLUGIN_VERSION, method.getRunningPluginVersion(), "Unexpected running plugin version");
assertEquals("", method.getRunningSha256(), "Unexpected running SHA256");
assertNotNull(method.getConfig(), "Missing config for Token");
assertEquals(
Map.of(
"default_lease_ttl", TK_LEASE_TTL.toString(),
"max_lease_ttl", TK_MAX_LEASE_TTL.toString()
),
method.getConfig(),
"Unexpected config for Token"
);
assertEquals(TK_LEASE_TTL, method.getConfig().getDefaultLeaseTtl(), "Unexpected default TTL");
assertEquals(TK_MAX_LEASE_TTL, method.getConfig().getMaxLeaseTtl(), "Unexpected max TTL");
assertEquals(TK_FORCE_NO_CACHE, method.getConfig().getForceNoCache(), "Unexpected force no cache flag");
assertEquals(TK_TOKEN_TYPE, method.getConfig().getTokenType(), "Unexpected token type");
assertNull(method.getOptions(), "Unexpected options");
}
}

View File

@ -19,6 +19,10 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import de.stklcode.jvault.connector.model.response.embedded.MfaConstraintAny;
import de.stklcode.jvault.connector.model.response.embedded.MfaMethodId;
import de.stklcode.jvault.connector.model.response.embedded.MfaRequirement;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
import java.util.Map;
@ -44,29 +48,50 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
private static final String AUTH_ENTITY_ID = "";
private static final String AUTH_TOKEN_TYPE = "service";
private static final Boolean AUTH_ORPHAN = false;
private static final String MFA_REQUEST_ID = "d0c9eec7-6921-8cc0-be62-202b289ef163";
private static final String MFA_KEY = "enforcementConfigUserpass";
private static final String MFA_METHOD_TYPE = "totp";
private static final String MFA_METHOD_ID = "820997b3-110e-c251-7e8b-ff4aa428a6e1";
private static final Boolean MFA_METHOD_USES_PASSCODE = true;
private static final String MFA_METHOD_NAME = "sample_mfa_method_name";
private static final String RES_JSON = "{\n" +
" \"auth\": {\n" +
" \"accessor\": \"" + AUTH_ACCESSOR + "\",\n" +
" \"client_token\": \"" + AUTH_CLIENT_TOKEN + "\",\n" +
" \"policies\": [\n" +
" \"" + AUTH_POLICY_1 + "\", \n" +
" \"" + AUTH_POLICY_2 + "\"\n" +
" ],\n" +
" \"token_policies\": [\n" +
" \"" + AUTH_POLICY_2 + "\",\n" +
" \"" + AUTH_POLICY_1 + "\" \n" +
" ],\n" +
" \"metadata\": {\n" +
" \"" + AUTH_META_KEY + "\": \"" + AUTH_META_VALUE + "\"\n" +
" },\n" +
" \"lease_duration\": " + AUTH_LEASE_DURATION + ",\n" +
" \"renewable\": " + AUTH_RENEWABLE + ",\n" +
" \"entity_id\": \"" + AUTH_ENTITY_ID + "\",\n" +
" \"token_type\": \"" + AUTH_TOKEN_TYPE + "\",\n" +
" \"orphan\": " + AUTH_ORPHAN + "\n" +
" }\n" +
"}";
" \"auth\": {\n" +
" \"accessor\": \"" + AUTH_ACCESSOR + "\",\n" +
" \"client_token\": \"" + AUTH_CLIENT_TOKEN + "\",\n" +
" \"policies\": [\n" +
" \"" + AUTH_POLICY_1 + "\", \n" +
" \"" + AUTH_POLICY_2 + "\"\n" +
" ],\n" +
" \"token_policies\": [\n" +
" \"" + AUTH_POLICY_2 + "\",\n" +
" \"" + AUTH_POLICY_1 + "\" \n" +
" ],\n" +
" \"metadata\": {\n" +
" \"" + AUTH_META_KEY + "\": \"" + AUTH_META_VALUE + "\"\n" +
" },\n" +
" \"lease_duration\": " + AUTH_LEASE_DURATION + ",\n" +
" \"renewable\": " + AUTH_RENEWABLE + ",\n" +
" \"entity_id\": \"" + AUTH_ENTITY_ID + "\",\n" +
" \"token_type\": \"" + AUTH_TOKEN_TYPE + "\",\n" +
" \"orphan\": " + AUTH_ORPHAN + ",\n" +
" \"mfa_requirement\": {\n" +
" \"mfa_request_id\": \"" + MFA_REQUEST_ID + "\",\n" +
" \"mfa_constraints\": {\n" +
" \"" + MFA_KEY + "\": {\n" +
" \"any\": [\n" +
" {\n" +
" \"type\": \"" + MFA_METHOD_TYPE + "\",\n" +
" \"id\": \"" + MFA_METHOD_ID + "\",\n" +
" \"uses_passcode\": " + MFA_METHOD_USES_PASSCODE + ",\n" +
" \"name\": \"" + MFA_METHOD_NAME + "\"\n" +
" }\n" +
" ]\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
AuthResponseTest() {
super(AuthResponse.class);
@ -82,6 +107,13 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
}
}
@Test
void testEqualsHashcodeMfa() {
EqualsVerifier.simple().forClass(MfaRequirement.class).verify();
EqualsVerifier.simple().forClass(MfaConstraintAny.class).verify();
EqualsVerifier.simple().forClass(MfaMethodId.class).verify();
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@ -107,5 +139,14 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
assertEquals(2, data.getTokenPolicies().size(), "Incorrect number of token policies");
assertTrue(data.getTokenPolicies().containsAll(Set.of(AUTH_POLICY_2, AUTH_POLICY_1)), "Incorrect token policies");
assertEquals(Map.of(AUTH_META_KEY, AUTH_META_VALUE), data.getMetadata(), "Incorrect auth metadata");
assertEquals(MFA_REQUEST_ID, data.getMfaRequirement().getMfaRequestId(), "Incorrect MFA request ID");
assertEquals(Set.of(MFA_KEY), data.getMfaRequirement().getMfaConstraints().keySet(), "Incorrect MFA constraint keys");
var mfaConstraint = data.getMfaRequirement().getMfaConstraints().get(MFA_KEY);
assertEquals(1, mfaConstraint.getAny().size(), "Incorrect number of any constraints");
assertEquals(MFA_METHOD_TYPE, mfaConstraint.getAny().get(0).getType(), "Incorrect MFA method type");
assertEquals(MFA_METHOD_ID, mfaConstraint.getAny().get(0).getId(), "Incorrect MFA method type");
assertEquals(MFA_METHOD_USES_PASSCODE, mfaConstraint.getAny().get(0).getUsesPasscode(), "Incorrect MFA method uses passcode");
assertEquals(MFA_METHOD_NAME, mfaConstraint.getAny().get(0).getName(), "Incorrect MFA method uses passcode");
}
}

View File

@ -109,7 +109,7 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
assertNotNull(res.getMetadata(), "SecretResponse does not contain metadata");
assertEquals(SECRET_META_CREATED, res.getMetadata().getCreatedTimeString(), "Incorrect creation date string");
assertNotNull(res.getMetadata().getCreatedTime(), "Creation date parsing failed");
assertEquals("", res.getMetadata().getDeletionTimeString(), "Incorrect deletion date string");
assertNull(res.getMetadata().getDeletionTimeString(), "Incorrect deletion date string");
assertNull(res.getMetadata().getDeletionTime(), "Incorrect deletion date");
assertFalse(res.getMetadata().isDestroyed(), "Secret destroyed when not expected");
assertEquals(1, res.getMetadata().getVersion(), "Incorrect secret version");

View File

@ -16,11 +16,13 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
@ -85,4 +87,137 @@ class PlainSecretResponseTest extends AbstractModelTest<PlainSecretResponse> {
assertEquals(SECRET_DATA_V1, res.get(SECRET_DATA_K1), "Response does not contain correct data");
assertEquals(SECRET_DATA_V2, res.get(SECRET_DATA_K2), "Response does not contain correct data");
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
void testGetter() {
final var stringKey = "string";
final var stringVal = "test";
final var numberKey = "number";
final var numberVal = 123.45;
final var listKey = "list";
final var listVal = List.of("foo", "bar");
final var complexKey = "complex";
final var complexVal = new ComplexType("val1", 678);
SecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(
"{\n" +
" \"request_id\": \"req-id\",\n" +
" \"lease_id\": \"lea-id\",\n" +
" \"lease_duration\": " + 123456 + ",\n" +
" \"renewable\": true,\n" +
" \"data\": {\n" +
" \"" + stringKey + "\": \"" + stringVal + "\",\n" +
" \"" + numberKey + "\": \"" + numberVal + "\",\n" +
" \"" + listKey + "\": [\"" + String.join("\", \"", listVal) + "\"],\n" +
" \"" + complexKey + "\": {" +
" \"field1\": \"" + complexVal.field1 + "\",\n" +
" \"field2\": " + complexVal.field2 + "\n" +
" },\n" +
" \"" + complexKey + "Json\": \"" + objectMapper.writeValueAsString(complexVal).replace("\"", "\\\"") + "\"\n" +
" }\n" +
"}",
PlainSecretResponse.class
),
"SecretResponse deserialization failed"
);
assertEquals(stringVal, res.get(stringKey), "unexpected value for string (implicit)");
assertEquals(
stringVal,
assertDoesNotThrow(() -> res.get(stringKey, String.class), "getting string failed"),
"unexpected value for string (explicit)"
);
assertEquals(String.valueOf(numberVal), res.get(numberKey), "unexpected value for number (implicit)");
assertEquals(
numberVal,
assertDoesNotThrow(() -> res.get(numberKey, Double.class), "getting number failed"),
"unexpected value for number (explicit)"
);
assertEquals(
String.valueOf(numberVal),
assertDoesNotThrow(() -> res.get(numberKey, String.class), "getting number as string failed"),
"unexpected value for number as string (explicit)"
);
assertEquals(listVal, res.get(listKey), "unexpected value for list (implicit)");
assertEquals(
listVal,
assertDoesNotThrow(() -> res.get(listKey, ArrayList.class), "getting list failed"),
"unexpected value for list (explicit)"
);
assertEquals(complexVal.toMap(), res.get(complexKey), "unexpected value for complex type (implicit)");
assertEquals(
complexVal.toMap(),
assertDoesNotThrow(() -> res.get(complexKey, HashMap.class), "getting complex type as map failed"),
"unexpected value for complex type as map (explicit)"
);
assertEquals(
complexVal,
assertDoesNotThrow(() -> res.get(complexKey, ComplexType.class), "getting complex type failed"),
"unexpected value for complex type (explicit)"
);
assertThrows(
InvalidResponseException.class,
() -> res.get(complexKey, Integer.class),
"getting complex type as integer should fail"
);
assertEquals(
complexVal,
assertDoesNotThrow(() -> res.get(complexKey + "Json", ComplexType.class), "getting complex type from JSON string failed"),
"unexpected value for complex type from JSON string"
);
}
/**
* Test class for complex field mapping.
*/
private static class ComplexType {
@JsonProperty("field1")
private String field1;
@JsonProperty("field2")
private Integer field2;
private ComplexType() {
// Required for JSON deserialization.
}
private ComplexType(String field1, Integer field2) {
this.field1 = field1;
this.field2 = field2;
}
private Map<String, Object> toMap() {
return Map.of(
"field1", field1,
"field2", field2
);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
ComplexType that = (ComplexType) o;
return Objects.equals(field1, that.field1) && Objects.equals(field2, that.field2);
}
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
}
}

View File

@ -20,6 +20,8 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.time.ZonedDateTime;
import static org.junit.jupiter.api.Assertions.*;
/**
@ -34,7 +36,8 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
private static final Integer SHARES = 5;
private static final Integer PROGRESS_SEALED = 2;
private static final Integer PROGRESS_UNSEALED = 0;
private static final String VERSION = "1.8.2";
private static final String VERSION = "1.15.4";
private static final String BUILD_DATE = "2023-11-22T20:59:54Z";
private static final String CLUSTER_NAME = "vault-cluster-d6ec3c7f";
private static final String CLUSTER_ID = "3e8b3fec-3749-e056-ba41-b62a63b997e8";
private static final String NONCE = "ef05d55d-4d2c-c594-a5e8-55bc88604c24";
@ -51,6 +54,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
" \"progress\": " + PROGRESS_SEALED + ",\n" +
" \"nonce\": \"\",\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"build_date\": \"" + BUILD_DATE + "\",\n" +
" \"migration\": \"" + MIGRATION + "\",\n" +
" \"recovery_seal\": \"" + RECOVERY_SEAL + "\",\n" +
" \"storage_type\": \"" + STORAGE_TYPE + "\"\n" +
@ -64,6 +68,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
" \"n\": " + SHARES + ",\n" +
" \"progress\": " + PROGRESS_UNSEALED + ",\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"build_date\": \"" + BUILD_DATE + "\",\n" +
" \"cluster_name\": \"" + CLUSTER_NAME + "\",\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
" \"nonce\": \"" + NONCE + "\",\n" +
@ -105,6 +110,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
assertEquals(PROGRESS_SEALED, res.getProgress(), "Incorrect progress");
assertEquals("", res.getNonce(), "Nonce not empty");
assertEquals(VERSION, res.getVersion(), "Incorrect version");
assertEquals(ZonedDateTime.parse(BUILD_DATE), res.getBuildDate(), "Incorrect build date");
assertEquals(MIGRATION, res.getMigration(), "Incorrect migration");
assertEquals(RECOVERY_SEAL, res.getRecoverySeal(), "Incorrect recovery seal");
assertEquals(STORAGE_TYPE, res.getStorageType(), "Incorrect storage type");
@ -127,6 +133,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
assertEquals(PROGRESS_UNSEALED, res.getProgress(), "Incorrect progress");
assertEquals(NONCE, res.getNonce(), "Incorrect nonce");
assertEquals(VERSION, res.getVersion(), "Incorrect version");
assertEquals(ZonedDateTime.parse(BUILD_DATE), res.getBuildDate(), "Incorrect build date");
assertEquals(CLUSTER_NAME, res.getClusterName(), "Incorrect cluster name");
assertEquals(CLUSTER_ID, res.getClusterId(), "Incorrect cluster ID");
assertEquals(MIGRATION, res.getMigration(), "Incorrect migration");

View File

@ -0,0 +1,103 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit test for {@link MountConfig}.
*
* @author Stefan Kalscheuer
*/
class MountConfigTest extends AbstractModelTest<MountConfig> {
private static final Integer DEFAULT_LEASE_TTL = 1800;
private static final Integer MAX_LEASE_TTL = 3600;
private static final Boolean FORCE_NO_CACHE = false;
private static final String TOKEN_TYPE = "default-service";
private static final String AUDIT_NON_HMAC_REQ_KEYS_1 = "req1";
private static final String AUDIT_NON_HMAC_REQ_KEYS_2 = "req2";
private static final String AUDIT_NON_HMAC_RES_KEYS_1 = "res1";
private static final String AUDIT_NON_HMAC_RES_KEYS_2 = "res2";
private static final String LISTING_VISIBILITY = "unauth";
private static final String PT_REQ_HEADER_1 = "prh1";
private static final String PT_REQ_HEADER_2 = "prh2";
private static final String ALLOWED_RES_HEADER_1 = "arh1";
private static final String ALLOWED_RES_HEADER_2 = "arh2";
private static final String ALLOWED_MANAGED_KEY_1 = "amk1";
private static final String ALLOWED_MANAGED_KEY_2 = "amk2";
private static final String DEL_AUTH_ACCESSOR_1 = "daa1";
private static final String DEL_AUTH_ACCESSOR_2 = "daa2";
private static final Integer LOCKOUT_THRESH = 7200;
private static final Integer LOCKOUT_DURATION = 86400;
private static final Integer LOCKOUT_CNT_RESET_DURATION = 43200;
private static final Boolean LOCKOUT_DISABLE = false;
private static final String RES_JSON = "{\n" +
" \"default_lease_ttl\": " + DEFAULT_LEASE_TTL + ",\n" +
" \"force_no_cache\": " + FORCE_NO_CACHE + ",\n" +
" \"max_lease_ttl\": " + MAX_LEASE_TTL + ",\n" +
" \"token_type\": \"" + TOKEN_TYPE + "\",\n" +
" \"audit_non_hmac_request_keys\": [\"" + AUDIT_NON_HMAC_REQ_KEYS_1 + "\", \"" + AUDIT_NON_HMAC_REQ_KEYS_2 + "\"],\n" +
" \"audit_non_hmac_response_keys\": [\"" + AUDIT_NON_HMAC_RES_KEYS_1 + "\", \"" + AUDIT_NON_HMAC_RES_KEYS_2 + "\"],\n" +
" \"listing_visibility\": \"" + LISTING_VISIBILITY + "\",\n" +
" \"passthrough_request_headers\": [\"" + PT_REQ_HEADER_1 + "\", \"" + PT_REQ_HEADER_2 + "\"],\n" +
" \"allowed_response_headers\": [\"" + ALLOWED_RES_HEADER_1 + "\", \"" + ALLOWED_RES_HEADER_2 + "\"],\n" +
" \"allowed_managed_keys\": [\"" + ALLOWED_MANAGED_KEY_1 + "\", \"" + ALLOWED_MANAGED_KEY_2 + "\"],\n" +
" \"delegated_auth_accessors\": [\"" + DEL_AUTH_ACCESSOR_1 + "\", \"" + DEL_AUTH_ACCESSOR_2 + "\"],\n" +
" \"user_lockout_config\": {\n" +
" \"lockout_threshold\": " + LOCKOUT_THRESH + ",\n" +
" \"lockout_duration\": " + LOCKOUT_DURATION + ",\n" +
" \"lockout_counter_reset_duration\": " + LOCKOUT_CNT_RESET_DURATION + ",\n" +
" \"lockout_disable\": " + LOCKOUT_DISABLE + "\n" +
" }\n" +
"}";
MountConfigTest() {
super(MountConfig.class);
}
@Override
protected MountConfig createFull() {
try {
return objectMapper.readValue(RES_JSON, MountConfig.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
void jsonRoundtrip() {
MountConfig mountConfig = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, MountConfig.class),
"MountConfig deserialization failed"
);
assertNotNull(mountConfig, "Parsed response is NULL");
// Verify data.
assertEquals(DEFAULT_LEASE_TTL, mountConfig.getDefaultLeaseTtl(), "Unexpected default lease TTL");
assertEquals(MAX_LEASE_TTL, mountConfig.getMaxLeaseTtl(), "Unexpected max lease TTL");
assertEquals(FORCE_NO_CACHE, mountConfig.getForceNoCache(), "Unexpected force no cache");
assertEquals(TOKEN_TYPE, mountConfig.getTokenType(), "Unexpected token type");
assertEquals(List.of(AUDIT_NON_HMAC_REQ_KEYS_1, AUDIT_NON_HMAC_REQ_KEYS_2), mountConfig.getAuditNonHmacRequestKeys(), "Unexpected audit no HMAC request keys");
assertEquals(List.of(AUDIT_NON_HMAC_RES_KEYS_1, AUDIT_NON_HMAC_RES_KEYS_2), mountConfig.getAuditNonHmacResponseKeys(), "Unexpected audit no HMAC response keys");
assertEquals(LISTING_VISIBILITY, mountConfig.getListingVisibility(), "Unexpected listing visibility");
assertEquals(List.of(PT_REQ_HEADER_1, PT_REQ_HEADER_2), mountConfig.getPassthroughRequestHeaders(), "Unexpected passthrough request headers");
assertEquals(List.of(ALLOWED_RES_HEADER_1, ALLOWED_RES_HEADER_2), mountConfig.getAllowedResponseHeaders(), "Unexpected allowed response headers");
assertEquals(List.of(ALLOWED_MANAGED_KEY_1, ALLOWED_MANAGED_KEY_2), mountConfig.getAllowedManagedKeys(), "Unexpected allowed managed keys");
assertEquals(List.of(DEL_AUTH_ACCESSOR_1, DEL_AUTH_ACCESSOR_2), mountConfig.getDelegatedAuthAccessors(), "Unexpected delegate auth accessors");
assertNotNull(mountConfig.getUserLockoutConfig(), "Missing user lockout config");
var ulc = mountConfig.getUserLockoutConfig();
assertEquals(LOCKOUT_THRESH, ulc.getLockoutThreshold(), "Unexpected lockout threshold");
assertEquals(LOCKOUT_DURATION, ulc.getLockoutDuration(), "Unexpected lockout duration");
assertEquals(LOCKOUT_CNT_RESET_DURATION, ulc.getLockoutCounterResetDuration(), "Unexpected lockout counter reset duration");
assertEquals(LOCKOUT_DISABLE, ulc.getLockoutDisable(), "Unexpected lockout disable");
}
}