Compare commits

..

No commits in common. "main" and "v1.1.0" have entirely different histories.
main ... v1.1.0

107 changed files with 1260 additions and 3360 deletions
.drone.yml.editorconfig
.github/workflows
.gitignore
.mvn/wrapper
CHANGELOG.mdREADME.mdmvnwmvnw.cmdpom.xml
src
main/java
test

39
.drone.yml Normal file

@ -0,0 +1,39 @@
kind: pipeline
name: default
steps:
- name: compile
image: maven:3-jdk-11
commands:
- mvn -B clean compile
when:
branch:
- main
- develop
- feature/*
- fix/*
- release/*
- name: unit-tests
image: maven:3-jdk-11
commands:
- mvn -B test
when:
branch:
- develop
- feature/*
- fix/*
- name: unit-integration-tests
image: maven:3-jdk-11
environment:
VAULT_VERSION: 1.10.1
commands:
- curl -s -o vault_1.10.1_linux_amd64.zip https://releases.hashicorp.com/vault/1.10.1/vault_1.10.1_linux_amd64.zip
- curl -s https://releases.hashicorp.com/vault/1.10.1/vault_1.10.1_SHA256SUMS | grep linux_amd64 | sha256sum -c
- unzip vault_1.10.1_linux_amd64.zip
- rm vault_1.10.1_linux_amd64.zip
- mv vault /bin/
- mvn -B -P integration-test verify
when:
branch:
- main
- release/*

@ -1,14 +0,0 @@
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

@ -1,56 +0,0 @@
name: CI
on:
push:
branches:
- 'main'
pull_request:
branches:
- 'main'
jobs:
build-with-it:
if: github.ref_name == 'main' || github.base_ref == 'main' || startsWith(github.ref_name, 'release/')
runs-on: ubuntu-latest
strategy:
matrix:
jdk: [ 11, 17, 21 ]
vault: [ '1.2.0', '1.19.0' ]
include:
- jdk: 21
vault: '1.19.0'
analysis: true
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Java
uses: actions/setup-java@v4
with:
java-version: ${{ matrix.jdk }}
distribution: 'temurin'
- name: Compile
run: ./mvnw -B clean compile
- name: Set up Vault
run: |
wget -q "https://releases.hashicorp.com/vault/${{ matrix.vault }}/vault_${{ matrix.vault }}_linux_amd64.zip"
wget -q -O - "https://releases.hashicorp.com/vault/${{ matrix.vault }}/vault_${{ matrix.vault }}_SHA256SUMS" | grep linux_amd64 | sha256sum -c
tmp="$(mktemp -d)"
unzip "vault_${{ matrix.vault }}_linux_amd64.zip" -d "$tmp"
rm "vault_${{ matrix.vault }}_linux_amd64.zip"
sudo mv "$tmp/vault" /usr/bin/vault
rm -rf "$tmp"
- name: Test (Unit & Integration)
env:
VAULT_VERSION: ${{ matrix.vault }}
run: ./mvnw -B -P coverage -P integration-test verify
- name: Analysis
if: matrix.analysis && env.SONAR_TOKEN != ''
run: >
./mvnw -B sonar:sonar
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.organization=stklcode-github
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

@ -1,44 +1,51 @@
name: CI
on:
push:
branches:
- '**'
- '!main'
pull_request:
branches:
- '**'
- '!main'
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
jdk: [ 11, 17, 21 ]
jdk: [ 11, 17 ]
vault: [ '1.10.1' ]
include:
- jdk: 21
- jdk: 11
vault: '1.10.1'
analysis: true
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Set up Java
uses: actions/setup-java@v4
uses: actions/setup-java@v2
with:
java-version: ${{ matrix.jdk }}
distribution: 'temurin'
- name: Compile
run: ./mvnw -B clean compile
run: mvn -B clean compile
- name: Set up Vault
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')
run: |
wget -q "https://releases.hashicorp.com/vault/${{ matrix.vault }}/vault_${{ matrix.vault }}_linux_amd64.zip"
wget -q -O - "https://releases.hashicorp.com/vault/${{ matrix.vault }}/vault_${{ matrix.vault }}_SHA256SUMS" | grep linux_amd64 | sha256sum -c
unzip "vault_${{ matrix.vault }}_linux_amd64.zip"
rm "vault_${{ matrix.vault }}_linux_amd64.zip"
sudo mv vault /usr/bin/vault
- name: Test (Unit & Integration)
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')
env:
VAULT_VERSION: ${{ matrix.vault }}
run: mvn -B -P coverage -P integration-test verify
- name: Test (Unit)
run: ./mvnw -B -P coverage verify
if: github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/')
run: mvn -B -P coverage verify
- name: Analysis
if: matrix.analysis && env.SONAR_TOKEN != ''
if: matrix.analysis && github.event_name == 'push'
run: >
./mvnw -B sonar:sonar
mvn -B sonar:sonar
-Dsonar.host.url=https://sonarcloud.io
-Dsonar.organization=stklcode-github
-Dsonar.login=$SONAR_TOKEN
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

20
.gitignore vendored

@ -1,17 +1,5 @@
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
.idea
*.iml
.bin
/target/
/*.iml
/.idea/
/*.project
*~

@ -1,2 +0,0 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar

@ -1,151 +1,3 @@
## 1.5.0 (2025-04-13)
### Deprecations
* `read...Credentials()` methods for specific database mounts (#92)
### Features
* Support Vault transit API (#89)
* Support PEM certificate string from `VAULT_CACERT` environment variable (#93)
### Improvements
* Replace deprecated `java.net.URL` usage with `java.net.URI` (#94)
### Fix
* Fix initialization from environment without explicit port
### Dependencies
* Updated Jackson to 2.18.3 (#90)
### Test
* Tested against Vault 1.2 to 1.19
## 1.4.0 (2024-12-07)
### Removal
* Remove deprecated `get...TimeString()` on model classes (#77)
* Drop support for deprecated `App-ID` auth backend (#61) (#78)
### Fix
* Add jackson-annotations requirement to module-info (#84)
### Dependencies
* Updated Jackson to 2.18.2 (#85)
### Test
* Tested against Vault 1.2 to 1.18
## 1.3.1 (2024-10-03)
### Dependencies
* Updated Jackson to 2.18.0 (#80)
### Fix
* Remove `Automatic-Module-Name` from JAR manifest (#79)
## 1.3.0 (2024-06-29)
### Improvements
* Simplify JSON parsing in error handler
* Add new fields from Vault 1.16 and 1.17 to `HealthResponse`
* `echo_duration_ms`
* `clock_skew_ms`
* `replication_primary_canary_age_ms`
* `enterprise`
* Add missing `num_uses` field to `AuthData`
* Add `mount_type` attribute to common response model
* Add `auth` attribute to common response model
* Add `custom_metadata`, `cas_required` and `delete_version_after` fields for KVv2 metadata
* Generate and attach CycloneDX SBOM
### Fix
* Rename `enable_local_secret_id` to `local_secret_ids` in `AppRole` model
### Dependencies
* Updated Jackson to 2.17.1
### Test
* Tested against Vault 1.2 to 1.17
## 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
* Use `[+-]XX:XX` notation for timezone in date/time parsing
### Improvements
* Use explicit UTF-8 encoding for parsing responses
### Dependencies
* Updated Jackson to 2.15.2
### Test
* Tested against Vault 1.2.0 to 1.13.3
## 1.1.3 (2023-01-31)
### Deprecations
* AppID components (deprecated since 0.4) are marked for removal with the next major release
### Dependencies
* Updated Jackson to 2.14.2
### Improvements
* Minor internal refactoring
### Test
* Tested against Vault 1.2.0 to 1.12.2
## 1.1.2 (2022-10-26)
### Dependencies
* Updated Jackson to 2.13.4.2
### Test
* Tested against Vault 1.2.0 to 1.12.0
* Disable AppID tests for Vault 1.12 and above (auth method removed)
* Tested with Java 19
## 1.1.1 (2022-08-29)
### Dependencies
* Updated Jackson to 2.13.3
### Test
* Tested against Vault 1.11.2
* Tested with Java 18
## 1.1.0 (2022-04-24)
### Fix

@ -1,11 +1,11 @@
# Java Vault Connector
[![CI](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml/badge.svg)](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=de.stklcode.jvault%3Ajvault-connector&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=de.stklcode.jvault%3Ajvault-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)
[![Maven Central Version](https://img.shields.io/maven-central/v/de.stklcode.jvault/jvault-connector)](https://central.sonatype.com/artifact/de.stklcode.jvault/jvault-connector)
[![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](assets/logo.png)
![Logo](https://raw.githubusercontent.com/stklcode/jvaultconnector/main/assets/logo.png)
Java Vault Connector is a connector library for [Vault](https://www.vaultproject.io) by [Hashicorp](https://www.hashicorp.com) written in Java. The connector allows simple usage of Vault's secret store in own applications.
@ -18,6 +18,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Token
* Username/Password
* AppRole (register and authenticate)
* AppID (register and authenticate) [_deprecated_]
* Tokens
* Creation and lookup of tokens and token roles
* TokenBuilder for speaking creation of complex configurations
@ -28,11 +29,10 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Delete secrets
* Renew/revoke leases
* Raw secret content or JSON decoding
* SQL secret handling
* KV v1 and v2 support
* Database secret handling
* Transit API support
* Connector Factory with builder pattern
* Tested against Vault 1.2 to 1.19
* Tested against Vault 1.10.1
## 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.5.0</version>
<version>1.1.0</version>
</dependency>
```

332
mvnw vendored

@ -1,332 +0,0 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.3.2
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ]; then
if [ -f /usr/local/etc/mavenrc ]; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ]; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ]; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false
darwin=false
mingw=false
case "$(uname)" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true ;;
Darwin*)
darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"
export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"
export JAVA_HOME
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ]; then
if [ -r /etc/gentoo-release ]; then
JAVA_HOME=$(java-config --jre-home)
fi
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin; then
[ -n "$JAVA_HOME" ] \
&& JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
[ -n "$CLASSPATH" ] \
&& CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] \
&& JAVA_HOME="$(
cd "$JAVA_HOME" || (
echo "cannot cd into $JAVA_HOME." >&2
exit 1
)
pwd
)"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "$javaExecutable" : '\([^ ]*\)')" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
if $darwin; then
javaHome="$(dirname "$javaExecutable")"
javaExecutable="$(cd "$javaHome" && pwd -P)/javac"
else
javaExecutable="$(readlink -f "$javaExecutable")"
fi
javaHome="$(dirname "$javaExecutable")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ]; then
if [ -n "$JAVA_HOME" ]; then
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(
\unset -f command 2>/dev/null
\command -v java
)"
fi
fi
if [ ! -x "$JAVACMD" ]; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ]; then
echo "Warning: JAVA_HOME environment variable is not set." >&2
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]; then
echo "Path not specified to find_maven_basedir" >&2
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ]; do
if [ -d "$wdir"/.mvn ]; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(
cd "$wdir/.." || exit 1
pwd
)
fi
# end of workaround
done
printf '%s' "$(
cd "$basedir" || exit 1
pwd
)"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' <"$1"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in wrapperUrl)
wrapperUrl="$safeValue"
break
;;
esac
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget >/dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl >/dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in wrapperSha256Sum)
wrapperSha256Sum=$value
break
;;
esac
done <"$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum >/dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c >/dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum >/dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c >/dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." >&2
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] \
&& JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] \
&& CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] \
&& MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

206
mvnw.cmd vendored

@ -1,206 +0,0 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.3.2
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo. >&2
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo. >&2
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo. >&2
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo. >&2
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash;"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Error 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Error 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Error 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%

276
pom.xml

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>1.5.1-SNAPSHOT</version>
<version>1.1.0</version>
<packaging>jar</packaging>
@ -21,11 +21,16 @@
</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>Europe/Berlin</timezone>
<timezone>+1</timezone>
</developer>
</developers>
@ -33,7 +38,6 @@
<connection>scm:git:git://github.com/stklcode/jvaultconnector.git</connection>
<developerConnection>scm:git:git@github.com:stklcode/jvaultconnector.git</developerConnection>
<url>https://github.com/stklcode/jvaultconnector</url>
<tag>HEAD</tag>
</scm>
<issueManagement>
@ -41,123 +45,56 @@
<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.18.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.18.3</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.12.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.17.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.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.19.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.19.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.14.0</version>
<version>3.10.1</version>
<configuration>
<release>11</release>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.4.1</version>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.4</version>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.5.3</version>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<argLine>
@{argLine}
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.databind
</argLine>
<archive>
<manifestEntries>
<Automatic-Module-Name>de.stklcode.jvault.connector</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.4</version>
<version>2.5.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.4.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.1</version>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.3</version>
<version>2.22.2</version>
<configuration>
<argLine>
@{argLine}
@ -168,55 +105,69 @@
--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.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<version>2.9.1</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.13</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>5.1.0.4751</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
<execution>
<id>enforce-versions</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>[3.6.3,)</version>
</requireMavenVersion>
<requireJavaVersion>
<version>[11,)</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.5.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.33.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>
<profile>
<id>sources</id>
@ -228,6 +179,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
@ -251,7 +203,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.11.2</version>
<version>3.4.0</version>
<configuration>
<source>11</source>
</configuration>
@ -268,29 +220,6 @@
</build>
</profile>
<profile>
<id>sbom</id>
<build>
<plugins>
<plugin>
<groupId>org.cyclonedx</groupId>
<artifactId>cyclonedx-maven-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>makeBom</goal>
</goals>
<configuration>
<skipNotDeployed>false</skipNotDeployed>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>sign</id>
<build>
@ -298,7 +227,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.2.7</version>
<version>3.0.1</version>
<executions>
<execution>
<id>sign-artifacts</id>
@ -323,6 +252,7 @@
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.8</version>
<executions>
<execution>
<id>default-prepare-agent</id>
@ -349,6 +279,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.22.2</version>
<executions>
<execution>
<goals>
@ -369,11 +300,7 @@
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>12.1.1</version>
<configuration>
<nvdApiKey>${env.NVD_API_KEY}</nvdApiKey>
<nvdDatafeedUrl>${env.NVD_DATAFEED_URL}</nvdDatafeedUrl>
</configuration>
<version>7.1.0</version>
<executions>
<execution>
<goals>
@ -387,20 +314,17 @@
</profile>
<profile>
<id>central</id>
<build>
<plugins>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>0.7.0</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
</configuration>
</plugin>
</plugins>
</build>
<id>sonatype</id>
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -41,62 +41,34 @@ import static java.util.Collections.singletonMap;
* @since 0.1
*/
public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_SYS = "sys";
private static final String PATH_SYS_AUTH = PATH_SYS + "/auth";
private static final String PATH_RENEW = PATH_SYS + "/leases/renew";
private static final String PATH_REVOKE = PATH_SYS + "/leases/revoke/";
private static final String PATH_HEALTH = PATH_SYS + "/health";
private static final String PATH_SEAL = PATH_SYS + "/seal";
private static final String PATH_SEAL_STATUS = PATH_SYS + "/seal-status";
private static final String PATH_UNSEAL = PATH_SYS + "/unseal";
private static final String PATH_AUTH = "auth";
private static final String PATH_AUTH_TOKEN = PATH_AUTH + "/token";
private static final String PATH_SEAL_STATUS = "sys/seal-status";
private static final String PATH_SEAL = "sys/seal";
private static final String PATH_UNSEAL = "sys/unseal";
private static final String PATH_RENEW = "sys/leases/renew";
private static final String PATH_AUTH = "sys/auth";
private static final String PATH_TOKEN = "auth/token";
private static final String PATH_LOOKUP = "/lookup";
private static final String PATH_CREATE = "/create";
private static final String PATH_ROLES = "/roles";
private static final String PATH_CREATE_ORPHAN = "/create-orphan";
private static final String PATH_AUTH_USERPASS = PATH_AUTH + "/userpass/login/";
private static final String PATH_AUTH_APPROLE = PATH_AUTH + "/approle";
private static final String PATH_AUTH_APPROLE_ROLE = PATH_AUTH_APPROLE + "/role/%s%s";
private static final String PATH_AUTH_USERPASS = "auth/userpass/login/";
private static final String PATH_AUTH_APPID = "auth/app-id/";
private static final String PATH_AUTH_APPROLE = "auth/approle/";
private static final String PATH_AUTH_APPROLE_ROLE = "auth/approle/role/%s%s";
private static final String PATH_REVOKE = "sys/leases/revoke/";
private static final String PATH_HEALTH = "sys/health";
private static final String PATH_DATA = "/data/";
private static final String PATH_METADATA = "/metadata/";
private static final String PATH_LOGIN = "/login";
private static final String PATH_DELETE = "/delete/";
private static final String PATH_UNDELETE = "/undelete/";
private static final String PATH_DESTROY = "/destroy/";
private static final String PATH_TRANSIT = "transit";
private static final String PATH_TRANSIT_ENCRYPT = PATH_TRANSIT + "/encrypt/";
private static final String PATH_TRANSIT_DECRYPT = PATH_TRANSIT + "/decrypt/";
private static final String PATH_TRANSIT_HASH = PATH_TRANSIT + "/hash/";
private final RequestHelper request;
private boolean authorized = false; // Authorization status.
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.
*
@ -130,6 +102,24 @@ 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;
@ -149,10 +139,11 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
Map<String, String> param = mapOfStrings(
"key", key,
"reset", reset
);
Map<String, String> param = new HashMap<>(2, 1);
param.put("key", key);
if (reset != null) {
param.put("reset", reset.toString());
}
return request.put(PATH_UNSEAL, param, token, SealResponse.class);
}
@ -181,7 +172,7 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
/* Issue request and parse response */
AuthMethodsResponse amr = request.get(PATH_SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
AuthMethodsResponse amr = request.get(PATH_AUTH, emptyMap(), token, AuthMethodsResponse.class);
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
}
@ -191,7 +182,7 @@ public class HTTPVaultConnector implements VaultConnector {
/* set token */
this.token = token;
this.tokenTTL = 0;
TokenResponse res = request.post(PATH_AUTH_TOKEN + PATH_LOOKUP, emptyMap(), token, TokenResponse.class);
TokenResponse res = request.post(PATH_TOKEN + PATH_LOOKUP, emptyMap(), token, TokenResponse.class);
authorized = true;
return res;
@ -205,12 +196,25 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException {
final Map<String, String> payload = mapOfStrings(
"role_id", roleID,
"secret_id", secretID
@Deprecated(since = "0.4", forRemoval = false)
public final AuthResponse authAppId(final String appID, final String userID) throws VaultConnectorException {
return queryAuth(
PATH_AUTH_APPID + "login",
Map.of(
"app_id", appID,
"user_id", userID
)
);
return queryAuth(PATH_AUTH_APPROLE + PATH_LOGIN, payload);
}
@Override
public final AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException {
final Map<String, String> payload = new HashMap<>(2, 1);
payload.put("role_id", roleID);
if (secretID != null) {
payload.put("secret_id", secretID);
}
return queryAuth(PATH_AUTH_APPROLE + "login", payload);
}
/**
@ -233,6 +237,40 @@ public class HTTPVaultConnector implements VaultConnector {
return auth;
}
@Override
@Deprecated(since = "0.4", forRemoval = false)
public final boolean registerAppId(final String appID, final String policy, final String displayName)
throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
PATH_AUTH_APPID + "map/app-id/" + appID,
Map.of(
"value", policy,
"display_name", displayName
),
token
);
return true;
}
@Override
@Deprecated(since = "0.4", forRemoval = false)
public final boolean registerUserId(final String appID, final String userID) throws VaultConnectorException {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(
PATH_AUTH_APPID + "map/user-id/" + userID,
singletonMap("value", appID),
token
);
return true;
}
@Override
public final boolean createAppRole(final AppRole role) throws VaultConnectorException {
requireAuth();
@ -346,7 +384,7 @@ public class HTTPVaultConnector implements VaultConnector {
requireAuth();
SecretListResponse secrets = request.get(
PATH_AUTH_APPROLE + "/role?list=true",
PATH_AUTH_APPROLE + "role?list=true",
emptyMap(),
token,
SecretListResponse.class
@ -377,18 +415,19 @@ 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);
Map<String, String> args = new HashMap<>(1, 1);
if (version != null) {
args.put("version", version.toString());
}
return request.get(mount + PATH_DATA + key, args, token, MetaSecretResponse.class);
}
@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 */
@ -396,25 +435,20 @@ 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(
"max_versions", maxVersions,
"cas_required", casRequired
);
Map<String, Object> payload = new HashMap<>(2, 1);
if (maxVersions != null) {
payload.put("max_versions", maxVersions);
}
payload.put("cas_required", casRequired);
write(mount + PATH_METADATA + key, payload);
}
@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()) {
@ -422,7 +456,12 @@ public class HTTPVaultConnector implements VaultConnector {
}
// Add CAS value to options map if present.
Map<String, Object> options = mapOf("cas", cas);
Map<String, Object> options;
if (cas != null) {
options = singletonMap("cas", cas);
} else {
options = emptyMap();
}
/* Issue request and parse metadata response */
return request.post(
@ -446,15 +485,14 @@ 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()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
// By default, data is directly passed as payload.
// By default data is directly passed as payload.
Object payload = data;
// If options are given, split payload in two parts.
@ -488,20 +526,17 @@ 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);
}
@ -515,10 +550,7 @@ 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 */
@ -540,10 +572,11 @@ public class HTTPVaultConnector implements VaultConnector {
public final SecretResponse renew(final String leaseID, final Integer increment) throws VaultConnectorException {
requireAuth();
Map<String, String> payload = mapOfStrings(
"lease_id", leaseID,
"increment", increment
);
Map<String, String> payload = new HashMap<>(2, 1);
payload.put("lease_id", leaseID);
if (increment != null) {
payload.put("increment", increment.toString());
}
/* Issue request and parse secret response */
return request.put(PATH_RENEW, payload, token, SecretResponse.class);
@ -551,12 +584,12 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final AuthResponse createToken(final Token token) throws VaultConnectorException {
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE);
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE);
}
@Override
public final AuthResponse createToken(final Token token, final boolean orphan) throws VaultConnectorException {
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE_ORPHAN);
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE_ORPHAN);
}
@Override
@ -564,7 +597,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (role == null || role.isEmpty()) {
throw new InvalidRequestException("No role name specified.");
}
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE + "/" + role);
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE + "/" + role);
}
@Override
@ -599,7 +632,7 @@ public class HTTPVaultConnector implements VaultConnector {
/* Request HTTP response and parse Secret */
return request.get(
PATH_AUTH_TOKEN + PATH_LOOKUP,
PATH_TOKEN + PATH_LOOKUP,
singletonMap("token", token),
token,
TokenResponse.class
@ -617,7 +650,7 @@ public class HTTPVaultConnector implements VaultConnector {
}
// Issue request and expect code 204 with empty response.
request.postWithoutResponse(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, role, token);
request.postWithoutResponse(PATH_TOKEN + PATH_ROLES + "/" + name, role, token);
return true;
}
@ -627,14 +660,14 @@ public class HTTPVaultConnector implements VaultConnector {
requireAuth();
// Request HTTP response and parse response.
return request.get(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
return request.get(PATH_TOKEN + PATH_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
}
@Override
public List<String> listTokenRoles() throws VaultConnectorException {
requireAuth();
return list(PATH_AUTH_TOKEN + PATH_ROLES);
return list(PATH_TOKEN + PATH_ROLES);
}
@Override
@ -646,52 +679,11 @@ public class HTTPVaultConnector implements VaultConnector {
}
// Issue request and expect code 204 with empty response.
request.deleteWithoutResponse(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, token);
request.deleteWithoutResponse(PATH_TOKEN + PATH_ROLES + "/" + name, token);
return true;
}
@Override
public final TransitResponse transitEncrypt(final String keyName, final String plaintext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"plaintext", plaintext
);
return request.post(PATH_TRANSIT_ENCRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse transitDecrypt(final String keyName, final String ciphertext)
throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = mapOf(
"ciphertext", ciphertext
);
return request.post(PATH_TRANSIT_DECRYPT + keyName, payload, token, TransitResponse.class);
}
@Override
public final TransitResponse transitHash(final String algorithm, final String input, final String format)
throws VaultConnectorException {
if (format != null && !"hex".equals(format) && !"base64".equals(format)) {
throw new IllegalArgumentException("Unsupported format " + format);
}
requireAuth();
Map<String, Object> payload = mapOf(
"input", input,
"format", format
);
return request.post(PATH_TRANSIT_HASH + algorithm, payload, token, TransitResponse.class);
}
/**
* Check for required authorization.
*
@ -703,42 +695,4 @@ public class HTTPVaultConnector implements VaultConnector {
throw new AuthorizationRequiredException();
}
}
/**
* Generate a map of non-null {@link String} keys and values
*
* @param keyValues Key-value tuples as vararg.
* @return The map of non-null keys and values.
*/
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) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (key instanceof String && val != null) {
map.put((String) key, val.toString());
}
}
return map;
}
/**
* Generate a map of non-null {@link String} keys and {@link Object} values
*
* @param keyValues Key-value tuples as vararg.
* @return The map of non-null keys and values.
*/
private static Map<String, Object> mapOf(Object... keyValues) {
Map<String, Object> map = new HashMap<>(keyValues.length / 2, 1);
for (int i = 0; i < keyValues.length; i = i + 2) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (key instanceof String && val != null) {
map.put((String) key, val);
}
}
return map;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,17 +20,18 @@ import de.stklcode.jvault.connector.exception.ConnectionException;
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Objects;
/**
* Vault Connector Builder implementation for HTTP Vault connectors.
@ -95,14 +96,10 @@ public final class HTTPVaultConnectorBuilder {
* @since 1.0
*/
public HTTPVaultConnectorBuilder withBaseURL(final URI baseURL) {
String path = baseURL.getPath();
if (path == null || path.isBlank()) {
path = DEFAULT_PREFIX;
}
return withTLS(!("http".equalsIgnoreCase(baseURL.getScheme())))
return withTLS(!("http".equalsIgnoreCase(Objects.requireNonNullElse(baseURL.getScheme(), ""))))
.withHost(baseURL.getHost())
.withPort(baseURL.getPort())
.withPrefix(path);
.withPrefix(baseURL.getPath());
}
/**
@ -296,7 +293,7 @@ public final class HTTPVaultConnectorBuilder {
}
/**
* Build connector based on the {@code VAULT_ADDR} and {@code VAULT_CACERT} (optional) environment variables.
* Build connector based on the {@code }VAULT_ADDR} and {@code VAULT_CACERT} (optional) environment variables.
*
* @return self
* @throws VaultConnectorException if Vault address from environment variables is malformed
@ -304,10 +301,13 @@ public final class HTTPVaultConnectorBuilder {
*/
public HTTPVaultConnectorBuilder fromEnv() throws VaultConnectorException {
/* Parse URL from environment variable */
if (System.getenv(ENV_VAULT_ADDR) != null && !System.getenv(ENV_VAULT_ADDR).isBlank()) {
if (System.getenv(ENV_VAULT_ADDR) != null && !System.getenv(ENV_VAULT_ADDR).trim().isEmpty()) {
try {
withBaseURL(System.getenv(ENV_VAULT_ADDR));
} catch (URISyntaxException e) {
var url = new URL(System.getenv(ENV_VAULT_ADDR));
this.host = url.getHost();
this.port = url.getPort();
this.tls = url.getProtocol().equals("https");
} catch (MalformedURLException e) {
throw new ConnectionException("URL provided in environment variable malformed", e);
}
}
@ -315,7 +315,7 @@ public final class HTTPVaultConnectorBuilder {
/* Read number of retries */
if (System.getenv(ENV_VAULT_MAX_RETRIES) != null) {
try {
withNumberOfRetries(Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES)));
numberOfRetries = Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES));
} catch (NumberFormatException ignored) {
/* Ignore malformed values. */
}
@ -325,12 +325,8 @@ public final class HTTPVaultConnectorBuilder {
token = System.getenv(ENV_VAULT_TOKEN);
/* Parse certificate, if set */
if (System.getenv(ENV_VAULT_CACERT) != null && !System.getenv(ENV_VAULT_CACERT).isBlank()) {
X509Certificate cert = certificateFromString(System.getenv(ENV_VAULT_CACERT));
if (cert == null) {
cert = certificateFromFile(Paths.get(System.getenv(ENV_VAULT_CACERT)));
}
return withTrustedCA(cert);
if (System.getenv(ENV_VAULT_CACERT) != null && !System.getenv(ENV_VAULT_CACERT).trim().isEmpty()) {
return withTrustedCA(Paths.get(System.getenv(ENV_VAULT_CACERT)));
}
return this;
}
@ -402,28 +398,6 @@ public final class HTTPVaultConnectorBuilder {
return con;
}
/**
* Read given certificate file to X.509 certificate.
*
* @param cert Certificate string (optionally PEM)
* @return X.509 Certificate object if parseable, else {@code null}
* @throws TlsException on error
* @since 1.5.0
*/
private X509Certificate certificateFromString(final String cert) throws TlsException {
// Check if PEM header is present in given string
if (cert.contains("-BEGIN ") && cert.contains("-END")) {
try (var is = new ByteArrayInputStream(cert.getBytes(StandardCharsets.UTF_8))) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
} catch (IOException | CertificateException e) {
throw new TlsException("Unable to read certificate.", e);
}
}
// Not am PEM string, skip
return null;
}
/**
* Read given certificate file to X.509 certificate.
*

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,10 @@ import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import java.io.Serializable;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Vault Connector interface.
@ -109,6 +112,18 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
AuthResponse authUserPass(final String username, final String password) throws VaultConnectorException;
/**
* Authorize to Vault using AppID method.
*
* @param appID The App ID
* @param userID The User ID
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #authAppRole} instead.
*/
@Deprecated(since = "0.4", forRemoval = false)
AuthResponse authAppId(final String appID, final String userID) throws VaultConnectorException;
/**
* Authorize to Vault using AppRole method without secret ID.
*
@ -132,6 +147,20 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException;
/**
* Register new App-ID with policy.
*
* @param appID The unique App-ID
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #createAppRole} instead.
*/
@Deprecated(since = "0.4", forRemoval = false)
boolean registerAppId(final String appID, final String policy, final String displayName)
throws VaultConnectorException;
/**
* Register a new AppRole role from given metamodel.
*
@ -313,6 +342,38 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException;
/**
* Register User-ID with App-ID.
*
* @param appID The App-ID
* @param userID The User-ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
* Consider using {@link #createAppRoleSecret} instead.
*/
@Deprecated(since = "0.4", forRemoval = false)
boolean registerUserId(final String appID, final String userID) throws VaultConnectorException;
/**
* Register new App-ID and User-ID at once.
*
* @param appID The App-ID
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @param userID The User-ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
*/
@Deprecated(since = "0.4", forRemoval = false)
default boolean registerAppUserId(final String appID,
final String policy,
final String displayName,
final String userID) throws VaultConnectorException {
return registerAppId(appID, policy, userID) && registerUserId(appID, userID);
}
/**
* Get authorization status.
*
@ -359,9 +420,7 @@ 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);
}
@ -379,10 +438,7 @@ 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.
@ -397,8 +453,7 @@ 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.
@ -427,10 +482,7 @@ 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.
@ -475,8 +527,7 @@ 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.
@ -523,8 +574,7 @@ 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.
@ -536,8 +586,7 @@ 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.
@ -549,8 +598,7 @@ 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.
@ -671,82 +719,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
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.
*
@ -754,9 +726,7 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
* @deprecated use {@link #readDbCredentials(String, String)} your MySQL mountpoint
*/
@Deprecated(since = "1.5.0", forRemoval = true)
default CredentialsResponse readMySqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mysql");
}
@ -768,9 +738,7 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
* @deprecated use {@link #readDbCredentials(String, String)} your PostgreSQL mountpoint
*/
@Deprecated(since = "1.5.0", forRemoval = true)
default CredentialsResponse readPostgreSqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "postgresql");
}
@ -782,32 +750,28 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
* @deprecated use {@link #readDbCredentials(String, String)} your MSSQL mountpoint
*/
@Deprecated(since = "1.5.0", forRemoval = true)
default CredentialsResponse readMsSqlCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mssql");
}
/**
* Read credentials for MongoDB backend at default mount point.
* Read credentials for MSSQL backend at default mount point.
*
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
* @deprecated use {@link #readDbCredentials(String, String)} your MongoDB mountpoint
*/
@Deprecated(since = "1.5.0", forRemoval = true)
default CredentialsResponse readMongoDbCredentials(final String role) throws VaultConnectorException {
return readDbCredentials(role, "mongodb");
}
/**
* Read credentials for database backends.
* Read credentials for SQL backends.
*
* @param role the role name
* @param mount mount point of the database backend
* @param mount mount point of the SQL backend
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,5 +23,4 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public class AuthorizationRequiredException extends VaultConnectorException {
private static final long serialVersionUID = 2629577936657393880L;
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public class ConnectionException extends VaultConnectorException {
private static final long serialVersionUID = 3005430116002990418L;
/**
* Constructs a new empty exception.
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public class InvalidRequestException extends VaultConnectorException {
private static final long serialVersionUID = -6712239648281809159L;
/**
* Constructs a new empty exception.
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,8 +24,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public final class InvalidResponseException extends VaultConnectorException {
private static final long serialVersionUID = 2003151038614163479L;
private final Integer statusCode;
private final String response;

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public class PermissionDeniedException extends VaultConnectorException {
private static final long serialVersionUID = -7149134015090750776L;
/**
* Constructs a new empty exception.
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.4.0
*/
public class TlsException extends VaultConnectorException {
private static final long serialVersionUID = -5139276834988258086L;
/**
* Constructs a new empty exception.
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,8 +23,6 @@ package de.stklcode.jvault.connector.exception;
* @since 0.1
*/
public abstract class VaultConnectorException extends Exception {
private static final long serialVersionUID = -2612477894310906036L;
/**
* Constructs a new empty exception.
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,10 +1,7 @@
package de.stklcode.jvault.connector.internal;
import com.fasterxml.jackson.core.JsonProcessingException;
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 com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.model.response.ErrorResponse;
@ -44,7 +41,7 @@ public final class RequestHelper implements Serializable {
private final int retries; // Number of retries on 5xx errors.
private final String tlsVersion; // TLS version (#22).
private final X509Certificate trustedCaCert; // Trusted CA certificate.
private final JsonMapper jsonMapper;
private final ObjectMapper jsonMapper;
/**
* Constructor of the request helper.
@ -65,11 +62,7 @@ public final class RequestHelper implements Serializable {
this.timeout = timeout;
this.tlsVersion = tlsVersion;
this.trustedCaCert = trustedCaCert;
this.jsonMapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.build();
this.jsonMapper = new ObjectMapper();
}
/**
@ -134,8 +127,7 @@ 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);
}
@ -151,8 +143,7 @@ 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));
@ -263,8 +254,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("&"))
);
}
@ -364,15 +355,6 @@ 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
}
}
}
}
@ -404,8 +386,7 @@ 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);
}
}
@ -418,7 +399,7 @@ public final class RequestHelper implements Serializable {
* @throws InvalidResponseException on reading errors
*/
private String handleResult(final HttpResponse<InputStream> response) throws InvalidResponseException {
try (var reader = new BufferedReader(new InputStreamReader(response.body(), UTF_8))) {
try (var reader = new BufferedReader(new InputStreamReader(response.body()))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
@ -432,19 +413,18 @@ public final class RequestHelper implements Serializable {
* @throws VaultConnectorException Expected exception with details to throw
*/
private void handleError(final HttpResponse<InputStream> response) throws VaultConnectorException {
try (var body = response.body()) {
if (body != null) {
try (var reader = new BufferedReader(new InputStreamReader(body, UTF_8))) {
ErrorResponse er = jsonMapper.readValue(reader, ErrorResponse.class);
if (response.body() != null) {
try (var reader = new BufferedReader(new InputStreamReader(response.body()))) {
var responseString = reader.lines().collect(Collectors.joining("\n"));
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied")) {
throw new PermissionDeniedException();
}
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString());
}
}
} catch (IOException ignored) {
// Exception ignored.
}
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +32,18 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRole implements Serializable {
private static final long serialVersionUID = 693228837510483448L;
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;
@ -55,9 +66,9 @@ public final class AppRole implements Serializable {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer secretIdTtl;
@JsonProperty("local_secret_ids")
@JsonProperty("enable_local_secret_ids")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean localSecretIds;
private Boolean enableLocalSecretIds;
@JsonProperty("token_ttl")
@JsonInclude(JsonInclude.Include.NON_NULL)
@ -111,7 +122,7 @@ public final class AppRole implements Serializable {
this.secretIdBoundCidrs = builder.secretIdBoundCidrs;
this.secretIdNumUses = builder.secretIdNumUses;
this.secretIdTtl = builder.secretIdTtl;
this.localSecretIds = builder.localSecretIds;
this.enableLocalSecretIds = builder.enableLocalSecretIds;
this.tokenTtl = builder.tokenTtl;
this.tokenMaxTtl = builder.tokenMaxTtl;
this.tokenPolicies = builder.tokenPolicies;
@ -123,17 +134,6 @@ 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
*/
@ -262,10 +262,9 @@ public final class AppRole implements Serializable {
/**
* @return Enable local secret IDs?
* @since 0.9
* @since 1.3 renamed to {@code getLocalSecretIds()}
*/
public Boolean getLocalSecretIds() {
return localSecretIds;
public Boolean getEnableLocalSecretIds() {
return enableLocalSecretIds;
}
/**
@ -336,7 +335,7 @@ public final class AppRole implements Serializable {
Objects.equals(secretIdBoundCidrs, appRole.secretIdBoundCidrs) &&
Objects.equals(secretIdNumUses, appRole.secretIdNumUses) &&
Objects.equals(secretIdTtl, appRole.secretIdTtl) &&
Objects.equals(localSecretIds, appRole.localSecretIds) &&
Objects.equals(enableLocalSecretIds, appRole.enableLocalSecretIds) &&
Objects.equals(tokenTtl, appRole.tokenTtl) &&
Objects.equals(tokenMaxTtl, appRole.tokenMaxTtl) &&
Objects.equals(tokenPolicies, appRole.tokenPolicies) &&
@ -351,7 +350,7 @@ public final class AppRole implements Serializable {
@Override
public int hashCode() {
return Objects.hash(name, id, bindSecretId, secretIdBoundCidrs, secretIdNumUses, secretIdTtl,
localSecretIds, tokenTtl, tokenMaxTtl, tokenPolicies, tokenBoundCidrs, tokenExplicitMaxTtl,
enableLocalSecretIds, tokenTtl, tokenMaxTtl, tokenPolicies, tokenBoundCidrs, tokenExplicitMaxTtl,
tokenNoDefaultPolicy, tokenNumUses, tokenPeriod, tokenType);
}
@ -371,7 +370,7 @@ public final class AppRole implements Serializable {
private List<String> tokenPolicies;
private Integer secretIdNumUses;
private Integer secretIdTtl;
private Boolean localSecretIds;
private Boolean enableLocalSecretIds;
private Integer tokenTtl;
private Integer tokenMaxTtl;
private List<String> tokenBoundCidrs;
@ -528,13 +527,12 @@ public final class AppRole implements Serializable {
/**
* Enable or disable local secret IDs.
*
* @param localSecretIds Enable local secret IDs?
* @param enableLocalSecretIds Enable local secret IDs?
* @return self
* @since 0.9
* @since 1.3 renamed to {@code withLocalSecretIds()}
*/
public Builder withLocalSecretIds(final Boolean localSecretIds) {
this.localSecretIds = localSecretIds;
public Builder withEnableLocalSecretIds(final Boolean enableLocalSecretIds) {
this.enableLocalSecretIds = enableLocalSecretIds;
return this;
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ package de.stklcode.jvault.connector.model;
*/
public enum AuthBackend {
TOKEN("token"),
APPID("app-id"),
APPROLE("approle"),
USERPASS("userpass"),
GITHUB("github"), // Not supported yet.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,6 +34,16 @@ 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;
@ -113,16 +123,6 @@ 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
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -36,6 +36,15 @@ 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;
@ -120,15 +129,6 @@ 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
*/

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +17,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import java.util.Objects;
/**
* Vault response for authentication providing auth info in {@link AuthData} field.
*
@ -28,4 +31,30 @@ import de.stklcode.jvault.connector.model.response.embedded.AuthData;
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthResponse extends VaultDataResponse {
private static final long serialVersionUID = 1628851361067456715L;
@JsonProperty("auth")
private AuthData auth;
/**
* @return Authentication data
*/
public AuthData getAuth() {
return auth;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
AuthResponse that = (AuthResponse) o;
return Objects.equals(auth, that.auth);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), auth);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,7 +29,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class HealthResponse implements VaultResponse {
private static final long serialVersionUID = 8675155916902904516L;
private static final long serialVersionUID = 6483840078694294401L;
@JsonProperty("cluster_id")
private String clusterID;
@ -61,18 +61,6 @@ public final class HealthResponse implements VaultResponse {
@JsonProperty("performance_standby")
private Boolean performanceStandby;
@JsonProperty("echo_duration_ms")
private Long echoDurationMs;
@JsonProperty("clock_skew_ms")
private Long clockSkewMs;
@JsonProperty("replication_primary_canary_age_ms")
private Long replicationPrimaryCanaryAgeMs;
@JsonProperty("enterprise")
private Boolean enterprise;
/**
* @return The Cluster ID.
*/
@ -146,38 +134,6 @@ public final class HealthResponse implements VaultResponse {
return performanceStandby;
}
/**
* @return Heartbeat echo duration in milliseconds (since Vault 1.16)
* @since 1.3
*/
public Long getEchoDurationMs() {
return echoDurationMs;
}
/**
* @return Clock skew in milliseconds (since Vault 1.16)
* @since 1.3
*/
public Long getClockSkewMs() {
return clockSkewMs;
}
/**
* @return Replication primary canary age in milliseconds (since Vault 1.17)
* @since 1.3
*/
public Long getReplicationPrimaryCanaryAgeMs() {
return replicationPrimaryCanaryAgeMs;
}
/**
* @return Enterprise instance? (since Vault 1.17)
* @since 1.3
*/
public Boolean isEnterprise() {
return enterprise;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -195,17 +151,12 @@ public final class HealthResponse implements VaultResponse {
Objects.equals(initialized, that.initialized) &&
Objects.equals(replicationPerfMode, that.replicationPerfMode) &&
Objects.equals(replicationDrMode, that.replicationDrMode) &&
Objects.equals(performanceStandby, that.performanceStandby) &&
Objects.equals(echoDurationMs, that.echoDurationMs) &&
Objects.equals(clockSkewMs, that.clockSkewMs) &&
Objects.equals(replicationPrimaryCanaryAgeMs, that.replicationPrimaryCanaryAgeMs) &&
Objects.equals(enterprise, that.enterprise);
Objects.equals(performanceStandby, that.performanceStandby);
}
@Override
public int hashCode() {
return Objects.hash(clusterID, clusterName, version, serverTimeUTC, standby, sealed, initialized,
replicationPerfMode, replicationDrMode, performanceStandby, echoDurationMs, clockSkewMs,
replicationPrimaryCanaryAgeMs, enterprise);
replicationPerfMode, replicationDrMode, performanceStandby);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ 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;
/**
@ -30,7 +29,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SealResponse implements VaultResponse {
private static final long serialVersionUID = -6000309255473305787L;
private static final long serialVersionUID = -3661916639367542617L;
@JsonProperty("type")
private String type;
@ -53,9 +52,6 @@ public final class SealResponse implements VaultResponse {
@JsonProperty("version")
private String version;
@JsonProperty("build_date")
private ZonedDateTime buildDate;
@JsonProperty("nonce")
private String nonce;
@ -126,14 +122,6 @@ 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
@ -197,7 +185,6 @@ 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) &&
@ -208,7 +195,7 @@ public final class SealResponse implements VaultResponse {
@Override
public int hashCode() {
return Objects.hash(type, sealed, initialized, threshold, numberOfShares, progress, version, buildDate, nonce,
return Objects.hash(type, sealed, initialized, threshold, numberOfShares, progress, version, nonce,
clusterName, clusterId, migration, recoverySeal, storageType);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,10 +17,7 @@
package de.stklcode.jvault.connector.model.response;
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 com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
@ -82,21 +79,8 @@ 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 = JsonMapper.builder()
.addModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.build();
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());
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,11 +30,14 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenResponse extends VaultDataResponse {
private static final long serialVersionUID = -4341114947980033457L;
private static final long serialVersionUID = -4053126653764241197L;
@JsonProperty("data")
private TokenData data;
@JsonProperty("auth")
private Boolean auth;
/**
* @return Token data
*/
@ -42,6 +45,12 @@ public final class TokenResponse extends VaultDataResponse {
return data;
}
/**
* @return Auth data
*/
public Boolean getAuth() {
return auth;
}
@Override
public boolean equals(Object o) {
@ -51,11 +60,11 @@ public final class TokenResponse extends VaultDataResponse {
return false;
}
TokenResponse that = (TokenResponse) o;
return Objects.equals(data, that.data);
return Objects.equals(data, that.data) && Objects.equals(auth, that.auth);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
return Objects.hash(super.hashCode(), data, auth);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,92 +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.model.response;
import com.fasterxml.jackson.annotation.JsonSetter;
import java.util.Map;
import java.util.Objects;
/**
* Response entity for transit operations.
*
* @author Stefan Kalscheuer
* @since 1.5.0
*/
public class TransitResponse extends VaultDataResponse {
private static final long serialVersionUID = 6873804240772242771L;
private String ciphertext;
private String plaintext;
private String sum;
@JsonSetter("data")
private void setData(Map<String, String> data) {
ciphertext = data.get("ciphertext");
plaintext = data.get("plaintext");
sum = data.get("sum");
}
/**
* Get ciphertext.
* Populated after encryption.
*
* @return Ciphertext
*/
public String getCiphertext() {
return ciphertext;
}
/**
* Get plaintext.
* Base64 encoded, populated after decryption.
*
* @return Plaintext
*/
public String getPlaintext() {
return plaintext;
}
/**
* Get hash sum.
* Hex or Base64 string. Populated after hashing.
*
* @return Hash sum
*/
public String getSum() {
return sum;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
TransitResponse that = (TransitResponse) o;
return Objects.equals(ciphertext, that.ciphertext) &&
Objects.equals(plaintext, that.plaintext) &&
Objects.equals(sum, that.sum);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), ciphertext, plaintext, sum);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import de.stklcode.jvault.connector.model.response.embedded.WrapInfo;
import java.util.List;
@ -30,7 +29,7 @@ import java.util.Objects;
* @since 0.1
*/
public abstract class VaultDataResponse implements VaultResponse {
private static final long serialVersionUID = 4787715235558510045L;
private static final long serialVersionUID = 7486270767477652184L;
@JsonProperty("request_id")
private String requestId;
@ -50,12 +49,6 @@ public abstract class VaultDataResponse implements VaultResponse {
@JsonProperty("wrap_info")
private WrapInfo wrapInfo;
@JsonProperty("auth")
private AuthData auth;
@JsonProperty("mount_type")
private String mountType;
/**
* @return Request ID
* @since 1.1
@ -100,22 +93,6 @@ public abstract class VaultDataResponse implements VaultResponse {
return wrapInfo;
}
/**
* @return Authentication information for this response
* @since 1.3
*/
public final AuthData getAuth() {
return auth;
}
/**
* @return Information about the type of mount this secret is from (since Vault 1.17)
* @since 1.3
*/
public final String getMountType() {
return mountType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -129,13 +106,11 @@ public abstract class VaultDataResponse implements VaultResponse {
Objects.equals(leaseId, that.leaseId) &&
Objects.equals(leaseDuration, that.leaseDuration) &&
Objects.equals(warnings, that.warnings) &&
Objects.equals(wrapInfo, that.wrapInfo) &&
Objects.equals(auth, that.auth) &&
Objects.equals(mountType, that.mountType);
Objects.equals(wrapInfo, that.wrapInfo);
}
@Override
public int hashCode() {
return Objects.hash(requestId, leaseId, renewable, leaseDuration, warnings, wrapInfo, auth, mountType);
return Objects.hash(requestId, leaseId, renewable, leaseDuration, warnings, wrapInfo);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,7 +33,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthData implements Serializable {
private static final long serialVersionUID = 5969334512309655317L;
private static final long serialVersionUID = -6962244199229885869L;
@JsonProperty("client_token")
private String clientToken;
@ -65,12 +65,6 @@ public final class AuthData implements Serializable {
@JsonProperty("orphan")
private boolean orphan;
@JsonProperty("num_uses")
private Integer numUses;
@JsonProperty("mfa_requirement")
private MfaRequirement mfaRequirement;
/**
* @return Client token
*/
@ -137,14 +131,6 @@ public final class AuthData implements Serializable {
return accessor;
}
/**
* @return allowed number of uses for the issued token
* @since 1.3
*/
public Integer getNumUses() {
return numUses;
}
/**
* @return Token is orphan
* @since 0.9
@ -153,14 +139,6 @@ 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) {
@ -179,14 +157,12 @@ 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(numUses, authData.numUses) &&
Objects.equals(mfaRequirement, authData.mfaRequirement);
Objects.equals(tokenType, authData.tokenType);
}
@Override
public int hashCode() {
return Objects.hash(clientToken, accessor, policies, tokenPolicies, metadata, leaseDuration, renewable,
entityId, tokenType, orphan, numUses, mfaRequirement);
entityId, tokenType, orphan);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,7 +34,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethod implements Serializable {
private static final long serialVersionUID = -439987082190917691L;
private static final long serialVersionUID = -2718660627880077335L;
private AuthBackend type;
private String rawType;
@ -42,14 +42,11 @@ 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 MountConfig config;
private Map<String, String> config;
@JsonProperty("external_entropy_access")
private boolean externalEntropyAccess;
@ -57,18 +54,6 @@ 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;
@ -106,14 +91,6 @@ public final class AuthMethod implements Serializable {
return accessor;
}
/**
* @return Deprecation status
* @since 1.2
*/
public String getDeprecationStatus() {
return deprecationStatus;
}
/**
* @return Description
*/
@ -123,10 +100,8 @@ public final class AuthMethod implements Serializable {
/**
* @return Configuration data
* @since 0.2
* @since 1.2 Returns {@link MountConfig} instead of {@link Map}
*/
public MountConfig getConfig() {
public Map<String, String> getConfig() {
return config;
}
@ -145,38 +120,6 @@ 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
@ -207,19 +150,13 @@ 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, deprecationStatus, description, config, externalEntropyAccess,
local, options, pluginVersion, runningPluginVersion, runningSha256, sealWrap, uuid);
return Objects.hash(type, rawType, accessor, description, config, externalEntropyAccess, local, sealWrap, uuid);
}
}

@ -1,62 +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.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);
}
}

@ -1,94 +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.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);
}
}

@ -1,73 +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.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);
}
}

@ -1,168 +0,0 @@
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);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,7 +21,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Map;
import java.util.Objects;
@ -34,10 +35,12 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretMetadata implements Serializable {
private static final long serialVersionUID = -905059942871916214L;
private static final long serialVersionUID = 1684891108903409038L;
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
@JsonProperty("created_time")
private ZonedDateTime createdTime;
private String createdTimeString;
@JsonProperty("current_version")
private Integer currentVersion;
@ -49,25 +52,31 @@ public final class SecretMetadata implements Serializable {
private Integer oldestVersion;
@JsonProperty("updated_time")
private ZonedDateTime updatedTime;
private String updatedTime;
@JsonProperty("versions")
private Map<Integer, VersionMetadata> versions;
@JsonProperty("cas_required")
private Boolean casRequired;
@JsonProperty("custom_metadata")
private HashMap<String, String> customMetadata;
@JsonProperty("delete_version_after")
private String deleteVersionAfter;
/**
* @return Time of secret creation as raw string representation.
*/
public String getCreatedTimeString() {
return createdTimeString;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
return createdTime;
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
@ -92,10 +101,25 @@ public final class SecretMetadata implements Serializable {
}
/**
* @return Time of secret update.
* @return Time of secret update as raw string representation.
*/
public String getUpdatedTimeString() {
return updatedTime;
}
/**
* @return Time of secret update..
*/
public ZonedDateTime getUpdatedTime() {
return updatedTime;
if (updatedTime != null && !updatedTime.isEmpty()) {
try {
return ZonedDateTime.parse(updatedTime, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
@ -105,30 +129,6 @@ public final class SecretMetadata implements Serializable {
return versions;
}
/**
* @return CAS required?
* @since 1.3
*/
public Boolean isCasRequired() {
return casRequired;
}
/**
* @return Custom metadata.
* @since 1.3
*/
public Map<String, String> getCustomMetadata() {
return customMetadata;
}
/**
* @return time duration to delete version
* @since 1.3
*/
public String getDeleteVersionAfter() {
return deleteVersionAfter;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -137,20 +137,16 @@ public final class SecretMetadata implements Serializable {
return false;
}
SecretMetadata that = (SecretMetadata) o;
return Objects.equals(createdTime, that.createdTime) &&
return Objects.equals(createdTimeString, that.createdTimeString) &&
Objects.equals(currentVersion, that.currentVersion) &&
Objects.equals(maxVersions, that.maxVersions) &&
Objects.equals(oldestVersion, that.oldestVersion) &&
Objects.equals(updatedTime, that.updatedTime) &&
Objects.equals(versions, that.versions) &&
Objects.equals(casRequired, that.casRequired) &&
Objects.equals(customMetadata, that.customMetadata) &&
Objects.equals(deleteVersionAfter, that.deleteVersionAfter);
Objects.equals(versions, that.versions);
}
@Override
public int hashCode() {
return Objects.hash(createdTime, currentVersion, maxVersions, oldestVersion, updatedTime, versions, casRequired,
customMetadata, deleteVersionAfter);
return Objects.hash(createdTimeString, currentVersion, maxVersions, oldestVersion, updatedTime, versions);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,7 +34,7 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenData implements Serializable {
private static final long serialVersionUID = -5749716740973138916L;
private static final long serialVersionUID = 2915180734313753649L;
@JsonProperty("accessor")
private String accessor;
@ -52,7 +52,7 @@ public final class TokenData implements Serializable {
private String entityId;
@JsonProperty("expire_time")
private ZonedDateTime expireTime;
private String expireTime;
@JsonProperty("explicit_max_ttl")
private Integer explicitMaxTtl;
@ -61,7 +61,7 @@ public final class TokenData implements Serializable {
private String id;
@JsonProperty("issue_time")
private ZonedDateTime issueTime;
private String issueTime;
@JsonProperty("meta")
private Map<String, Object> meta;
@ -123,12 +123,24 @@ public final class TokenData implements Serializable {
return entityId;
}
/**
* @return Expire time as raw string value
* @since 0.9
*/
public String getExpireTimeString() {
return expireTime;
}
/**
* @return Expire time (parsed)
* @since 0.9
*/
public ZonedDateTime getExpireTime() {
return expireTime;
if (expireTime == null) {
return null;
} else {
return ZonedDateTime.parse(expireTime);
}
}
/**
@ -146,12 +158,24 @@ public final class TokenData implements Serializable {
return id;
}
/**
* @return Issue time as raw string value
* @since 0.9
*/
public String getIssueTimeString() {
return issueTime;
}
/**
* @return Expire time (parsed)
* @since 0.9
*/
public ZonedDateTime getIssueTime() {
return issueTime;
if (issueTime == null) {
return null;
} else {
return ZonedDateTime.parse(issueTime);
}
}
/**

@ -1,77 +0,0 @@
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);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Objects;
/**
@ -34,13 +34,15 @@ import java.util.Objects;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class VersionMetadata implements Serializable {
private static final long serialVersionUID = 8495687554714216478L;
private static final long serialVersionUID = -5286693953873839611L;
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
@JsonProperty("created_time")
private ZonedDateTime createdTime;
private String createdTimeString;
@JsonProperty("deletion_time")
private ZonedDateTime deletionTime;
private String deletionTimeString;
@JsonProperty("destroyed")
private boolean destroyed;
@ -48,21 +50,48 @@ public final class VersionMetadata implements Serializable {
@JsonProperty("version")
private Integer version;
@JsonProperty("custom_metadata")
private HashMap<String, String> customMetadata;
/**
* @return Time of secret creation as raw string representation.
*/
public String getCreatedTimeString() {
return createdTimeString;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
return createdTime;
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
* @return Time for secret deletion as raw string representation.
*/
public String getDeletionTimeString() {
return deletionTimeString;
}
/**
* @return Time for secret deletion.
*/
public ZonedDateTime getDeletionTime() {
return deletionTime;
if (deletionTimeString != null && !deletionTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(deletionTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
@ -79,14 +108,6 @@ public final class VersionMetadata implements Serializable {
return version;
}
/**
* @return Custom metadata.
* @since 1.3
*/
public Map<String, String> getCustomMetadata() {
return customMetadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
@ -96,14 +117,13 @@ public final class VersionMetadata implements Serializable {
}
VersionMetadata that = (VersionMetadata) o;
return destroyed == that.destroyed &&
Objects.equals(createdTime, that.createdTime) &&
Objects.equals(deletionTime, that.deletionTime) &&
Objects.equals(version, that.version) &&
Objects.equals(customMetadata, that.customMetadata);
Objects.equals(createdTimeString, that.createdTimeString) &&
Objects.equals(deletionTimeString, that.deletionTimeString) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(createdTime, deletionTime, destroyed, version, customMetadata);
return Objects.hash(createdTimeString, deletionTimeString, destroyed, version);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,7 +19,6 @@ 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;
/**
@ -29,7 +28,7 @@ import java.util.Objects;
* @since 1.1
*/
public class WrapInfo implements Serializable {
private static final long serialVersionUID = 4864973237090355607L;
private static final long serialVersionUID = -7764500642913116581L;
@JsonProperty("token")
private String token;
@ -38,7 +37,7 @@ public class WrapInfo implements Serializable {
private Integer ttl;
@JsonProperty("creation_time")
private ZonedDateTime creationTime;
private String creationTime;
@JsonProperty("creation_path")
private String creationPath;
@ -60,7 +59,7 @@ public class WrapInfo implements Serializable {
/**
* @return Creation time
*/
public ZonedDateTime getCreationTime() {
public String getCreationTime() {
return creationTime;
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,8 +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.annotation;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jsr310;
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,10 +25,7 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.concurrent.atomic.AtomicReference;
import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;
import static org.junit.jupiter.api.Assertions.*;
@ -41,8 +38,6 @@ import static org.junit.jupiter.api.Assertions.*;
*/
class HTTPVaultConnectorBuilderTest {
private static final String VAULT_ADDR = "https://localhost:8201";
private static final String VAULT_ADDR_2 = "http://localhost";
private static final String VAULT_ADDR_3 = "https://localhost/vault/";
private static final Integer VAULT_MAX_RETRIES = 13;
private static final String VAULT_TOKEN = "00001111-2222-3333-4444-555566667777";
@ -117,22 +112,6 @@ class HTTPVaultConnectorBuilderTest {
return null;
});
withVaultEnv(VAULT_ADDR_2, null, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Factory creation from minimal environment failed"
);
assertEquals(VAULT_ADDR_2 + "/v1/", getRequestHelperPrivate(builder.build(), "baseURL"), "URL without port not set correctly");
return null;
});
withVaultEnv(VAULT_ADDR_3, null, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Factory creation from minimal environment failed"
);
assertEquals(VAULT_ADDR_3, getRequestHelperPrivate(builder.build(), "baseURL"), "URL with custom path not set correctly");
return null;
});
// Provide address and number of retries.
withVaultEnv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
@ -149,6 +128,20 @@ class HTTPVaultConnectorBuilderTest {
return null;
});
// Provide CA certificate.
String VAULT_CACERT = tempDir.toString() + "/doesnotexist";
withVaultEnv(VAULT_ADDR, VAULT_CACERT, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
TlsException e = assertThrows(
TlsException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Creation with unknown cert path failed"
);
assertTrue(e.getCause() instanceof NoSuchFileException);
assertEquals(VAULT_CACERT, ((NoSuchFileException) e.getCause()).getFile());
return null;
});
// Automatic authentication.
withVaultEnv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
@ -172,64 +165,11 @@ class HTTPVaultConnectorBuilderTest {
});
}
/**
* Test CA certificate handling from environment variables
*/
@Test
void testCertificateFromEnv() throws Exception {
// From direct PEM content
String pem = Files.readString(Paths.get(getClass().getResource("/tls/ca.pem").toURI()));
AtomicReference<Object> certFromPem = new AtomicReference<>();
withVaultEnv(VAULT_ADDR, pem, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Builder with PEM certificate from environment failed"
);
HTTPVaultConnector connector = builder.build();
certFromPem.set(getRequestHelperPrivate(connector, "trustedCaCert"));
assertNotNull(certFromPem.get(), "Trusted CA cert from PEM not set");
return null;
});
// From file path
String file = Paths.get(getClass().getResource("/tls/ca.pem").toURI()).toString();
AtomicReference<Object> certFromFile = new AtomicReference<>();
withVaultEnv(VAULT_ADDR, file, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Builder with certificate path from environment failed"
);
HTTPVaultConnector connector = builder.build();
certFromFile.set(getRequestHelperPrivate(connector, "trustedCaCert"));
assertNotNull(certFromFile.get(), "Trusted CA cert from file not set");
return null;
});
assertEquals(certFromPem.get(), certFromFile.get(), "Certificates from PEM and file should be equal");
// Non-existing path CA certificate path
String doesNotExist = tempDir.toString() + "/doesnotexist";
withVaultEnv(VAULT_ADDR, doesNotExist, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
TlsException e = assertThrows(
TlsException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Creation with unknown cert path failed"
);
assertEquals(doesNotExist, assertInstanceOf(NoSuchFileException.class, e.getCause()).getFile());
return null;
});
}
private SystemLambda.WithEnvironmentVariables withVaultEnv(String vaultAddr, String vaultCacert, String vaultMaxRetries, String vaultToken) {
return withEnvironmentVariable("VAULT_ADDR", vaultAddr)
.and("VAULT_CACERT", vaultCacert)
.and("VAULT_MAX_RETRIES", vaultMaxRetries)
.and("VAULT_TOKEN", vaultToken);
private SystemLambda.WithEnvironmentVariables withVaultEnv(String vault_addr, String vault_cacert, String vault_max_retries, String vault_token) {
return withEnvironmentVariable("VAULT_ADDR", vault_addr)
.and("VAULT_CACERT", vault_cacert)
.and("VAULT_MAX_RETRIES", vault_max_retries)
.and("VAULT_TOKEN", vault_token);
}
private Object getRequestHelperPrivate(HTTPVaultConnector connector, String fieldName) throws NoSuchFieldException, IllegalAccessException {

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,16 +30,13 @@ import org.junit.jupiter.api.io.TempDir;
import java.io.*;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Collections.singletonMap;
import static org.apache.commons.io.FileUtils.copyDirectory;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
@ -52,7 +49,7 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
* @since 0.1
*/
class HTTPVaultConnectorIT {
private static String VAULT_VERSION = "1.19.0"; // The vault version this test is supposed to run against.
private static String VAULT_VERSION = "1.10.1"; // 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";
@ -83,6 +80,11 @@ class HTTPVaultConnectorIT {
// Initialize Vault.
VaultConfiguration config = initializeVault(tempDir, isTls);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Initialize connector.
HTTPVaultConnectorBuilder builder = HTTPVaultConnector.builder()
@ -126,11 +128,13 @@ class HTTPVaultConnectorIT {
@Test
@Order(10)
@DisplayName("Read secrets")
@SuppressWarnings("deprecation")
void readSecretTest() {
authUser();
assumeTrue(connector.isAuthorized());
// Try to read path user has no permission to read.
SecretResponse res = null;
final String invalidPath = "secret/invalid/path";
VaultConnectorException e = assertThrows(
@ -146,7 +150,7 @@ class HTTPVaultConnectorIT {
assertFalse(Pattern.compile("[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}").matcher(stackTrace(e)).find());
// Try to read accessible path with known value.
SecretResponse res = assertDoesNotThrow(
res = assertDoesNotThrow(
() -> connector.read(SECRET_PATH + "/" + SECRET_KEY),
"Valid secret path could not be read"
);
@ -211,6 +215,7 @@ class HTTPVaultConnectorIT {
@Test
@Order(30)
@DisplayName("Write secrets")
@SuppressWarnings("deprecation")
void writeSecretTest() {
authUser();
assumeTrue(connector.isAuthorized());
@ -544,6 +549,73 @@ class HTTPVaultConnectorIT {
}
}
@Nested
@DisplayName("App-ID Tests")
class AppIdTests {
private static final String APP_ID = "152AEA38-85FB-47A8-9CBD-612D645BFACA";
private static final String USER_ID = "5ADF8218-D7FB-4089-9E38-287465DBF37E";
/**
* App-ID authentication roundtrip.
*/
@Test
@Order(10)
@DisplayName("Authenticate with App-ID")
@SuppressWarnings("deprecation")
void authAppIdTest() {
// Try unauthorized access first.
assumeFalse(connector.isAuthorized());
assertThrows(
AuthorizationRequiredException.class,
() -> connector.registerAppId("", "", ""),
"Expected exception not thrown"
);
assertThrows(
AuthorizationRequiredException.class,
() -> connector.registerUserId("", ""),
"Expected exception not thrown"
);
}
/**
* App-ID authentication roundtrip.
*/
@Test
@Order(20)
@DisplayName("Register App-ID")
@SuppressWarnings("deprecation")
void registerAppIdTest() {
// Authorize.
authRoot();
assumeTrue(connector.isAuthorized());
// Register App-ID.
boolean res = assertDoesNotThrow(
() -> connector.registerAppId(APP_ID, "user", "App Name"),
"Failed to register App-ID"
);
assertTrue(res, "Failed to register App-ID");
// Register User-ID.
res = assertDoesNotThrow(
() -> connector.registerUserId(APP_ID, USER_ID),
"Failed to register App-ID"
);
assertTrue(res, "Failed to register App-ID");
connector.resetAuth();
assumeFalse(connector.isAuthorized());
// Authenticate with created credentials.
AuthResponse resp = assertDoesNotThrow(
() -> connector.authAppId(APP_ID, USER_ID),
"Failed to authenticate using App-ID"
);
assertTrue(connector.isAuthorized(), "Authorization flag not set after App-ID login");
}
}
@Nested
@DisplayName("AppRole Tests")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@ -835,9 +907,8 @@ class HTTPVaultConnectorIT {
assertFalse(res.getAuth().isRenewable(), "Root token should not be renewable");
assertFalse(res.getAuth().isOrphan(), "Root token should not be orphan");
// Starting with Vault 1.0 a warning "custom ID uses weaker SHA1..." is given.
// Starting with Vault 1.11 a second warning "Endpoint ignored unrecognized parameters" is given.
assertFalse(res.getWarnings().isEmpty(), "Token creation did not return expected warning");
// Starting with Vault 1.0 a warning "custom ID uses weaker SHA1.." is given.
assertEquals(1, res.getWarnings().size(), "Token creation did not return expected warning");
// Create token with attributes.
Token token2 = Token.builder()
@ -989,75 +1060,6 @@ class HTTPVaultConnectorIT {
}
}
@Nested
@DisplayName("Transit Tests")
class TransitTests {
@Test
@DisplayName("Transit encryption")
void transitEncryptTest() {
assertDoesNotThrow(() -> connector.authToken(TOKEN_ROOT));
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitEncrypt("my-key", "dGVzdCBtZQ=="),
"Failed to encrypt via transit"
);
assertNotNull(transitResponse.getCiphertext());
assertTrue(transitResponse.getCiphertext().startsWith("vault:v1:"));
transitResponse = assertDoesNotThrow(
() -> connector.transitEncrypt("my-key", "test me".getBytes(UTF_8)),
"Failed to encrypt binary data via transit"
);
assertNotNull(transitResponse.getCiphertext());
assertTrue(transitResponse.getCiphertext().startsWith("vault:v1:"));
}
@Test
@DisplayName("Transit decryption")
void transitDecryptTest() {
assertDoesNotThrow(() -> connector.authToken(TOKEN_ROOT));
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitDecrypt("my-key", "vault:v1:1mhLVkBAR2nrFtIkJF/qg57DWfRj0FWgR6tvkGO8XOnL6sw="),
"Failed to decrypt via transit"
);
assertEquals("dGVzdCBtZQ==", transitResponse.getPlaintext());
}
@Test
@DisplayName("Transit hash")
void transitHashText() {
assertDoesNotThrow(() -> connector.authToken(TOKEN_ROOT));
assumeTrue(connector.isAuthorized());
TransitResponse transitResponse = assertDoesNotThrow(
() -> connector.transitHash("sha2-512", "dGVzdCBtZQ=="),
"Failed to hash via transit"
);
assertEquals("7677af0ee4effaa9f35e9b1e82d182f79516ab8321786baa23002de7c06851059492dd37d5fc3791f17d81d4b58198d24a6fd8bbd62c42c1c30b371da500f193", transitResponse.getSum());
TransitResponse transitResponseBase64 = assertDoesNotThrow(
() -> connector.transitHash("sha2-256", "dGVzdCBtZQ==", "base64"),
"Failed to hash via transit with base64 output"
);
assertEquals("5DfYkW7cvGLkfy36cXhqmZcygEy9HpnFNB4WWXKOl1M=", transitResponseBase64.getSum());
transitResponseBase64 = assertDoesNotThrow(
() -> connector.transitHash("sha2-256", "test me".getBytes(UTF_8), "base64"),
"Failed to hash binary data via transit"
);
assertEquals("5DfYkW7cvGLkfy36cXhqmZcygEy9HpnFNB4WWXKOl1M=", transitResponseBase64.getSum());
}
}
@Nested
@DisplayName("Misc Tests")
class MiscTests {
@ -1075,9 +1077,8 @@ class HTTPVaultConnectorIT {
() -> connector.getAuthBackends(),
"Could not list supported auth backends"
);
assertEquals(3, supportedBackends.size());
assertTrue(supportedBackends.containsAll(List.of(AuthBackend.TOKEN, AuthBackend.USERPASS, AuthBackend.APPROLE)));
assertEquals(4, supportedBackends.size());
assertTrue(supportedBackends.containsAll(List.of(AuthBackend.TOKEN, AuthBackend.USERPASS, AuthBackend.APPID, AuthBackend.APPROLE)));
}
/**
@ -1220,39 +1221,30 @@ class HTTPVaultConnectorIT {
}
// Write configuration file.
File configFile = new File(dir, "vault.conf");
BufferedWriter bw = null;
File configFile;
try {
Files.write(configFile.toPath(), config.toString().getBytes(UTF_8));
configFile = new File(dir, "vault.conf");
bw = new BufferedWriter(new FileWriter(configFile));
bw.write(config.toString());
} catch (IOException e) {
throw new IllegalStateException("Unable to generate config file", e);
} finally {
try {
if (bw != null)
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Start vault process.
try {
vaultProcess = new ProcessBuilder("vault", "server", "-config", configFile.toString())
.directory(dir)
.start();
vaultProcess = Runtime.getRuntime().exec("vault server -config " + configFile.toString());
} catch (IOException e) {
throw new IllegalStateException("Unable to start vault. Make sure vault binary is in your executable path", e);
}
await().atMost(5, TimeUnit.SECONDS).until(() -> {
try (InputStream stdout = vaultProcess.getInputStream();
InputStreamReader reader = new InputStreamReader(stdout);
BufferedReader br = new BufferedReader(reader)) {
String line = br.readLine();
while (line != null) {
if (line.contains("Vault server started")) {
return true;
} else {
line = br.readLine();
}
}
return false;
}
});
return config;
}
@ -1277,14 +1269,28 @@ class HTTPVaultConnectorIT {
* @return port number
*/
private static Integer getFreePort() {
try (ServerSocket socket = new ServerSocket(0)) {
ServerSocket socket = null;
try {
socket = new ServerSocket(0);
socket.setReuseAddress(true);
return socket.getLocalPort();
int port = socket.getLocalPort();
try {
socket.close();
} catch (IOException e) {
// Ignore IOException on close()
}
return port;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
throw new IllegalStateException("Unable to find a free TCP port");
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,13 @@
package de.stklcode.jvault.connector;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import de.stklcode.jvault.connector.exception.ConnectionException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.exception.PermissionDeniedException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.function.Executable;
import java.io.IOException;
@ -36,7 +36,9 @@ import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
import static org.junit.jupiter.api.Assertions.*;
/**
@ -46,15 +48,18 @@ import static org.junit.jupiter.api.Assertions.*;
* @author Stefan Kalscheuer
* @since 0.7.0
*/
@WireMockTest
class HTTPVaultConnectorTest {
@RegisterExtension
static WireMockExtension wireMock = WireMockExtension.newInstance()
.options(wireMockConfig().dynamicPort())
.build();
/**
* Test exceptions thrown during request.
*/
@Test
void requestExceptionTest(WireMockRuntimeInfo wireMock) throws IOException, URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build();
void requestExceptionTest() throws IOException, URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.url("/")).withTimeout(250).build();
// Test invalid response code.
final int responseCode = 400;
@ -86,12 +91,12 @@ class HTTPVaultConnectorTest {
"Querying health status succeeded on invalid instance"
);
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
assertInstanceOf(IOException.class, e.getCause(), "Unexpected cause");
assertTrue(e.getCause() instanceof IOException, "Unexpected cause");
// Now simulate a failing request that succeeds on second try.
connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withNumberOfRetries(1).withTimeout(250).build();
connector = HTTPVaultConnector.builder(wireMock.url("/")).withNumberOfRetries(1).withTimeout(250).build();
stubFor(
wireMock.stubFor(
WireMock.any(anyUrl())
.willReturn(aResponse().withStatus(500))
.willReturn(aResponse().withStatus(500))
@ -188,8 +193,8 @@ class HTTPVaultConnectorTest {
* Test behavior on unparsable responses.
*/
@Test
void parseExceptionTest(WireMockRuntimeInfo wireMock) throws URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build();
void parseExceptionTest() throws URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.url("/")).withTimeout(250).build();
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
@ -222,14 +227,26 @@ class HTTPVaultConnectorTest {
* Test requests that expect an empty response with code 204, but receive a 200 body.
*/
@Test
void nonEmpty204ResponseTest(WireMockRuntimeInfo wireMock) throws URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build();
void nonEmpty204ResponseTest() throws URISyntaxException {
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.url("/")).withTimeout(250).build();
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockHttpResponse(200, "{}", "application/json");
// Now test the methods expecting a 204.
assertThrows(
InvalidResponseException.class,
() -> connector.registerAppId("appID", "policy", "displayName"),
"registerAppId() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.registerUserId("appID", "userID"),
"registerUserId() with 200 response succeeded"
);
assertThrows(
InvalidResponseException.class,
() -> connector.createAppRole("appID", Collections.singletonList("policy")),
@ -283,7 +300,7 @@ class HTTPVaultConnectorTest {
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getDeclaredField(fieldName);
if (field.canAccess(target)) {
if (field.isAccessible()) {
return field.get(target);
}
field.setAccessible(true);
@ -295,7 +312,7 @@ class HTTPVaultConnectorTest {
private void setPrivate(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
boolean accessible = field.canAccess(target);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(target, value);
field.setAccessible(accessible);
@ -305,7 +322,7 @@ class HTTPVaultConnectorTest {
}
private void mockHttpResponse(int status, String body, String contentType) {
stubFor(
wireMock.stubFor(
WireMock.any(anyUrl()).willReturn(
aResponse().withStatus(status).withBody(body).withHeader("Content-Type", contentType)
)

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,10 +1,5 @@
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.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
@ -21,7 +16,6 @@ import static org.junit.jupiter.api.Assertions.fail;
*/
public abstract class AbstractModelTest<T> {
protected final Class<?> modelClass;
protected final ObjectMapper objectMapper;
/**
* Test case constructor.
@ -30,11 +24,6 @@ public abstract class AbstractModelTest<T> {
*/
protected AbstractModelTest(Class<T> modelClass) {
this.modelClass = modelClass;
this.objectMapper = JsonMapper.builder()
.addModule(new JavaTimeModule())
.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
.build();
}
/**

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Field;
@ -115,14 +116,16 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
*/
@Test
void jsonTest() throws NoSuchFieldException, IllegalAccessException {
// A simple roundtrip first. All set fields should be present afterward.
ObjectMapper mapper = new ObjectMapper();
// A simple roundtrip first. All set fields should be present afterwards..
AppRoleSecret secret = new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
String secretJson = assertDoesNotThrow(() -> objectMapper.writeValueAsString(secret), "Serialization failed");
String secretJson = assertDoesNotThrow(() -> mapper.writeValueAsString(secret), "Serialization failed");
// CIDR list is comma-separated when used as input, but List otherwise, hence convert string to list.
String secretJson2 = commaSeparatedToList(secretJson);
AppRoleSecret secret2 = assertDoesNotThrow(
() -> objectMapper.readValue(secretJson2, AppRoleSecret.class),
() -> mapper.readValue(secretJson2, AppRoleSecret.class),
"Deserialization failed"
);
assertEquals(secret2.getId(), secret.getId());
@ -142,9 +145,9 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
assumeTrue(secret.getNumUses() == 678);
setPrivateField(secret, "ttl", 12345);
assumeTrue(secret.getTtl() == 12345);
String secretJson3 = assertDoesNotThrow(() -> objectMapper.writeValueAsString(secret), "Serialization failed");
String secretJson3 = assertDoesNotThrow(() -> mapper.writeValueAsString(secret), "Serialization failed");
secret2 = assertDoesNotThrow(
() -> objectMapper.readValue(commaSeparatedToList(secretJson3), AppRoleSecret.class),
() -> mapper.readValue(commaSeparatedToList(secretJson3), AppRoleSecret.class),
"Deserialization failed"
);
assertEquals(secret2.getId(), secret.getId());
@ -162,7 +165,7 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
"\"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(() -> mapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed");
assertEquals("TEST_ACCESSOR", secret2.getAccessor());
assertEquals("TEST_CREATION", secret2.getCreationTime());
assertEquals("TEST_EXPIRATION", secret2.getExpirationTime());
@ -173,14 +176,14 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
private static void setPrivateField(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = object.getClass().getDeclaredField(fieldName);
boolean accessible = field.canAccess(object);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(object, value);
field.setAccessible(accessible);
}
private static String commaSeparatedToList(String json) {
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]")
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":\\[$1\\]")
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -43,7 +44,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
private static final String POLICY_2 = "policy2";
private static final Integer SECRET_ID_NUM_USES = 10;
private static final Integer SECRET_ID_TTL = 7200;
private static final Boolean LOCAL_SECRET_IDS = false;
private static final Boolean ENABLE_LOCAL_SECRET_IDS = false;
private static final Integer TOKEN_TTL = 4800;
private static final Integer TOKEN_MAX_TTL = 9600;
private static final Integer TOKEN_EXPLICIT_MAX_TTL = 14400;
@ -52,8 +53,8 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
private static final Integer TOKEN_PERIOD = 1234;
private static final Token.Type TOKEN_TYPE = Token.Type.DEFAULT_SERVICE;
private static final String JSON_MIN = "{\"role_name\":\"" + NAME + "\"}";
private static final String JSON_FULL = String.format("{\"role_name\":\"%s\",\"role_id\":\"%s\",\"bind_secret_id\":%s,\"secret_id_bound_cidrs\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"local_secret_ids\":%s,\"token_ttl\":%d,\"token_max_ttl\":%d,\"token_policies\":\"%s\",\"token_bound_cidrs\":\"%s\",\"token_explicit_max_ttl\":%d,\"token_no_default_policy\":%s,\"token_num_uses\":%d,\"token_period\":%d,\"token_type\":\"%s\"}",
NAME, ID, BIND_SECRET_ID, CIDR_1, SECRET_ID_NUM_USES, SECRET_ID_TTL, LOCAL_SECRET_IDS, TOKEN_TTL, TOKEN_MAX_TTL, POLICY, CIDR_1, TOKEN_EXPLICIT_MAX_TTL, TOKEN_NO_DEFAULT_POLICY, TOKEN_NUM_USES, TOKEN_PERIOD, TOKEN_TYPE.value());
private static final String JSON_FULL = String.format("{\"role_name\":\"%s\",\"role_id\":\"%s\",\"bind_secret_id\":%s,\"secret_id_bound_cidrs\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"enable_local_secret_ids\":%s,\"token_ttl\":%d,\"token_max_ttl\":%d,\"token_policies\":\"%s\",\"token_bound_cidrs\":\"%s\",\"token_explicit_max_ttl\":%d,\"token_no_default_policy\":%s,\"token_num_uses\":%d,\"token_period\":%d,\"token_type\":\"%s\"}",
NAME, ID, BIND_SECRET_ID, CIDR_1, SECRET_ID_NUM_USES, SECRET_ID_TTL, ENABLE_LOCAL_SECRET_IDS, TOKEN_TTL, TOKEN_MAX_TTL, POLICY, CIDR_1, TOKEN_EXPLICIT_MAX_TTL, TOKEN_NO_DEFAULT_POLICY, TOKEN_NUM_USES, TOKEN_PERIOD, TOKEN_TYPE.value());
AppRoleTest() {
super(AppRole.class);
@ -68,7 +69,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
.withTokenPolicies(POLICIES)
.withSecretIdNumUses(SECRET_ID_NUM_USES)
.withSecretIdTtl(SECRET_ID_TTL)
.withLocalSecretIds(LOCAL_SECRET_IDS)
.withEnableLocalSecretIds(ENABLE_LOCAL_SECRET_IDS)
.withTokenTtl(TOKEN_TTL)
.withTokenMaxTtl(TOKEN_MAX_TTL)
.withTokenBoundCidrs(BOUND_CIDR_LIST)
@ -98,7 +99,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
assertNull(role.getTokenPolicies());
assertNull(role.getSecretIdNumUses());
assertNull(role.getSecretIdTtl());
assertNull(role.getLocalSecretIds());
assertNull(role.getEnableLocalSecretIds());
assertNull(role.getTokenTtl());
assertNull(role.getTokenMaxTtl());
assertNull(role.getTokenBoundCidrs());
@ -109,7 +110,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
assertNull(role.getTokenType());
// Optional fields should be ignored, so JSON string should only contain role_name.
assertEquals(JSON_MIN, objectMapper.writeValueAsString(role));
assertEquals(JSON_MIN, new ObjectMapper().writeValueAsString(role));
}
/**
@ -125,7 +126,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
assertEquals(POLICIES, role.getTokenPolicies());
assertEquals(SECRET_ID_NUM_USES, role.getSecretIdNumUses());
assertEquals(SECRET_ID_TTL, role.getSecretIdTtl());
assertEquals(LOCAL_SECRET_IDS, role.getLocalSecretIds());
assertEquals(ENABLE_LOCAL_SECRET_IDS, role.getEnableLocalSecretIds());
assertEquals(TOKEN_TTL, role.getTokenTtl());
assertEquals(TOKEN_MAX_TTL, role.getTokenMaxTtl());
assertEquals(BOUND_CIDR_LIST, role.getTokenBoundCidrs());
@ -136,7 +137,7 @@ class AppRoleTest extends AbstractModelTest<AppRole> {
assertEquals(TOKEN_TYPE.value(), role.getTokenType());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(role));
assertEquals(JSON_FULL, new ObjectMapper().writeValueAsString(role));
}
/**

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,6 +35,7 @@ class AuthBackendTest {
@Test
void forTypeTest() {
assertEquals(AuthBackend.TOKEN, AuthBackend.forType("token"));
assertEquals(AuthBackend.APPID, AuthBackend.forType("app-id"));
assertEquals(AuthBackend.USERPASS, AuthBackend.forType("userpass"));
assertEquals(AuthBackend.GITHUB, AuthBackend.forType("github"));
assertEquals(AuthBackend.UNKNOWN, AuthBackend.forType(""));

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
@ -132,7 +133,7 @@ class TokenRoleTest extends AbstractModelTest<TokenRole> {
assertNull(role.getTokenType());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(role));
assertEquals("{}", new ObjectMapper().writeValueAsString(role));
}
/**
@ -173,10 +174,10 @@ class TokenRoleTest extends AbstractModelTest<TokenRole> {
assertNull(role.getTokenType());
// Empty builder should be equal to no-arg construction.
assertEquals(new TokenRole(), role);
assertEquals(role, new TokenRole());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(role));
assertEquals("{}", new ObjectMapper().writeValueAsString(role));
}
/**
@ -207,6 +208,6 @@ class TokenRoleTest extends AbstractModelTest<TokenRole> {
assertEquals(TOKEN_TYPE.value(), role.getTokenType());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(role));
assertEquals(JSON_FULL, new ObjectMapper().writeValueAsString(role));
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
@ -102,10 +103,10 @@ class TokenTest extends AbstractModelTest<Token> {
assertNull(token.getEntityAlias());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(token));
assertEquals("{}", new ObjectMapper().writeValueAsString(token));
// Empty builder should be equal to no-arg construction.
assertEquals(new Token(), token);
assertEquals(token, new Token());
}
/**
@ -128,7 +129,7 @@ class TokenTest extends AbstractModelTest<Token> {
assertEquals(PERIOD, token.getPeriod());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(token));
assertEquals(JSON_FULL, new ObjectMapper().writeValueAsString(token));
}
/**

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.AppRole;
import org.junit.jupiter.api.Test;
@ -68,7 +69,7 @@ class AppRoleResponseTest extends AbstractModelTest<AppRoleResponse> {
@Override
protected AppRoleResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, AppRoleResponse.class);
return new ObjectMapper().readValue(RES_JSON, AppRoleResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -91,7 +92,7 @@ class AppRoleResponseTest extends AbstractModelTest<AppRoleResponse> {
@Test
void jsonRoundtrip() {
AppRoleResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AppRoleResponse.class),
() -> new ObjectMapper().readValue(RES_JSON, AppRoleResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
@ -46,10 +47,7 @@ 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\": {" +
@ -65,15 +63,9 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
" \"" + TK_PATH + "\": {\n" +
" \"config\": {\n" +
" \"default_lease_ttl\": " + TK_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" +
" \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + "\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" +
@ -91,7 +83,7 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
@Override
protected AuthMethodsResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, AuthMethodsResponse.class);
return new ObjectMapper().readValue(RES_JSON, AuthMethodsResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -114,7 +106,7 @@ class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
@Test
void jsonRoundtrip() {
AuthMethodsResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AuthMethodsResponse.class),
() -> new ObjectMapper().readValue(RES_JSON, AuthMethodsResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
@ -146,16 +138,15 @@ 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(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");
assertEquals(
Map.of(
"default_lease_ttl", TK_LEASE_TTL.toString(),
"max_lease_ttl", TK_MAX_LEASE_TTL.toString()
),
method.getConfig(),
"Unexpected config for Token"
);
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,12 +17,9 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
@ -48,13 +45,6 @@ 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 Integer AUTH_NUM_USES = 42;
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" +
@ -75,23 +65,7 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
" \"renewable\": " + AUTH_RENEWABLE + ",\n" +
" \"entity_id\": \"" + AUTH_ENTITY_ID + "\",\n" +
" \"token_type\": \"" + AUTH_TOKEN_TYPE + "\",\n" +
" \"orphan\": " + AUTH_ORPHAN + ",\n" +
" \"num_uses\": " + AUTH_NUM_USES + ",\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" +
" \"orphan\": " + AUTH_ORPHAN + "\n" +
" }\n" +
"}";
@ -102,27 +76,20 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
@Override
protected AuthResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, AuthResponse.class);
return new ObjectMapper().readValue(RES_JSON, AuthResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
@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).
*/
@Test
void jsonRoundtrip() {
AuthResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AuthResponse.class),
() -> new ObjectMapper().readValue(RES_JSON, AuthResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
@ -136,20 +103,10 @@ class AuthResponseTest extends AbstractModelTest<AuthResponse> {
assertEquals(AUTH_ORPHAN, data.isOrphan(), "Incorrect auth orphan flag");
assertEquals(AUTH_TOKEN_TYPE, data.getTokenType(), "Incorrect auth token type");
assertEquals(AUTH_ENTITY_ID, data.getEntityId(), "Incorrect auth entity id");
assertEquals(AUTH_NUM_USES, data.getNumUses(), "Incorrect auth num uses");
assertEquals(2, data.getPolicies().size(), "Incorrect number of policies");
assertTrue(data.getPolicies().containsAll(Set.of(AUTH_POLICY_1, AUTH_POLICY_2)));
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");
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,8 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -50,7 +52,7 @@ class CredentialsResponseTest extends AbstractModelTest<CredentialsResponse> {
@Override
protected CredentialsResponse createFull() {
try {
return objectMapper.readValue(JSON, CredentialsResponse.class);
return new ObjectMapper().readValue(JSON, CredentialsResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -59,16 +61,18 @@ class CredentialsResponseTest extends AbstractModelTest<CredentialsResponse> {
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
void getCredentialsTest() {
void getCredentialsTest() throws InvalidResponseException {
// Create empty Object.
CredentialsResponse res = new CredentialsResponse();
assertNull(res.getUsername(), "Username not present in data map should not return anything");
assertNull(res.getPassword(), "Password not present in data map should not return anything");
res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, CredentialsResponse.class),
() -> new ObjectMapper().readValue(JSON, CredentialsResponse.class),
"Deserialization of CredentialsResponse failed"
);
assertEquals(VAL_USER, res.getUsername(), "Incorrect username");

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -43,7 +44,7 @@ class ErrorResponseTest extends AbstractModelTest<ErrorResponse> {
@Override
protected ErrorResponse createFull() {
try {
return objectMapper.readValue(JSON, ErrorResponse.class);
return new ObjectMapper().readValue(JSON, ErrorResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -55,15 +56,16 @@ class ErrorResponseTest extends AbstractModelTest<ErrorResponse> {
*/
@Test
void jsonRoundtrip() {
ObjectMapper om = new ObjectMapper();
ErrorResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, ErrorResponse.class),
() -> om.readValue(JSON, ErrorResponse.class),
"ErrorResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(List.of(ERROR_1, ERROR_2), res.getErrors(), "Unexpected error messages");
assertEquals(
JSON,
assertDoesNotThrow(() -> objectMapper.writeValueAsString(res), "ErrorResponse serialization failed"),
assertDoesNotThrow(() -> om.writeValueAsString(res), "ErrorResponse serialization failed"),
"Unexpected JSON string after serialization"
);
}
@ -72,13 +74,13 @@ class ErrorResponseTest extends AbstractModelTest<ErrorResponse> {
@Test
void testToString() {
ErrorResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, ErrorResponse.class),
() -> new ObjectMapper().readValue(JSON, ErrorResponse.class),
"ErrorResponse deserialization failed"
);
assertEquals(ERROR_1, res.toString());
res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON_EMPTY, ErrorResponse.class),
() -> new ObjectMapper().readValue(JSON_EMPTY, ErrorResponse.class),
"ErrorResponse deserialization failed with empty list"
);
assertEquals("error response", res.toString());

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -31,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.*;
class HealthResponseTest extends AbstractModelTest<HealthResponse> {
private static final String CLUSTER_ID = "c9abceea-4f46-4dab-a688-5ce55f89e228";
private static final String CLUSTER_NAME = "vault-cluster-5515c810";
private static final String VERSION = "0.17.0";
private static final String VERSION = "0.9.2";
private static final Long SERVER_TIME_UTC = 1469555798L;
private static final Boolean STANDBY = false;
private static final Boolean SEALED = false;
@ -39,10 +40,6 @@ class HealthResponseTest extends AbstractModelTest<HealthResponse> {
private static final Boolean PERF_STANDBY = false;
private static final String REPL_PERF_MODE = "disabled";
private static final String REPL_DR_MODE = "disabled";
private static final Long ECHO_DURATION = 1L;
private static final Long CLOCK_SKEW = 0L;
private static final Long REPL_PRIM_CANARY_AGE = 2L;
private static final Boolean ENTERPRISE = false;
private static final String RES_JSON = "{\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
@ -54,11 +51,7 @@ class HealthResponseTest extends AbstractModelTest<HealthResponse> {
" \"initialized\": " + INITIALIZED + ",\n" +
" \"replication_performance_mode\": \"" + REPL_PERF_MODE + "\",\n" +
" \"replication_dr_mode\": \"" + REPL_DR_MODE + "\",\n" +
" \"performance_standby\": " + PERF_STANDBY + ",\n" +
" \"echo_duration_ms\": " + ECHO_DURATION + ",\n" +
" \"clock_skew_ms\": " + CLOCK_SKEW + ",\n" +
" \"replication_primary_canary_age_ms\": " + REPL_PRIM_CANARY_AGE + ",\n" +
" \"enterprise\": " + ENTERPRISE + "\n" +
" \"performance_standby\": " + PERF_STANDBY + "\n" +
"}";
HealthResponseTest() {
@ -68,7 +61,7 @@ class HealthResponseTest extends AbstractModelTest<HealthResponse> {
@Override
protected HealthResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, HealthResponse.class);
return new ObjectMapper().readValue(RES_JSON, HealthResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -81,7 +74,7 @@ class HealthResponseTest extends AbstractModelTest<HealthResponse> {
@Test
void jsonRoundtrip() {
HealthResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, HealthResponse.class),
() -> new ObjectMapper().readValue(RES_JSON, HealthResponse.class),
"Health deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
@ -95,9 +88,5 @@ class HealthResponseTest extends AbstractModelTest<HealthResponse> {
assertEquals(PERF_STANDBY, res.isPerformanceStandby(), "Incorrect performance standby state");
assertEquals(REPL_PERF_MODE, res.getReplicationPerfMode(), "Incorrect replication perf mode");
assertEquals(REPL_DR_MODE, res.getReplicationDrMode(), "Incorrect replication DR mode");
assertEquals(ECHO_DURATION, res.getEchoDurationMs(), "Incorrect echo duration");
assertEquals(CLOCK_SKEW, res.getClockSkewMs(), "Incorrect clock skew");
assertEquals(REPL_PRIM_CANARY_AGE, res.getReplicationPrimaryCanaryAgeMs(), "Incorrect canary age");
assertEquals(ENTERPRISE, res.isEnterprise(), "Incorrect enterprise flag");
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -39,7 +40,7 @@ class HelpResponseTest extends AbstractModelTest<HelpResponse> {
@Override
protected HelpResponse createFull() {
try {
return objectMapper.readValue(JSON, HelpResponse.class);
return new ObjectMapper().readValue(JSON, HelpResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -51,15 +52,16 @@ class HelpResponseTest extends AbstractModelTest<HelpResponse> {
*/
@Test
void jsonRoundtrip() {
ObjectMapper om = new ObjectMapper();
HelpResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, HelpResponse.class),
() -> om.readValue(JSON, HelpResponse.class),
"HelpResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(HELP, res.getHelp(), "Unexpected help text");
assertEquals(
JSON,
assertDoesNotThrow(() -> objectMapper.writeValueAsString(res), "HelpResponse serialization failed"),
assertDoesNotThrow(() -> om.writeValueAsString(res), "HelpResponse serialization failed"),
"Unexpected JSON string after serialization"
);
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,11 +17,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
@ -43,9 +43,6 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
private static final String SECRET_META_CREATED = "2018-03-22T02:24:06.945319214Z";
private static final String SECRET_META_DELETED = "2018-03-23T03:25:07.056420325Z";
private static final List<String> SECRET_WARNINGS = null;
private static final String CUSTOM_META_KEY = "foo";
private static final String CUSTOM_META_VAL = "bar";
private static final String SECRET_JSON_V2 = "{\n" +
" \"request_id\": \"" + SECRET_REQUEST_ID + "\",\n" +
" \"lease_id\": \"" + SECRET_LEASE_ID + "\",\n" +
@ -58,7 +55,6 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
" },\n" +
" \"metadata\": {\n" +
" \"created_time\": \"" + SECRET_META_CREATED + "\",\n" +
" \"custom_metadata\": null,\n" +
" \"deletion_time\": \"\",\n" +
" \"destroyed\": false,\n" +
" \"version\": 1\n" +
@ -78,9 +74,6 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
" },\n" +
" \"metadata\": {\n" +
" \"created_time\": \"" + SECRET_META_CREATED + "\",\n" +
" \"custom_metadata\": {" +
" \"" + CUSTOM_META_KEY + "\": \"" + CUSTOM_META_VAL + "\"" +
" },\n" +
" \"deletion_time\": \"" + SECRET_META_DELETED + "\",\n" +
" \"destroyed\": true,\n" +
" \"version\": 2\n" +
@ -96,7 +89,7 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
@Override
protected MetaSecretResponse createFull() {
try {
return objectMapper.readValue(SECRET_JSON_V2, MetaSecretResponse.class);
return new ObjectMapper().readValue(SECRET_JSON_V2, MetaSecretResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -110,29 +103,31 @@ class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
void jsonRoundtrip() {
// KV v2 secret.
MetaSecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON_V2, MetaSecretResponse.class),
() -> new ObjectMapper().readValue(SECRET_JSON_V2, MetaSecretResponse.class),
"SecretResponse deserialization failed"
);
assertSecretData(res);
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().getDeletionTime(), "Incorrect deletion date");
assertFalse(res.getMetadata().isDestroyed(), "Secret destroyed when not expected");
assertEquals(1, res.getMetadata().getVersion(), "Incorrect secret version");
assertNull(res.getMetadata().getCustomMetadata(), "Incorrect custom metadata");
// Deleted KV v2 secret.
res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON_V2_2, MetaSecretResponse.class),
() -> new ObjectMapper().readValue(SECRET_JSON_V2_2, MetaSecretResponse.class),
"SecretResponse deserialization failed"
);
assertSecretData(res);
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(SECRET_META_DELETED, res.getMetadata().getDeletionTimeString(), "Incorrect deletion date string");
assertNotNull(res.getMetadata().getDeletionTime(), "Incorrect deletion date");
assertTrue(res.getMetadata().isDestroyed(), "Secret destroyed when not expected");
assertEquals(2, res.getMetadata().getVersion(), "Incorrect secret version");
assertEquals(Map.of(CUSTOM_META_KEY, CUSTOM_META_VAL), res.getMetadata().getCustomMetadata(), "Incorrect custom metadata");
}
private void assertSecretData(SecretResponse res) {

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,11 +17,10 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
@ -37,20 +36,11 @@ class MetadataResponseTest extends AbstractModelTest<MetadataResponse> {
private static final Integer CURRENT_VERSION = 3;
private static final Integer MAX_VERSIONS = 0;
private static final Integer OLDEST_VERSION = 1;
private static final Boolean CAS_REQUIRED = false;
private static final String CUSTOM_META_KEY = "test";
private static final String CUSTOM_META_VAL = "123";
private static final String DELETE_VERSION_AFTER = "0s";
private static final String META_JSON = "{\n" +
" \"data\": {\n" +
" \"cas_required\": " + CAS_REQUIRED + ",\n" +
" \"created_time\": \"" + V1_TIME + "\",\n" +
" \"current_version\": " + CURRENT_VERSION + ",\n" +
" \"custom_metadata\": {" +
" \"" + CUSTOM_META_KEY + "\": \"" + CUSTOM_META_VAL + "\"" +
" },\n" +
" \"delete_version_after\": \"" + DELETE_VERSION_AFTER + "\"," +
" \"max_versions\": " + MAX_VERSIONS + ",\n" +
" \"oldest_version\": " + OLDEST_VERSION + ",\n" +
" \"updated_time\": \"" + V3_TIME + "\",\n" +
@ -81,7 +71,7 @@ class MetadataResponseTest extends AbstractModelTest<MetadataResponse> {
@Override
protected MetadataResponse createFull() {
try {
return objectMapper.readValue(META_JSON, MetadataResponse.class);
return new ObjectMapper().readValue(META_JSON, MetadataResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -94,22 +84,23 @@ class MetadataResponseTest extends AbstractModelTest<MetadataResponse> {
@Test
void jsonRoundtrip() {
MetadataResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(META_JSON, MetadataResponse.class),
() -> new ObjectMapper().readValue(META_JSON, MetadataResponse.class),
"MetadataResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertNotNull(res.getMetadata(), "Parsed metadata is NULL");
assertEquals(CAS_REQUIRED, res.getMetadata().isCasRequired(), "Incorrect CAS required flag");
assertEquals(V1_TIME, res.getMetadata().getCreatedTimeString(), "Incorrect created time");
assertNotNull(res.getMetadata().getCreatedTime(), "Parting created time failed");
assertEquals(CURRENT_VERSION, res.getMetadata().getCurrentVersion(), "Incorrect current version");
assertEquals(MAX_VERSIONS, res.getMetadata().getMaxVersions(), "Incorrect max versions");
assertEquals(OLDEST_VERSION, res.getMetadata().getOldestVersion(), "Incorrect oldest version");
assertEquals(Map.of(CUSTOM_META_KEY, CUSTOM_META_VAL), res.getMetadata().getCustomMetadata(), "Incorrect custom metadata");
assertEquals(DELETE_VERSION_AFTER, res.getMetadata().getDeleteVersionAfter(), "Incorrect delete version after");
assertEquals(V3_TIME, res.getMetadata().getUpdatedTimeString(), "Incorrect updated time");
assertNotNull(res.getMetadata().getUpdatedTime(), "Parting updated time failed");
assertEquals(3, res.getMetadata().getVersions().size(), "Incorrect number of versions");
assertEquals(V2_TIME, res.getMetadata().getVersions().get(1).getDeletionTimeString(), "Incorrect version 1 delete time");
assertNotNull(res.getMetadata().getVersions().get(1).getDeletionTime(), "Parsing version delete time failed");
assertTrue(res.getMetadata().getVersions().get(1).isDestroyed(), "Incorrect version 1 destroyed state");
assertEquals(V2_TIME, res.getMetadata().getVersions().get(2).getCreatedTimeString(), "Incorrect version 2 created time");
assertNotNull(res.getMetadata().getVersions().get(2).getCreatedTime(), "Parsing version created failed");
assertFalse(res.getMetadata().getVersions().get(3).isDestroyed(), "Incorrect version 3 destroyed state");
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,13 +16,12 @@
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 com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@ -61,7 +60,7 @@ class PlainSecretResponseTest extends AbstractModelTest<PlainSecretResponse> {
@Override
protected PlainSecretResponse createFull() {
try {
return objectMapper.readValue(SECRET_JSON, PlainSecretResponse.class);
return new ObjectMapper().readValue(SECRET_JSON, PlainSecretResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -74,7 +73,7 @@ class PlainSecretResponseTest extends AbstractModelTest<PlainSecretResponse> {
@Test
void jsonRoundtrip() {
SecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON, PlainSecretResponse.class),
() -> new ObjectMapper().readValue(SECRET_JSON, PlainSecretResponse.class),
"SecretResponse deserialization failed"
);
@ -87,137 +86,4 @@ 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);
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,11 +17,10 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.time.ZonedDateTime;
import static org.junit.jupiter.api.Assertions.*;
/**
@ -36,8 +35,7 @@ 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.15.4";
private static final String BUILD_DATE = "2023-11-22T20:59:54Z";
private static final String VERSION = "1.8.2";
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";
@ -54,7 +52,6 @@ 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" +
@ -68,7 +65,6 @@ 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" +
@ -84,7 +80,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
@Override
protected SealResponse createFull() {
try {
return objectMapper.readValue(RES_UNSEALED, SealResponse.class);
return new ObjectMapper().readValue(RES_UNSEALED, SealResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -98,7 +94,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
void jsonRoundtripSealed() {
// First test sealed Vault's response.
SealResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_SEALED, SealResponse.class),
() -> new ObjectMapper().readValue(RES_SEALED, SealResponse.class),
"SealResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
@ -110,7 +106,6 @@ 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");
@ -121,7 +116,7 @@ class SealResponseTest extends AbstractModelTest<SealResponse> {
// Not test unsealed Vault's response.
res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_UNSEALED, SealResponse.class),
() -> new ObjectMapper().readValue(RES_UNSEALED, SealResponse.class),
"SealResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
@ -133,7 +128,6 @@ 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");

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -53,7 +54,7 @@ class SecretListResponseTest extends AbstractModelTest<SecretListResponse> {
@Override
protected SecretListResponse createFull() {
try {
return objectMapper.readValue(JSON, SecretListResponse.class);
return new ObjectMapper().readValue(JSON, SecretListResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -66,7 +67,7 @@ class SecretListResponseTest extends AbstractModelTest<SecretListResponse> {
@Test
void getKeysTest() {
SecretListResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, SecretListResponse.class),
() -> new ObjectMapper().readValue(JSON, SecretListResponse.class),
"SecretListResponse deserialization failed"
);

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
@ -49,7 +50,7 @@ class SecretVersionResponseTest extends AbstractModelTest<SecretVersionResponse>
@Override
protected SecretVersionResponse createFull() {
try {
return objectMapper.readValue(META_JSON, SecretVersionResponse.class);
return new ObjectMapper().readValue(META_JSON, SecretVersionResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -62,11 +63,13 @@ class SecretVersionResponseTest extends AbstractModelTest<SecretVersionResponse>
@Test
void jsonRoundtrip() {
SecretVersionResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(META_JSON, SecretVersionResponse.class),
() -> new ObjectMapper().readValue(META_JSON, SecretVersionResponse.class),
"SecretVersionResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertNotNull(res.getMetadata(), "Parsed metadata is NULL");
assertEquals(CREATION_TIME, res.getMetadata().getCreatedTimeString(), "Incorrect created time");
assertEquals(DELETION_TIME, res.getMetadata().getDeletionTimeString(), "Incorrect deletion time");
assertFalse(res.getMetadata().isDestroyed(), "Incorrect destroyed state");
assertEquals(VERSION, res.getMetadata().getVersion(), "Incorrect version");
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import org.junit.jupiter.api.Test;
@ -55,7 +56,6 @@ class TokenResponseTest extends AbstractModelTest<TokenResponse> {
private static final String TOKEN_ID = "my-token";
private static final String TOKEN_ISSUE_TIME = "2018-04-17T11:35:54.466476078-04:00";
private static final String TOKEN_TYPE = "service";
private static final String MOUNT_TYPE = "token";
private static final String RES_JSON = "{\n" +
" \"lease_id\": \"\",\n" +
@ -86,8 +86,7 @@ class TokenResponseTest extends AbstractModelTest<TokenResponse> {
" \"type\": \"" + TOKEN_TYPE + "\"\n" +
" },\n" +
" \"warnings\": null,\n" +
" \"auth\": null,\n" +
" \"mount_type\": \"" + MOUNT_TYPE + "\"\n" +
" \"auth\": null\n" +
"}";
TokenResponseTest() {
@ -97,7 +96,7 @@ class TokenResponseTest extends AbstractModelTest<TokenResponse> {
@Override
protected TokenResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, TokenResponse.class);
return new ObjectMapper().readValue(RES_JSON, TokenResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
@ -120,14 +119,13 @@ class TokenResponseTest extends AbstractModelTest<TokenResponse> {
@Test
void jsonRoundtrip() {
TokenResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, TokenResponse.class),
() -> new ObjectMapper().readValue(RES_JSON, TokenResponse.class),
"TokenResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(RES_LEASE_DURATION, res.getLeaseDuration(), "Incorrect lease duration");
assertEquals(RES_RENEWABLE, res.isRenewable(), "Incorrect response renewable flag");
assertEquals(RES_LEASE_DURATION, res.getLeaseDuration(), "Incorrect response lease duration");
assertEquals(MOUNT_TYPE, res.getMountType(), "Incorrect mount type");
// Extract token data.
TokenData data = res.getData();
assertNotNull(data, "Token data is NULL");
@ -136,9 +134,11 @@ class TokenResponseTest extends AbstractModelTest<TokenResponse> {
assertEquals(TOKEN_TTL, data.getCreationTtl(), "Incorrect token creation TTL");
assertEquals(TOKEN_DISPLAY_NAME, data.getName(), "Incorrect token display name");
assertEquals(TOKEN_ENTITY_ID, data.getEntityId(), "Incorrect token entity ID");
assertEquals(TOKEN_EXPIRE_TIME, data.getExpireTimeString(), "Incorrect token expire time");
assertEquals(ZonedDateTime.parse(TOKEN_EXPIRE_TIME), data.getExpireTime(), "Incorrect parsed token expire time");
assertEquals(TOKEN_EXPLICIT_MAX_TTL, data.getExplicitMaxTtl(), "Incorrect token explicit max TTL");
assertEquals(TOKEN_ID, data.getId(), "Incorrect token ID");
assertEquals(TOKEN_ISSUE_TIME, data.getIssueTimeString(), "Incorrect token issue time");
assertEquals(ZonedDateTime.parse(TOKEN_ISSUE_TIME), data.getIssueTime(), "Incorrect parsed token issue time");
assertEquals(Map.of(TOKEN_META_KEY, TOKEN_META_VALUE), data.getMeta(), "Incorrect token metadata");
assertEquals(TOKEN_NUM_USES, data.getNumUses(), "Incorrect token number of uses");

@ -1,137 +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.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link TransitResponse} model.
*
* @author Stefan Kalscheuer
* @since 1.5.0
*/
class TransitResponseTest extends AbstractModelTest<TransitResponse> {
private static final String CIPHERTEXT = "vault:v1:XjsPWPjqPrBi1N2Ms2s1QM798YyFWnO4TR4lsFA=";
private static final String PLAINTEXT = "dGhlIHF1aWNrIGJyb3duIGZveAo=";
private static final String SUM = "dGhlIHF1aWNrIGJyb3duIGZveAo=";
TransitResponseTest() {
super(TransitResponse.class);
}
@Override
protected TransitResponse createFull() {
try {
return objectMapper.readValue(
json(
"\"ciphertext\": \"" + CIPHERTEXT + "\", " +
"\"plaintext\": \"" + PLAINTEXT + "\", " +
"\"sum\": \"" + SUM + "\""
),
TransitResponse.class
);
} catch (JsonProcessingException e) {
fail("Creation of full model failed", e);
return null;
}
}
@Test
void encryptionTest() {
TransitResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(
json("\"ciphertext\": \"" + CIPHERTEXT + "\""),
TransitResponse.class
),
"TransitResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals("987c6daf-b0e2-4142-a970-1e61fdb249d7", res.getRequestId(), "Incorrect request id");
assertEquals("", res.getLeaseId(), "Unexpected lease id");
assertFalse(res.isRenewable(), "Unexpected renewable flag");
assertEquals(0, res.getLeaseDuration(), "Unexpected lease duration");
assertEquals(CIPHERTEXT, res.getCiphertext(), "Incorrect ciphertext");
assertNull(res.getPlaintext(), "Unexpected plaintext");
assertNull(res.getSum(), "Unexpected sum");
assertNull(res.getWrapInfo(), "Unexpected wrap info");
assertNull(res.getWarnings(), "Unexpected warnings");
assertNull(res.getAuth(), "Unexpected auth");
}
@Test
void decryptionTest() {
TransitResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(
json("\"plaintext\": \"" + PLAINTEXT + "\""),
TransitResponse.class
),
"TransitResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals("987c6daf-b0e2-4142-a970-1e61fdb249d7", res.getRequestId(), "Incorrect request id");
assertEquals("", res.getLeaseId(), "Unexpected lease id");
assertFalse(res.isRenewable(), "Unexpected renewable flag");
assertEquals(0, res.getLeaseDuration(), "Unexpected lease duration");
assertNull(res.getCiphertext(), "Unexpected ciphertext");
assertEquals(PLAINTEXT, res.getPlaintext(), "Incorrect plaintext");
assertNull(res.getSum(), "Unexpected sum");
assertNull(res.getWrapInfo(), "Unexpected wrap info");
assertNull(res.getWarnings(), "Unexpected warnings");
assertNull(res.getAuth(), "Unexpected auth");
}
@Test
void hashTest() {
TransitResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(
json("\"sum\": \"" + SUM + "\""),
TransitResponse.class
),
"TransitResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals("987c6daf-b0e2-4142-a970-1e61fdb249d7", res.getRequestId(), "Incorrect request id");
assertEquals("", res.getLeaseId(), "Unexpected lease id");
assertFalse(res.isRenewable(), "Unexpected renewable flag");
assertEquals(0, res.getLeaseDuration(), "Unexpected lease duration");
assertNull(res.getCiphertext(), "Unexpected ciphertext");
assertNull(res.getPlaintext(), "Unexpected plaintext");
assertEquals(SUM, res.getSum(), "Incorrect sum");
assertNull(res.getWrapInfo(), "Unexpected wrap info");
assertNull(res.getWarnings(), "Unexpected warnings");
assertNull(res.getAuth(), "Unexpected auth");
}
private static String json(String data) {
return "{\n" +
" \"request_id\" : \"987c6daf-b0e2-4142-a970-1e61fdb249d7\",\n" +
" \"lease_id\" : \"\",\n" +
" \"renewable\" : false,\n" +
" \"lease_duration\" : 0,\n" +
" \"data\" : {\n" +
" " + data + "\n" +
" },\n" +
" \"wrap_info\" : null,\n" +
" \"warnings\" : null,\n" +
" \"auth\" : null\n" +
"}";
}
}

@ -1,103 +0,0 @@
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");
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
* Copyright 2016-2025 Stefan Kalscheuer
* Copyright 2016-2022 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

@ -0,0 +1 @@
{"Value":"AAAAAQK5U1GclNj+Tga7D4bQ5wExYfVu2y+djHlAlhiJ/JHOS1gS0G/kDrjR8gCdg/Aw2UunrObAq/mrKw0HEe1wo2qA"}

@ -0,0 +1 @@
{"Value":"AAAAAQLbvc+neI458Mqhl2WUUjY5HMC1Ast0KjZ5pslwW+5TtjVHcqdzls4whrrYHGUWv+nTg6wxJaS46j5+FER+4gsgWVJE1S33ZqvGtmmueCVpac5ZM0biBDXOvE/YFQ=="}

@ -0,0 +1 @@
{"Value":"AAAAAQIK4FkvUHPiWUfHY7l9lGW1qf+sU2mAIWbjlfSvEIecbg94Mu4KAPxY3E2YLwOs7VyPZtWNZrZAZDMJJJzxM/pLux2o/IctJ5oXGtfPPjTjwNRRJ4U62wpRqBnBGX4="}

@ -1 +1 @@
{"Value":"AAAAAQK788ueZMqWkxK0+QrdrSU6/fJE0BHpMFCmXuZch21YdFNQSxZwgbTTH6uAyzxlEpHvAa4v9ScNyYXqRCLFK4X0DtDmpheIr/Ow6BPU8yxNglfI+qW5SKZKeYAYMHal0jX1MYsaIdYp8LJm52VMa+CdjTRx1J7ZhrNqiNHXbVJr6uOM+FDbGU4W8LyDy1ZhBKo17fwMnOycl5vBHu7b964iVHAIDkZHBq/4N1R/w7Sne7K/JvNPfuBNd1evRSErRNJRPp4DEkpK2v8lgIAXFjemE+aOysWYjGh/XVi8EzP09K1QFwfsvgIeIX0XVP4wMNn2xHedDyWQV55FJbD1LcrM2Q54HAHM0YGItI+MwPPzmP06Udoq3PZNRMWRI3l1rdjnIT+3FtbJr43EF8mf6SuYCzf9QWkGkixWAPAsMO+ZYCkHfsp8Ebq5IQmgK2IXzR7Dw8+tizs+8jaXB5JJ1vru0dZJa3b1oV9eizW5U53yIWNhC2v/d0rVxivvMbZ2deAP9OaqXm+oFukIjaFHDNbAskqvpHh8/7e91fqDFla2ZNEEPtVSsBBmyKHO0gDiDHKC3T2hrvvSRHsMHplIpD1mnppE2E4BlngIDgrfAe3G2jKUHg9eYLq9dMXBeysfzc8UtBI="}
{"Value":"AAAAAQKHd7fFuNKfj/RnIQoxQEOTorNNl1NfcVtynLwYvtvZGDvhAO34YmOFKJSQVSiT4Js08ik48DWWnZHCZzAyMlK0HqNGYQRzCkuHkzYpH0uLrTcddXLuKyc7j6IiRTl3qYb1pzdlTzDtlwsi4J7s+7orETS146yY/GnGPWM/Tmet7p5qOECRq7m04gBX0pnwZl12iRqWG4aHHY5D4aFp9KjKUBKCIhz2Tb9wcSKV/Aa/ct1d0K0k8pyu1r1OkaJqCQXQeIaJR/WBMECR4omezvg7IGCoBIkMjwFR1urSaB0xNdgk+ByZE0C5zeDfmVRylYlypPAlIBrGL/Q/d6+NWiiOzXdoQoqAa/Pyg7e2gClsKDBFDvSL1QVBdsnBIskDeig2t46Ew5qM00wY58BWlUxqjlCgsNm3nPHtWpc6+XiR4ZkEZ9VeEIQhwC/R+NxS+QCDKeuSfyZM3OqQBwOx22mTL0i86YzlUph0alBmDF1b1FkDbg46MSWTANrkov1LPDnOvvNet7pb0L6exN5Q+HKitmxn36E0KAiqj1hEaHMZF6Pe+IIScLPClgPNeMkZWLeD5kvLv9kfObTYneHVHqj8O9jGtk03T4vOoaUNJY9U0irBGzlV+GkneJUgBL1QBspfK4GvFvUrd0Ds/teayJba8HDxlpCEWr6h062BXsNPg5gUn4H/uvginsFng02M8IyrDA1QOb4XS1zdfrvq"}

@ -1 +1 @@
{"Value":"AAAAAQJ7mykBIbHX5k81qdXEpvlLgRF1ZSlODETcB1JBZ7nj0Muskpvvl3jofN5XH1Td8ibJlrR0o5o/OUjZAz9t0Da+ZzCy4ga9G2SmgWkUAravTqfPO/ZxWh2hqTso2WPBXRM3/IeR0SAv/zh7m7JILxjKybJmnl9U6fkjPID/us0AscckZ9kgJ4g8jwaTzPfRp5U8jMebHYbABXZ65PeUOvNiDVcOvQDWPJrMxICz12xbeJ8mKs5MHpcNkLtPoRCQSpsh0YYTwkuF7NvpIciqIJ4/Yb7wYO+vp9AATbiIs3sSFBWxXEl0OAg/SAmOvaR27Y7/NHN//mg81jkMOHv62/Fxf11I8t1d63oyWFQhP0xR9eoq5hGNQ/30I2m1prhZtLRC2ieKASBDMxTzyNS5G5bsVXvhCsxn8tiC0Ma+ySOfxMQzBRfbx8rtoGmLFP+l/d6VMOPGFxmYqzLS5HvvpCryscCqLn7A8i6TMSrZiF7ZevyfEBpThqhJiYHzUxf07O5IAe6JBSGuNV9gLE+uJXaiYLedJwSfjRKwdQyzer730dU1IegW3KYTb/5hSW4eaETKkjc/alC536WlvAv+5FyckDBc3aVC+hHB7lKZG5YANkOUS3m5I8epemOmuKQ5pnXLOdDkQ14DyNCC79NwLltkp0d6sTNstQ44XAbs0HlLjs4EFwg5Hps9qHeXgTOXeAvwUerJgM20nKszlB+Oy+JzZm0xOK5xoJwy+z0/U3PtJ+7pwAipesIa2QEzqMt3EneuPuwEcv3bPUcowukq542sCEK0CuZjLqTUU9nNqiZan5f5pWuL0hYw4NFIkNfQyRlqgKpMaplDk/2fBqaV98yn0DWceEMWRY4NXpEMS+ysPDIeamX99auWqakb95AZ7tySpkRAkZYtq1nY5Nu0w7NyJrJZ1lhBHs2ZjW0tpXn2CL0MhMVArg=="}
{"Value":"AAAAAQIOTEuNxtf2ZfGu6k9+65GFonDBiaetJnyLYFk1qfWPrE/VqUunbxuTv/QyK4pgC/q14sqypdxPe4Ygp/5mxzzuY6JXB0VKiDMXe9R5BltTG7++rLmKH/j+G7nEh71LER1/qVktU6x8dmDmTbpTgl5xzAB5DIXLMMKjjWda/7qJ3ivNI0pEOQ3JyT27SkqjqM49Dp1JIgKnjIEVQORqKcsSP+aqIncMjIo2iGXOGlDYAesp5tZ4hln3VCwXaIlrU8z6Mmntgcg7NeK/O2WTl86K644dbJUh6frGFDujrjOh/Pgp9n9u0BI3E9K9GD1Z1S1wEb1MCqzT/e9oHG7I7D8ku8PH11wGWGH6BmYlESYUG/KRVqu0XOQEfroLHZQiLE00yHBdStW/UJ9y5pmGpu1aiQ88Q8fpjF5xmRey99b4c6KUIpHjw5Af0XA9ZKsEJUyjS/dbMKPM6PBOW21LYefXH5b0poXMWgLW6LJV0zLuVMyVZJqANbzCVtheDPSsEjkHHwR/CLa2zs2Z6wJF3j1GDZxUFf4nbnuXQzz3M3kVPPtS6htlb0d8RN0/dkOrqUue+c4UjnXhiacXyVUnQQzUVu0sGak4vrQi0020aBzMXM2ZG7rNgvw+wcYFS4txO90kwJL6aOMXT2BeJiQ3wjjHb7M74/sSd0ffRTUlJSODSDotO7Q7Dfcnc1FLrIhXPRFPavTjFoL5HRy77xuGG7U7jTMoGra+rK54v0sxqKbS5WZi1hADQmAVIO8bPb+jA1qoejRFygW6sLgAdmEFQQJOhCV/BoKP3A=="}

@ -1 +0,0 @@
{"Value":"AAAAAQIwsBgPcs7Hxl7UnB8OkTq9+5HERx4Fzn8jAzR8uIhoQZfpNG/15m2LEEsDJw1o+lxc6u+h7XcOB1QRqrKz8k7CFR15A+qsxD0UclHZdnk+MmMwXL9SSvYugp7XPiXmpG/uTZVf1QtigXQuehEEJeFfJVI7aFACu2AHEGVt+5b6yDQEgTUruo0seazRXuVU+J6NFCDU3ZvkR8e0al6OcMail59KamzjSCYiqDF4TeUuOQ6Zyr7zIG7gSaas1sog2JIlvrh+wkHW6I+OyD9SJSTinGEGRXl0tq15qoBJ4srfXdWODmKtExEupArbiDR5PyvSI3KlIHKIFduslJZKkJwiV3dBscdva4Rqb98FffMVYuM4G4s+VpvDDX+WVsqWF1ssoHRAFWCNAJGsenVDTxblzAF/4rGkJ7LC9yjLGsBtMOCkCZAKDQ3C9VFu+LGhbMRA5p2RKxNKWGem4Cyp5AqMmx62UzDAgMLGgm89A0g9s1/3FnCoLPdVmlWc6cg4QahN30qJCInJeAmH7T9NwIjv6/QxyJxyDVtdMtcfnMNxx15Q29lbyWTcbaI1iabHpc16iS/gwAPQeBTSbcvc4OxqwC5PDFThFq6bwZXaekYz4ghC13j9Ht79GVKH9cPZb5M="}

Some files were not shown because too many files have changed in this diff Show More