129 Commits

Author SHA1 Message Date
a9f1eb63c2 use curl instead of wget for Drone CI builds 2019-08-16 20:16:23 +02:00
fd749b19e5 prepare 0.8.1 release 2019-08-16 20:01:48 +02:00
0cca2de1aa Set correct test scope for JUnit migration support lib (fix #30) 2019-08-16 19:59:09 +02:00
8b278f67fe Update Jackson dependency 2019-08-16 19:54:46 +02:00
8f3462b22a Test against Vault 1.2.2 2019-08-16 19:51:37 +02:00
a394cb7f0d Test against Vault 1.2.0 2019-07-30 21:11:13 +02:00
50d0b2fe56 Fix test exclusions for offline CI using JUnit tags 2019-07-27 15:07:36 +02:00
e2bb09d50f minor dependency updates 2019-07-26 19:39:36 +02:00
4a2b40a4cf fix Javadoc generation with JDK11+ 2019-07-26 19:28:18 +02:00
bed9c868f6 Test against 1.1.3; update Drone CI config; minor test cleanup 2019-06-17 19:11:26 +02:00
7a45af8856 Update dependencies 2019-06-01 20:05:08 +02:00
4bafcec012 Test against 1.1.2 2019-06-01 16:39:41 +02:00
4ca150a0cb Prepare 0.8.0 release 2019-03-24 11:53:45 +01:00
efb0aba7cd Clean up imports of factory class 2019-03-24 11:51:19 +01:00
17f1ee79db Change artifact id to jvault-connector (closes #28) 2019-03-24 11:41:59 +01:00
d8b9c2b373 Add static builder() method to AppRole class 2019-03-23 21:18:23 +01:00
04d530a5ed Use Token.builder() in unit tests 2019-03-23 21:13:53 +01:00
b134b5a605 Group HTTP connector tests into nested test suites
We're using JUnit 5 so leverage some new features to make reasonable
test groups.
2019-03-23 19:18:05 +01:00
f8ff93fa6c Update ReadMe to builder and replace deprecated examples [skip ci] 2019-03-23 11:12:17 +01:00
5bd6a90f25 Add static builder() method to Token class 2019-03-23 11:07:03 +01:00
92cfc3a597 Update missing test data 2019-03-22 19:16:29 +01:00
3f47da6134 Do not reuse surefire forks for testing
The offline test messes up with the online test if executed in advance
because of the static HTTP mock. With no guaranteed execution order
all online tests may be skipped. To prevent this, run each test in its
own fork.
2019-03-22 19:10:19 +01:00
3446d1590e Add some messages to test assumptions
Tests are skipped for some reason in CI environment. This might help to
debug the cause.
2019-03-22 18:01:50 +01:00
a24ddf65a5 Migrate TemporaryFolder rule to JUnit 5.4 TempDir annotation 2019-03-22 17:13:16 +01:00
d6f422e0ec Back to future [skip ci]
Some source files have lived in a feature branch since 2018 and did not
receive the update to 2019...
2019-03-22 17:08:28 +01:00
fbaa560551 Update dependencies 2019-03-22 16:49:28 +01:00
42c2869675 Minor JavaDoc adjustments 2019-03-22 16:45:16 +01:00
eabc458176 Add missing parameters to JavaDoc [skip ci] 2019-03-22 15:01:21 +01:00
dee7ec0b23 Implement update of KV v2 metadata (#16) 2019-03-22 14:40:27 +01:00
ab33325b8e Implement writing of KV v2 secret data (#16) 2019-03-22 14:27:30 +01:00
e4cf8a1dde Move 204 empty checks into request helper and make Error class private 2019-03-22 10:19:06 +01:00
22a48d4a90 Move parsing of JSON response into RequestHelper
The pattern is always the same, issue request, parse String response
into target class and catch exceptions. Bundle these blocks in the
helper class for GET, POST and PUT methods to reduce overhead in the
actual connector.
2019-03-22 10:03:49 +01:00
7020d3701c Default test against Vault 1.1.0 2019-03-21 20:22:38 +01:00
51e54d9870 Extract request methods and error codes into separate classes.
To clean up the actual connector class all HTTP wrappers are now bundled
within the RequestHelper class.
2019-03-21 20:17:04 +01:00
b103d6e804 Bundle authorization check in method to reduce repetition 2019-03-21 20:13:50 +01:00
1aade2882b Clean up imports and move common parameters into constants 2019-03-17 15:15:35 +01:00
7909a6772d Minor CI adjustments
Trusty is default in Travis and no need to encorce Xenial environment.

Correct pipeline name for Drone build, "java8" is no longer applicable.
2019-03-17 15:15:35 +01:00
5f391286e5 KV v2 and mount points in changelog 2019-03-17 15:15:35 +01:00
068f961454 Introduce "mount" parameter for KV v2 methods (#25)
The backend can be mounted on an arbitrary path, so this path can now be
passed as an arugment along with the corresponding method call.
2019-03-17 15:15:35 +01:00
ad2986195d Merge branch 'feature/16-kv_v2_support' into develop 2019-03-17 14:05:56 +01:00
487070bcd2 Test against 1.0.3 2019-03-17 12:18:11 +01:00
646379a69a Migrate unit tests to builder instead of factory 2019-03-17 12:17:28 +01:00
5e143e9f36 Switch to JDK 11 for CI builds 2019-01-14 20:32:18 +01:00
19f9a97422 Update dependencies
Most notable update is Jackson 2.9.8 that fixes several vulnerabilities.
Beyond that just some minor test dependency updates.
2019-01-14 20:24:40 +01:00
aca5443bdd Test against 1.0.1 2019-01-14 20:15:37 +01:00
e59073cf00 Update copyright notice to 2019 [skip ci]
Happy new year!
2019-01-08 19:11:18 +01:00
7032bd4b2b Test against 1.0.0 2018-12-10 18:12:25 +01:00
263669362f Code style: add curly braces to all one-line if-else blocks 2018-11-20 14:36:29 +01:00
3b2a3dd70a Add unit tests for CredentialsResponse and SecretListResponse models 2018-11-20 14:12:35 +01:00
d1876c88aa Add unit tests for secret metadata models and fixed JSON property name 2018-11-20 13:50:28 +01:00
c2bd54ca22 Extend unit test to new KV v2 methods [skip ci]
This test does not yet work without changes, because KV v2 is mounted on
 non-standard path and this is not yet supported (see #25).
2018-11-20 12:11:37 +01:00
493bed55f0 Add method to read specific secret version 2018-11-20 12:08:58 +01:00
e41a61f33b Add methods to delete, undelete and destroy KV v2 secret versions 2018-11-20 11:59:15 +01:00
e3f2193df2 Add capability to pass options map when writing to Vault
This is required to create or update KV v2 secrets. The existing write
method delegates to the new one with null-value for the options map.
2018-11-20 11:26:02 +01:00
068a87d915 Add methods for reading KV v2 data and metadata 2018-11-20 11:09:58 +01:00
04e92626bd Add response models for KV v2 API
Secret response is now split in data and metadata. Pure metadata queries
return the new SecretMetadata class.
2018-11-20 11:09:54 +01:00
c8aeb1396d Merge branch 'develop' 2018-11-20 10:52:57 +01:00
12083df14b Token creation test correction for Vault 1.0
Token creation with custom ID now raises a warning that causes the Unit
test to fail.
2018-11-20 10:51:39 +01:00
2e0d79424f Reformat changelog [skip ci] 2018-11-19 15:36:42 +01:00
9caeac4cba Support secret_id_bound_cidrs replacing bound_cidr_list for AppRole (#24)
The latter parameter is deprecated in Vault. The connector now supports
both while the former one is deprecated and will be removed when it is
removed from Vault.
2018-11-19 15:21:19 +01:00
c1ec929147 Re-build test data using Vault 0.11
In preparations for 1.0 compatibility and some later features the test
data generated with 0.5 becomes more of a problem without intermediate
updates, so it is now rebuilt from scratch.

As part of this change the unseal key is now split in 3 parts with a
threshold of 2 instead of the former 1/1,
2018-11-19 15:00:23 +01:00
96fd377db2 Moved Vault version to test into environment variable 2018-11-14 18:00:25 +01:00
46cb33fd7d Test against 0.11.5 2018-11-14 17:47:32 +01:00
384cc77de2 Add Drone CI configuration 2018-11-13 12:35:45 +01:00
2ca4473481 Test against 0.11.4 2018-10-24 09:25:03 +02:00
b6d36fbc5c Test against 0.11.4 2018-10-24 09:22:25 +02:00
c869a640a9 Test against 0.11.3 2018-10-10 14:17:38 +02:00
874341ddf7 Removed abstract methods defined in builder interface from factory 2018-10-06 14:28:58 +02:00
c111a6aff0 Enforce TLS 1.2 by default with option to override (#22)
The TLS version can be explicitly set in builder or constructor. If not
given, the connector will only use 1.2 as Vault does by default, too.
2018-10-06 14:24:06 +02:00
13793dc9ce Add replication flags to HealthResponse (closes #21) 2018-10-06 10:58:43 +02:00
44858edb76 Add missing flags to SealResponse (closes #20) 2018-10-05 20:31:59 +02:00
8287be48b9 Merge branch 'master' into develop 2018-10-05 19:41:36 +02:00
51d118786a Test against 0.11.2 2018-10-05 19:37:37 +02:00
0193bebf0b Fixed Sonarcloud quality gate badge [skip ci] 2018-09-09 14:57:12 +02:00
493e22e46b Update dependencies 2018-09-09 14:43:13 +02:00
6b1211d90f Modified test mocks for compatibility with JDK 10 build environments 2018-09-09 14:39:52 +02:00
ee9a6530d3 Test against 0.11.1 2018-09-09 14:01:31 +02:00
3db73a913a Test against 0.11.0 2018-08-29 11:45:37 +02:00
ce9cd9bbf1 Test against 0.10.4 2018-07-26 20:10:06 +02:00
9e50190c02 Test against 0.10.3 2018-06-22 20:26:42 +02:00
4dea8a1c5f Test against 0.10.2 2018-06-10 20:37:26 +02:00
47e7a3f4b0 Test against 0.10.1 2018-04-26 20:44:52 +02:00
a5a708895e Test against 0.10.0 2018-04-19 20:24:49 +02:00
810073cb3b Add issue numbers to 0.8.0 changelog 2018-04-10 19:01:27 +02:00
bc1223e1a3 Test against 0.10.0 2018-04-10 18:21:52 +02:00
4d46f2c6d1 Modified test mocks for compatibility with JDK 10 build environments 2018-03-28 17:39:30 +02:00
d2aaea1938 Clean up after migration to Builder
* Corrected implementation of Builder interface instead of the abstract Factory class

* Removed withSslContext() which is already deprecated in the factory
2018-03-25 17:45:59 +02:00
e996ff157a Minor logo modifications
Replaced circles of the "J" with squares
2018-03-25 17:11:16 +02:00
065f662b95 Add package-info and overview.html for more Javadoc output 2018-03-25 14:57:07 +02:00
44c1a685a6 Added more details from distribution version to POM 2018-03-24 14:08:04 +01:00
b02d06f1dd Added sources and javadoc profiles to POM 2018-03-24 14:08:00 +01:00
23fbc7a6d3 Replaced VaultConnectorFactory with VaultConnectorBuilder
The class is more a builder than a factory, so is has been renamed.
The factory delegates to the builder for now to ease migration, but will
be removed with the next major release.
2018-03-24 13:59:22 +01:00
1a18f9f6b7 Refactored custom trusted CA strategy
The connector no longer stores the final SSLContext, but the trusted
X509Certificate object and creates a SSLSocketFactory as required.
2018-03-24 13:43:27 +01:00
0c23f47bd5 Test against 0.9.6 2018-03-21 11:48:27 +01:00
aa2619b1b0 Bump version to 0.7.1 2018-03-17 14:01:55 +01:00
88d49bc9f7 Added automatic module name for JPMS compatibility 2018-03-15 19:54:18 +01:00
27e7a2dffc Test against 0.9.5 2018-02-27 18:07:23 +01:00
41c15f285c Test against 0.9.4 2018-02-24 10:58:45 +01:00
5f419e3f9b Updated test dependencies
* JUnit 5.1.0
* Mockito 2.15.0
2018-02-20 19:25:44 +01:00
9362e245ee Minor test changes
Changed Thread.sleep() to TimeUnit.SECONDS.sleep()
2018-02-20 19:25:07 +01:00
b2082925d5 Copy Vault data directory to temp location before each test
To avoid the annoying clean before each test run (because Vault data has
been modified by previous run), the data is now copied from resource
directory to temporary location.
2018-02-04 20:04:47 +01:00
38a7d4952d Minor dependency updates
* httpcore 4.4.9
* httpclient 4.5.5
* jackon 2.9.4
2018-01-29 16:34:28 +01:00
23cea38da6 Test against 0.9.3 2018-01-29 16:30:51 +01:00
bf2da210ba Test against 0.9.2 2018-01-28 10:35:19 +01:00
91fb012acc Update copyright notice to 2018 2018-01-01 17:22:02 +01:00
61e1f3f745 Test against 0.9.1 2017-12-22 16:40:15 +01:00
50cd400ba3 Migrated tests to JUnit5 and removed PowerMock
* Unit tests are using JUnit Jupiter framework
 * Enabled support for legacy rules for now
 * Replaced PowerMock with custom ByteBuddy redefinition for the offline test
2017-11-26 18:17:13 +01:00
470dcb48ba Test framework migrated to Mockito/Powermock 2
This simple migration with the bare minimum of changes necessary solves
compatibility issues with Java 9 build environments.
2017-11-25 18:43:25 +01:00
736f23c19a Tested against 0.9.0 2017-11-16 20:34:52 +01:00
ed2b9d62a3 Preparations for 0.7.0 release 2017-10-03 17:39:03 +02:00
007b523295 Extracted nested try-blocks from response handling into new methods. 2017-10-03 17:29:11 +02:00
061c1e9743 Minor CleanUp
Reworked some JavaDoc comments and optimized imports.
2017-10-03 17:12:03 +02:00
6904ed6817 Added tests for parse exceptions. 2017-09-27 20:23:14 +02:00
1ed5d8d992 Added tests for unexpected 200 responses.
Some methods do expect code 204 (successful without result), those are now covered by tested.
2017-09-27 20:07:12 +02:00
f70fc084be Override toString() on ErrorResponse
Partially reverts last commit that added first error message to exception
without checking for presence of such.
2017-09-25 20:48:36 +02:00
4b14ab3f4b Offline tests with mocked Vault server
Added some tests that do not require an actual Vault server to test constructors and exceptional behavior.
2017-09-25 20:39:13 +02:00
29776f459e Fixed Exception class on invalid response during sealStatus() 2017-09-25 20:35:37 +02:00
4ca8aa56d2 Test constructors and exceptions on sealStatus 2017-09-24 13:23:13 +02:00
32ab9f4bb1 Updated dependencies 2017-09-24 11:39:26 +02:00
e002fc749a Adaptation to Vault 0.8 endpoints for renew and revoke leases (#11)
Breaking backwards compatibility with Vault 0.7 and below.
2017-09-24 11:08:00 +02:00
b9ad2d1551 Test against 0.8.3
Minor test adaptation, because default policy is no longer added to AppRole by default.
2017-09-23 14:46:41 +02:00
35a8c2e0fa Test constants made constant 2017-09-09 20:32:56 +02:00
89f7581d17 Test against 0.8.2 2017-09-08 19:21:41 +02:00
43511dc20b AppRole path generation with pre-formatted String 2017-08-29 09:04:04 +02:00
05b44759c0 Bundled common error messages in static inner class 2017-08-29 08:56:30 +02:00
ba17286ab3 Add Slack notification to Travis 2017-08-29 08:25:31 +02:00
51e505313a Removed deprecated method listAppRoleSecretss (#14)
Has been deprecated because of a typo in the method name in v0.6.2.
As of 0.7 it is now removed. Please use listAppRoleSecrets() instead.
2017-08-29 08:18:10 +02:00
a1784245a3 Refactored (un)seal methods to throw Exception instead of catching it (#12) 2017-08-28 19:24:52 +02:00
df7de5dd73 Health status query and response model implemented (#15) 2017-08-28 17:50:24 +02:00
162 changed files with 5479 additions and 1756 deletions

26
.drone.yml Normal file
View File

@ -0,0 +1,26 @@
kind: pipeline
name: default
steps:
- name: test-online
image: maven:3-jdk-11
environment:
VAULT_VERSION: 1.2.2
commands:
- curl -o vault_1.2.2_linux_amd64.zip https://releases.hashicorp.com/vault/1.2.2/vault_1.2.2_linux_amd64.zip
- curl -s https://releases.hashicorp.com/vault/1.2.2/vault_1.2.2_SHA256SUMS | grep linux_amd64 | sha256sum -c
- unzip vault_1.2.2_linux_amd64.zip
- rm vault_1.2.2_linux_amd64.zip
- mv vault /bin/
- mvn clean test
when:
branch:
- master
- name: test-offline
image: maven:3-jdk-11
commands:
- mvn clean test -P offline-tests
when:
branch:
- develop
- feature/*

View File

@ -1,10 +1,6 @@
branches:
only:
- master
language: java
jdk:
- oraclejdk8
dist: trusty
- openjdk11
install: true
addons:
sonarcloud:
@ -12,14 +8,26 @@ addons:
token:
secure: "sM9OfX5jW764pn9cb2LSXArnXucKMws+eGeg5NnZxHRcGYt4hpBKLSregBSsBNzUoWVj0zNzPCpnh+UQvgxQzUerOqwEdjTBpy3SNPaxSn7UpoSg+Wz3aUmL9ugmx01b51/wMG4UCHEwTZt2tpgTPVtw8K6uSO78e0dSICCBHDnRcdQwOjMEQHIJJ/qHVRwuy/MzLCAP3W1JPZlsphZg9QsFyhB4hW97dE90joZezfocQIv2xI/r6k+BLz0pY6MxYCul0RiDumaiaej0CPvEJI/uSu//BAQjUdHw+mQgnKUYIbrn2ONOviwNfwdr94JyoZEN2B6zASUmNLjPf4AbIojDeyS+CrpQpm17EVm/Qk/Ds+Xra4PPPIcsZhiWzV0KoDUz9xLfXuRJ526VT5tDPiaeI7oETf0+8l+JIS1b399FyqHi7smzjpvC6GuKflQrbuHK4MuKzDh7WTHiqokGG4SS0wOQIaaHB3dfdwwQzPh6IM24e8CETxh3DjMeqUTU4DWmv5po55jZ934TtxVQvVN78bTG9O0zS9u+JmRY04OZ+OaXuFam6MfMUFQi0EPZzdGul/oWSibGUu3bNfVEBp60CnJwYNM/dKG6U7pJthLHvSwiQFOdKzHZ+l1jZJ4gPaXaIGqpwqVGr28ntqA/El1rytPixr2driE6bYMt5jw="
env:
- PATH=$PATH:.
- PATH=$PATH:. VAULT_VERSION=1.2.2
before_script:
- wget https://releases.hashicorp.com/vault/0.8.1/vault_0.8.1_linux_amd64.zip
- unzip vault_0.8.1_linux_amd64.zip
- rm vault_0.8.1_linux_amd64.zip
- |
if [ "$TRAVIS_BRANCH" = "master" ]; then
wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
wget -q -O - https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_SHA256SUMS | grep linux_amd64 | sha256sum -c
unzip vault_${VAULT_VERSION}_linux_amd64.zip
rm vault_${VAULT_VERSION}_linux_amd64.zip
fi
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar
- |
if [ "$TRAVIS_BRANCH" = "master" ]; then
mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar
else
mvn clean test -P offline-tests
fi
notifications:
slack:
secure: "YyE5GePOLkCVTtCy8j507BRmQrtrWhtvmUt4kY0Z2/ptf0LzfuDEJQ4ZbCxO5ri5IDJrrvyPAedjft818+bMzdFfxvi1oviIL+LZNhyev8gfeIBF/U2pvSLGKCRX4g4aZ6NKN3Untjdm8lmiVTltOyZ59JizQVwXzAl3LiOpnJugyBqbhOx4EIqBzwW3gaYAofMqY2LczW5W/M+99HJCst8Mb8H06GstCPEHCizAq7VRaUS68PstlxQMV0Q6bsSYMLFbLWmhuXs96WHqOrT+nNsl07ikr3N8c4HafhFutt2Jyc1+8gXO417+eSvVM0iBpHGwTmfGFfCqx/4Pf62DTJuvh8dR4fLgLDiqEeDrBEcRRDOs9cvXVOO22NN1HuBBJY8VRiFcwNAvuVMXCtnC+1RJRAZB2zubsANiFe+ygk/ywj37cVXY+NpqlBwcSph6jPHo2hD6cIl2rTWn1EnZH519Rh38xTSv6MRzAO9kWNVrAlX+UtvYS8Sk7Owrc0tET9Lc4zj6aI5tsA1wYbN3Jk6EbMhsF6K/XF2npt2qg09pxkj8wmxoUoR6/rGuSv55aSxTdLDmH+en4ahEm3uc4h1lYoVCk0yrZoTAas3zS4WpBCKnl+mweuKNxaejyy0Wv6NR9ZCTaS3yFgibNOjvDpxZxTAPdNBL7hn+k4LwgN4="

View File

@ -1,55 +1,164 @@
## 0.8.1 (2019-08-16)
### Fixes
* Removed compile dependency to JUnit library (#30)
### Improvements
* Updated ependencies
### Test
* Tested against Vault 1.2.2
## 0.8.0 (2019-03-24)
### Breaking
* Moved Maven artifact to `de.stklcode.jvault:jvault-connector` (#28)
* Removed support for `HTTPVaultConnectorFactory#withSslContext()` in favor of `#withTrustedCA()` due to
### Features
* Support for KV version 2 secret engine (#16)
* Ability to pass custom mount point to KV v2 read/write methods (#25)
### Improvements
* refactoring of the internal SSL handling (#17)
* `VaultConnector` extends `java.io.Serializable` (#19)
* Added missing flags to `SealResponse` (#20)
* Added replication flags to `HealthResponse` (#21)
* Enforce TLS 1.2 by default with option to override (#22)
* Build environment and tests now compatible with Java 10
* Updated dependencies to fix vulnerabilities (i.e. CVE-2018-7489)
* New static method `Token.builder()` to get token builder instance
* New static method `AppRole.builder()` to get AppRole builder instance
### Deprecation
* `VaultConnectorFactory` is deprecated in favor of `VaultConnectorBuilder` with identical API (#18)
* `AppRoleBuilder#withBoundCidrList(List)` is deprecated in favor of `AppRoleBuilder#withSecretIdBoundCidrs(List)` (#24)
## 0.7.1 (2018-03-17)
### Improvements
* Added automatic module name for JPMS compatibility
* Minor dependency updates
### Test
* Tested against Vault 0.9.5
## 0.7.0 (2017-10-03)
### Features
* Retrieval of health status via `getHealth()` (#15)
### Improvements
* `seal()`, `unseal()` are now `void` and throw Exception on error (#12)
* Adaptation to Vault 0.8 endpoints for `renew` and `revoke`, **breaking** 0.7 compatibility (#11)
### Removed
* Removed deprecated `listAppRoleSecretss()` (use `listAppRoleSecrets()`) (#14)
### Test
* Tested against Vault 0.8.3
## 0.6.2 [2017-08-19]
* [fix] Prevent potential NPE on SecretResponse getter
* [fix] Removed stack traces on PUT request and response deserialization (#13)
* [improvement] Fields of InvalidResposneException made final
* [deprecation] `listAppRoleSecretss()` in favor of `listAppRoleSecrets()` (#14)
* [test] Tested against Vault 0.8.1, increased coverage
### Fixes
* Prevent potential NPE on SecretResponse getter
* Removed stack traces on PUT request and response deserialization (#13)
## 0.6.1 [2017-08-02]
* [fix] `TokenModel.getPassword()` returned username instead of password
* [fix] `TokenModel.getUsername()` and `getPassword()` could produce NPE in multithreaded environments
* [fix] `TokenData.getCreatinTtl()` renamed to `getCreationTtl()` (typo fix)
* [test] Tested against Vault 0.7.3
### Improvements
* Fields of InvalidResposneException made final
## 0.6.0 [2017-05-12]
* [feature] Initialization from environment variables using `fromEnv()` in factory (#8)
* [feature] Automatic authentication with `buildAndAuth()`
* [feature] Custom timeout and number of retries (#9)
* [feature] Connector implements `AutoCloseable`
* [fix] `SecretResponse` does not throw NPE on `get(key)` and `getData()`
* [test] Tested against Vault 0.7.2
### Deprecation
* `listAppRoleSecretss()` in favor of `listAppRoleSecrets()` (#14)
### Test
* Tested against Vault 0.8.1, increased coverage
## 0.6.1 (2017-08-02)
### Fixes
* `TokenModel.getPassword()` returned username instead of password
* `TokenModel.getUsername()` and `getPassword()` could produce NPE in multithreaded environments
* `TokenData.getCreatinTtl()` renamed to `getCreationTtl()` (typo fix)
### Test
* Tested against Vault 0.7.3
## 0.6.0 (2017-05-12)
### Features
* Initialization from environment variables using `fromEnv()` in factory (#8)
* Automatic authentication with `buildAndAuth()`
* Custom timeout and number of retries (#9)
* Connector implements `AutoCloseable`
### Fixes
* `SecretResponse` does not throw NPE on `get(key)` and `getData()`
### Test
* Tested against Vault 0.7.2
## 0.5.0 (2017-03-18)
### Features
* Convenience methods for DB credentials (#7)
### Fixes
* Minor bugfix in TokenBuilder
### Deprecation
* `SecretResponse.getValue()` deprecated
### Test
* Tested against Vault 0.7.0
## 0.5.0 [2017-03-18]
* [feature] Convenience methods for DB credentials (#7)
* [fix] Minor bugfix in TokenBuilder
* [deprecation] `SecretResponse.getValue()` deprecated
* [test] Tested against Vault 0.7.0
## 0.4.1 [2016-12-24]
* [fix] Factory Null-tolerant for trusted certificate (#6)
* [test] StackTraces tested for secret leaks
* [test] Tested against Vault 0.6.4
### Fixes
* Factory Null-tolerant for trusted certificate (#6)
## 0.4.0 [2016-11-06]
* [feature] Option to provide a trusted CA certificate (#2)
* [feature] Deletion, revocation and renewal of secrets (#3)
* [feature] Token creation (#4)
* [feature] AppRole auth backend supported (#5)
* [improvement] Support for complex secrets
* [deprecation] App-ID backend marked as deprecated
### Test
* StackTraces tested for secret leaks
* Tested against Vault 0.6.4
## 0.3.0 [2016-10-07]
* [feature] Retrieval of JSON objects (#1)
* [test] Tested against Vault 0.6.2
## 0.2.0 [2016-09-01]
## 0.4.0 (2016-11-06)
### Features
* Option to provide a trusted CA certificate (#2)
* Deletion, revocation and renewal of secrets (#3)
* Token creation (#4)
* AppRole auth backend supported (#5)
### Improvements
* Support for complex secrets
### Deprecation
* App-ID backend marked as deprecated
## 0.3.0 (2016-10-07)
### Features
* Retrieval of JSON objects (#1)
### Test
* Tested against Vault 0.6.2
## 0.2.0 (2016-09-01)
### Improvements
* Dependecies updated and CommonsIO removed
* [fix] Fixed auth backend detection for Vault 0.6.1
* [test] Tested against Vault 0.6.1
## 0.1.1 [2016-06-20]
* [fix] Check for "permission denied" without status code 400 instead of 403
* [test] Tested against Vault 0.6.0
### Fixes
* Fixed auth backend detection for Vault 0.6.1
## 0.1.0 [2016-03-29]
### Test
* Tested against Vault 0.6.1
## 0.1.1 (2016-06-20)
### Fixes
* Check for "permission denied" without status code 400 instead of 403
### Test
* Tested against Vault 0.6.0
## 0.1.0 (2016-03-29)
* First release

View File

@ -1,9 +1,9 @@
# Java Vault Connector
[![Build Status](https://travis-ci.org/stklcode/jvaultconnector.svg?branch=master)](https://travis-ci.org/stklcode/jvaultconnector)
[![Quality Gate](https://sonarcloud.io/api/badges/gate?key=de.stklcode.jvault%3Aconnector)](https://sonarcloud.io/dashboard?id=de.stklcode.jvault%3Aconnector)
[![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%3Aconnector)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/master/LICENSE.txt)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.jvault/connector.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.jvault%22%20AND%20a%3A%22connector%22)
[![Maven Central](https://img.shields.io/maven-central/v/de.stklcode.jvault/jvault-connector.svg)](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22de.stklcode.jvault%22%20AND%20a%3A%22jvault-connector%22)
![Logo](https://raw.githubusercontent.com/stklcode/jvaultconnector/master/assets/logo.png)
@ -17,8 +17,8 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Authorization methods
* Token
* Username/Password
* AppID (register and authenticate) [_deprecated_]
* AppRole (register and authenticate)
* AppID (register and authenticate) [_deprecated_]
* Tokens
* Creation and lookup of tokens
* TokenBuilder for speaking creation of complex configuraitons
@ -30,16 +30,17 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* Renew/revoke leases
* Raw secret content or JSON decoding
* SQL secret handling
* KV v1 and v2 support
* Connector Factory with builder pattern
* Tested against Vault 0.8.1
* Tested against Vault 1.2.2
## Maven Artifact
```
```xml
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.2</version>
<artifactId>jvault-connector</artifactId>
<version>0.8.1</version>
</dependency>
```
@ -49,21 +50,21 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
```java
// Instantiate using builder pattern style factory (TLS enabled by default)
VaultConnector vault = VaultConnectorFactory.httpFactory()
VaultConnector vault = VaultConnectorBuilder.http()
.withHost("127.0.0.1")
.withPort(8200)
.withTLS()
.build();
// Instantiate with custom SSL context
VaultConnector vault = VaultConnectorFactory.httpFactory()
VaultConnector vault = VaultConnectorBuilder.http()
.withHost("example.com")
.withPort(8200)
.withTrustedCA(Paths.get("/path/to/CA.pem"))
.build();
// Initialization from environment variables
VaultConnector vault = VaultConnectorFactory.httpFactory()
VaultConnector vault = VaultConnectorBuilder.http()
.fromEnv()
.build();
```
@ -71,33 +72,33 @@ VaultConnector vault = VaultConnectorFactory.httpFactory()
### Authentication
```java
// Authenticate with token
// Authenticate with token.
vault.authToken("01234567-89ab-cdef-0123-456789abcdef");
// Authenticate with username and password
// Authenticate with username and password.
vault.authUserPass("username", "p4ssw0rd");
// Authenticate with AppID (secret - 2nd argument - is optional)
vault.authAppId("01234567-89ab-cdef-0123-456789abcdef", "fedcba98-7654-3210-fedc-ba9876543210");
// Authenticate with AppRole (secret - 2nd argument - is optional).
vault.authAppRole("01234567-89ab-cdef-0123-456789abcdef", "fedcba98-7654-3210-fedc-ba9876543210");
```
### Secret read & write
```java
// Retrieve secret (prefix "secret/" assumed, use read() to read arbitrary paths)
String secret = vault.readSecret("some/secret/key").getValue();
String secret = vault.readSecret("some/secret/key").get("value", String.class);
// Complex secret
// Complex secret.
Map<String, Object> secretData = vault.readSecret("another/secret/key").getData();
// Write simple secret
// Write simple secret.
vault.writeSecret("new/secret/key", "secret value");
// Write complex data to arbitraty path
Map<String, Object> map = [...]
// Write complex data to arbitraty path.
Map<String, Object> map = ...;
vault.write("any/path/to/write", map);
// Delete secret
// Delete secret.
vault.delete("any/path/to/write");
```
@ -105,10 +106,11 @@ vault.delete("any/path/to/write");
```java
// Create token using TokenBuilder
Token token = new TokenBuilder().withId("token id")
.withDisplayName("new test token")
.withPolicies("pol1", "pol2")
.build();
Token token = Token.builder()
.withId("token id")
.withDisplayName("new test token")
.withPolicies("pol1", "pol2")
.build();
vault.createToken(token);
// Create AppRole credentials
@ -122,11 +124,6 @@ AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
[JavaDoc API](http://jvault.stklcode.de/apidocs/)
## Planned features
* Creation and modification of policies
* Implement more authentication methods
## License
The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -2,11 +2,11 @@
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128">
<path d="M4,12 l60,104 l60,-104 z" stroke="none" fill="#000000" />
<circle cx="78" cy="24" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="38" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="52" r="6" stroke="none" fill="#00a9c7" />
<circle cx="78" cy="66" r="6" stroke="none" fill="#00a9c7" />
<circle cx="72" cy="78" r="6" stroke="none" fill="#00a9c7" />
<circle cx="58" cy="78" r="6" stroke="none" fill="#00a9c7" />
<circle cx="52" cy="66" r="6" stroke="none" fill="#00a9c7" />
</svg>
<rect x="74" y="20" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="74" y="34" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="74" y="48" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="74" y="62" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="68" y="74" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="54" y="74" width="8" height="8" stroke="none" fill="#00abe0" />
<rect x="48" y="62" width="8" height="8" stroke="none" fill="#00abe0" />
</svg>

Before

Width:  |  Height:  |  Size: 759 B

After

Width:  |  Height:  |  Size: 837 B

176
pom.xml
View File

@ -3,14 +3,15 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.stklcode.jvault</groupId>
<artifactId>connector</artifactId>
<version>0.6.2</version>
<artifactId>jvault-connector</artifactId>
<version>0.8.1</version>
<packaging>jar</packaging>
<name>jVaultConnector</name>
<description>Connector artifact for Hashicorp's Vault secret management</description>
<url>https://jvault.stklcode.de</url>
<inceptionYear>2016</inceptionYear>
<licenses>
<license>
@ -24,46 +25,110 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<developers>
<developer>
<name>Stefan Kalscheuer</name>
<email>stefan@stklcode.de</email>
<timezone>+1</timezone>
</developer>
</developers>
<scm>
<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>
</scm>
<issueManagement>
<system>GitHub Issues</system>
<url>https://github.com/stklcode/jvaultconnector/issues</url>
</issueManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<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>2.5.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<reuseForks>false</reuseForks>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.6</version>
<version>4.4.11</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
<version>4.5.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
<version>2.9.9</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
<version>2.9.9.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
@ -75,8 +140,97 @@
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.16.1</version>
<version>1.17.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>sources</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>javadoc</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<source>1.8</source>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>offline-tests</id>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludedGroups>online</excludedGroups>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
</profiles>
</project>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,11 @@ import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import java.util.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Vault Connector interface.
@ -30,7 +34,10 @@ import java.util.*;
* @author Stefan Kalscheuer
* @since 0.1
*/
public interface VaultConnector extends AutoCloseable {
public interface VaultConnector extends AutoCloseable, Serializable {
/**
* Default sub-path for Vault secrets.
*/
String PATH_SECRET = "secret";
/**
@ -42,35 +49,47 @@ public interface VaultConnector extends AutoCloseable {
* Retrieve status of vault seal.
*
* @return Seal status
* @throws VaultConnectorException on error
*/
SealResponse sealStatus();
SealResponse sealStatus() throws VaultConnectorException;
/**
* Seal vault.
*
* @return TRUE on success
* @throws VaultConnectorException on error
*/
boolean seal();
void seal() throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @param reset Discard previously provided keys (optional)
* @return TRUE on success
* @return Response with seal status
* @throws VaultConnectorException on error
*/
SealResponse unseal(final String key, final Boolean reset);
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
/**
* Unseal vault.
*
* @param key A single master share key
* @return TRUE on success
* @return Response with seal status
* @throws VaultConnectorException on error
*/
default SealResponse unseal(final String key) {
default SealResponse unseal(final String key) throws VaultConnectorException {
return unseal(key, null);
}
/**
* Query server health information.
*
* @return Health information.
* @throws VaultConnectorException on error
* @since 0.7.0
*/
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all availale authentication backends.
*
@ -103,7 +122,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param appID The App ID
* @param userID The User ID
* @return TRUE on success
* @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.
*/
@ -114,7 +133,7 @@ public interface VaultConnector extends AutoCloseable {
* Authorize to Vault using AppRole method without secret ID.
*
* @param roleID The role ID
* @return TRUE on success
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -127,7 +146,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleID The role ID
* @param secretID The secret ID
* @return TRUE on success
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -139,7 +158,7 @@ public interface VaultConnector extends AutoCloseable {
* @param appID The unique App-ID
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @return TRUE on success
* @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.
*/
@ -151,7 +170,7 @@ public interface VaultConnector extends AutoCloseable {
* Register a new AppRole role from given metamodel.
*
* @param role The role
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -161,7 +180,7 @@ public interface VaultConnector extends AutoCloseable {
* Register new AppRole role with default policy.
*
* @param roleName The role name
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -174,7 +193,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param policies The policies to associate with
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -187,7 +206,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param roleID A custom role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -201,7 +220,7 @@ public interface VaultConnector extends AutoCloseable {
* @param roleName The role name
* @param policies The policies to associate with
* @param roleID A custom role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -214,7 +233,7 @@ public interface VaultConnector extends AutoCloseable {
* Delete AppRole role from Vault.
*
* @param roleName The role anme
* @return TRUE on succevss
* @return {@code true} on succevss
* @throws VaultConnectorException on error
*/
boolean deleteAppRole(final String roleName) throws VaultConnectorException;
@ -244,7 +263,7 @@ public interface VaultConnector extends AutoCloseable {
*
* @param roleName The role name
* @param roleID The role ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.4.0
*/
@ -319,19 +338,6 @@ public interface VaultConnector extends AutoCloseable {
*/
List<String> listAppRoles() throws VaultConnectorException;
/**
* List existing (accessible) secret IDs for AppRole role.
*
* @param roleName The role name
* @return List of roles
* @throws VaultConnectorException on error
* @deprecated Use {@link #listAppRoleSecrets(String)}}. Will be removed in 0.7.0!
*/
@Deprecated
default List<String> listAppRoleSecretss(final String roleName) throws VaultConnectorException {
return listAppRoleSecrets(roleName);
}
/**
* List existing (accessible) secret IDs for AppRole role.
*
@ -346,10 +352,10 @@ public interface VaultConnector extends AutoCloseable {
*
* @param appID The App-ID
* @param userID The User-ID
* @return TRUE on success
* @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.
* Consider using {@link #createAppRoleSecret} instead.
*/
@Deprecated
boolean registerUserId(final String appID, final String userID) throws VaultConnectorException;
@ -361,7 +367,7 @@ public interface VaultConnector extends AutoCloseable {
* @param policy The policy to associate with
* @param displayName Arbitrary name to display
* @param userID The User-ID
* @return TRUE on success
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
*/
@ -392,7 +398,8 @@ public interface VaultConnector extends AutoCloseable {
/**
* Retrieve secret from Vault.
* Prefix "secret/" is automatically added to key.
* <br>
* Prefix {@code secret/} is automatically added to key.
*
* @param key Secret identifier
* @return Secret response
@ -402,6 +409,176 @@ public interface VaultConnector extends AutoCloseable {
return read(PATH_SECRET + "/" + key);
}
/**
* Retrieve the latest secret data for specific version from Vault.
* <br>
* Prefix "secret/data" is automatically added to key.
* Only available for KV v2 secrets.
*
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readSecretData(final String key) throws VaultConnectorException {
return readSecretVersion(key, null);
}
/**
* Retrieve the latest secret data for specific version from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readSecretData(final String mount, final String key) throws VaultConnectorException {
return readSecretVersion(mount, key, null);
}
/**
* Write secret to Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 secrets.
*
* @param key Secret identifier.
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeSecretData(final String key, final Map<String, Object> data) throws VaultConnectorException {
return writeSecretData(PATH_SECRET, key, data, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data) throws VaultConnectorException {
return writeSecretData(mount, key, data, null);
}
/**
* Write secret to Vault.
* <br>
* Path {@code <mount>/data/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data, final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret response
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretResponse readSecretVersion(final String key, final Integer version) throws VaultConnectorException {
return readSecretVersion(PATH_SECRET, key, version);
}
/**
* Retrieve secret data from Vault.
* <br>
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret responsef
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
* Path {@code secret/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
* @since 0.8
*/
default MetadataResponse readSecretMetadata(final String key) throws VaultConnectorException {
return readSecretMetadata(PATH_SECRET, key);
}
/**
* Update secret metadata.
* <br>
* Path {@code secret/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void updateSecretMetadata(final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException {
updateSecretMetadata(PATH_SECRET, key, maxVersions, casRequired);
}
/**
* Retrieve secret metadata from Vault.
* <br>
* Path {@code <mount>/metadata/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
* @since 0.8
*/
MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException;
/**
* Update secret metadata.
* <br>
* Path {@code <mount>/metadata/<key>} is written here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException;
/**
* List available nodes from Vault.
*
@ -414,7 +591,8 @@ public interface VaultConnector extends AutoCloseable {
/**
* List available secrets from Vault.
* Prefix "secret/" is automatically added to path.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param path Root path to search
* @return List of secret keys
@ -446,11 +624,25 @@ public interface VaultConnector extends AutoCloseable {
* @throws VaultConnectorException on error
* @since 0.5.0
*/
void write(final String key, final Map<String, Object> data) throws VaultConnectorException;
default void write(final String key, final Map<String, Object> data) throws VaultConnectorException {
write(key, data, null);
}
/**
* Write value to Vault.
*
* @param key Secret path
* @param data Secret content. Value must be be JSON serializable.
* @param options Secret options (optional).
* @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;
/**
* Write secret to Vault.
* Prefix "secret/" is automatically added to path.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @param value Secret value
@ -464,7 +656,8 @@ public interface VaultConnector extends AutoCloseable {
/**
* Write secret to Vault.
* Prefix "secret/" is automatically added to path.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @param data Secret content. Value must be be JSON serializable.
@ -472,8 +665,9 @@ public interface VaultConnector extends AutoCloseable {
* @since 0.5.0
*/
default void writeSecret(final String key, final Map<String, Object> data) throws VaultConnectorException {
if (key == null || key.isEmpty())
if (key == null || key.isEmpty()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
write(PATH_SECRET + "/" + key, data);
}
@ -488,7 +682,8 @@ public interface VaultConnector extends AutoCloseable {
/**
* Delete secret from Vault.
* Prefix "secret/" is automatically added to path.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @throws VaultConnectorException on error
@ -497,6 +692,135 @@ public interface VaultConnector extends AutoCloseable {
delete(PATH_SECRET + "/" + key);
}
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path. Only available for KV v2 stores.
*
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void deleteLatestSecretVersion(final String key) throws VaultConnectorException {
deleteLatestSecretVersion(PATH_SECRET, key);
}
/**
* Delete latest version of a secret from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteLatestSecretVersion(final String mount, final String key) throws VaultConnectorException;
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 stores.
*
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void deleteAllSecretVersions(final String key) throws VaultConnectorException {
deleteAllSecretVersions(PATH_SECRET, key);
}
/**
* Delete latest version of a secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
* Only available for KV v2 stores.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteAllSecretVersions(final String mount, final String key) throws VaultConnectorException;
/**
* Delete secret versions from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void deleteSecretVersions(final String key, final int... versions) throws VaultConnectorException {
deleteSecretVersions(PATH_SECRET, key, versions);
}
/**
* Delete secret versions from Vault.
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void deleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void undeleteSecretVersions(final String key, final int... versions) throws VaultConnectorException {
undeleteSecretVersions(PATH_SECRET, key, versions);
}
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
void undeleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
default void destroySecretVersions(final String key, final int... versions) throws VaultConnectorException {
destroySecretVersions(PATH_SECRET, key, versions);
}
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
void destroySecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException;
/**
* Revoke given lease immediately.
*
@ -564,10 +888,12 @@ public interface VaultConnector extends AutoCloseable {
*/
TokenResponse lookupToken(final String token) throws VaultConnectorException;
/**
* Read credentials for MySQL backend at default mount point.
*
* @param role the role name
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
@ -579,7 +905,7 @@ public interface VaultConnector extends AutoCloseable {
/**
* Read credentials for PostgreSQL backend at default mount point.
*
* @param role the role name
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
@ -591,7 +917,7 @@ public interface VaultConnector extends AutoCloseable {
/**
* Read credentials for MSSQL backend at default mount point.
*
* @param role the role name
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0
@ -603,7 +929,7 @@ public interface VaultConnector extends AutoCloseable {
/**
* Read credentials for MSSQL backend at default mount point.
*
* @param role the role name
* @param role the role name
* @return the credentials response
* @throws VaultConnectorException on error
* @since 0.5.0

View File

@ -0,0 +1,298 @@
/*
* Copyright 2016-2019 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.builder;
import de.stklcode.jvault.connector.HTTPVaultConnector;
import de.stklcode.jvault.connector.exception.ConnectionException;
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
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;
/**
* Vault Connector Factory implementation for HTTP Vault connectors.
*
* @author Stefan Kalscheuer
* @since 0.8.0
*/
public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
private static final String ENV_VAULT_ADDR = "VAULT_ADDR";
private static final String ENV_VAULT_CACERT = "VAULT_CACERT";
private static final String ENV_VAULT_TOKEN = "VAULT_TOKEN";
private static final String ENV_VAULT_MAX_RETRIES = "VAULT_MAX_RETRIES";
public static final String DEFAULT_HOST = "127.0.0.1";
public static final Integer DEFAULT_PORT = 8200;
public static final boolean DEFAULT_TLS = true;
public static final String DEFAULT_TLS_VERSION = "TLSv1.2";
public static final String DEFAULT_PREFIX = "/v1/";
public static final int DEFAULT_NUMBER_OF_RETRIES = 0;
private String host;
private Integer port;
private boolean tls;
private String tlsVersion;
private String prefix;
private X509Certificate trustedCA;
private int numberOfRetries;
private Integer timeout;
private String token;
/**
* Default empty constructor.
* Initializes factory with default values.
*/
public HTTPVaultConnectorBuilder() {
host = DEFAULT_HOST;
port = DEFAULT_PORT;
tls = DEFAULT_TLS;
tlsVersion = DEFAULT_TLS_VERSION;
prefix = DEFAULT_PREFIX;
numberOfRetries = DEFAULT_NUMBER_OF_RETRIES;
}
/**
* Set hostname (default: 127.0.0.1).
*
* @param host Hostname or IP address
* @return self
*/
public HTTPVaultConnectorBuilder withHost(final String host) {
this.host = host;
return this;
}
/**
* Set port (default: 8200).
*
* @param port Vault TCP port
* @return self
*/
public HTTPVaultConnectorBuilder withPort(final Integer port) {
this.port = port;
return this;
}
/**
* Set TLS usage (default: TRUE).
*
* @param useTLS use TLS or not
* @return self
*/
public HTTPVaultConnectorBuilder withTLS(final boolean useTLS) {
this.tls = useTLS;
return this;
}
/**
* Set TLS usage (default: TRUE).
*
* @param useTLS Use TLS or not.
* @param version Supported TLS version ({@code TLSv1.2}, {@code TLSv1.1}, {@code TLSv1.0}, {@code TLS}).
* @return self
* @since 0.8 Added version parameter (#22).
*/
public HTTPVaultConnectorBuilder withTLS(final boolean useTLS, final String version) {
this.tls = useTLS;
this.tlsVersion = version;
return this;
}
/**
* Convenience Method for TLS usage (enabled by default).
*
* @param version Supported TLS version ({@code TLSv1.2}, {@code TLSv1.1}, {@code TLSv1.0}, {@code TLS}).
* @return self
* @since 0.8 Added version parameter (#22).
*/
public HTTPVaultConnectorBuilder withTLS(final String version) {
return withTLS(true, version);
}
/**
* Convenience Method for TLS usage (enabled by default).
*
* @return self
*/
public HTTPVaultConnectorBuilder withTLS() {
return withTLS(true);
}
/**
* Convenience Method for NOT using TLS.
*
* @return self
*/
public HTTPVaultConnectorBuilder withoutTLS() {
return withTLS(false);
}
/**
* Set API prefix. Default is "/v1/" and changes should not be necessary for current state of development.
*
* @param prefix Vault API prefix (default: "/v1/"
* @return self
*/
public HTTPVaultConnectorBuilder withPrefix(final String prefix) {
this.prefix = prefix;
return this;
}
/**
* Add a trusted CA certifiate for HTTPS connections.
*
* @param cert path to certificate file
* @return self
* @throws VaultConnectorException on error
* @since 0.4.0
*/
public HTTPVaultConnectorBuilder withTrustedCA(final Path cert) throws VaultConnectorException {
if (cert != null) {
return withTrustedCA(certificateFromFile(cert));
} else {
this.trustedCA = null;
}
return this;
}
/**
* Add a trusted CA certifiate for HTTPS connections.
*
* @param cert path to certificate file
* @return self
* @since 0.8.0
*/
public HTTPVaultConnectorBuilder withTrustedCA(final X509Certificate cert) {
this.trustedCA = cert;
return this;
}
/**
* Set token for automatic authentication, using {@link #buildAndAuth()}.
*
* @param token Vault token
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorBuilder withToken(final String token) {
this.token = token;
return this;
}
/**
* 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
* @since 0.6.0
*/
public HTTPVaultConnectorBuilder fromEnv() throws VaultConnectorException {
/* Parse URL from environment variable */
if (System.getenv(ENV_VAULT_ADDR) != null && !System.getenv(ENV_VAULT_ADDR).trim().isEmpty()) {
try {
URL 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);
}
}
/* Read number of retries */
if (System.getenv(ENV_VAULT_MAX_RETRIES) != null) {
try {
numberOfRetries = Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES));
} catch (NumberFormatException ignored) {
/* Ignore malformed values. */
}
}
/* Read token */
token = System.getenv(ENV_VAULT_TOKEN);
/* Parse certificate, if set */
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;
}
/**
* Define the number of retries to attempt on 5xx errors.
*
* @param numberOfRetries The number of retries to attempt on 5xx errors (default: 0)
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorBuilder withNumberOfRetries(final int numberOfRetries) {
this.numberOfRetries = numberOfRetries;
return this;
}
/**
* Define a custom timeout for the HTTP connection.
*
* @param milliseconds Timeout value in milliseconds.
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorBuilder withTimeout(final int milliseconds) {
this.timeout = milliseconds;
return this;
}
@Override
public HTTPVaultConnector build() {
return new HTTPVaultConnector(host, tls, tlsVersion, port, prefix, trustedCA, numberOfRetries, timeout);
}
@Override
public HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
if (token == null) {
throw new ConnectionException("No vault token provided, unable to authenticate.");
}
HTTPVaultConnector con = build();
con.authToken(token);
return con;
}
/**
* Read given certificate file to X.509 certificate.
*
* @param certFile Path to certificate file
* @return X.509 Certificate object
* @throws TlsException on error
* @since 0.4.0
*/
private X509Certificate certificateFromFile(final Path certFile) throws TlsException {
try (InputStream is = Files.newInputStream(certFile)) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
} catch (IOException | CertificateException e) {
throw new TlsException("Unable to read certificate.", e);
}
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2016-2019 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.builder;
import de.stklcode.jvault.connector.VaultConnector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
/**
* Abstract Vault Connector Builder interface.
* Provides builder style for Vault connectors.
*
* @author Stefan Kalscheuer
* @since 0.8.0
*/
public interface VaultConnectorBuilder {
/**
* Get Factory implementation for HTTP Vault Connector.
*
* @return HTTP Connector Factory
*/
static HTTPVaultConnectorBuilder http() {
return new HTTPVaultConnectorBuilder();
}
/**
* Build command, produces connector after initialization.
*
* @return Vault Connector instance.
*/
VaultConnector build();
/**
* Build connector and authenticate with token set in factory or from environment.
*
* @return Authenticated Vault connector instance.
* @throws VaultConnectorException if authentication failed
* @since 0.6.0
*/
VaultConnector buildAndAuth() throws VaultConnectorException;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2016-2019 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.
*/
/**
* This package contains the {@link de.stklcode.jvault.connector.builder.VaultConnectorBuilder} to initialize a
* connector instance.
*/
package de.stklcode.jvault.connector.builder;

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified detail message.
*
* @param message the detail message
* @param message The detail message
*/
public InvalidResponseException(final String message) {
super(message);
@ -49,7 +49,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified cause.
*
* @param cause the cause
* @param cause The cause
*/
public InvalidResponseException(final Throwable cause) {
super(cause);
@ -60,8 +60,8 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Constructs a new exception with the specified detail message and cause.
*
* @param message the detail message
* @param cause the cause
* @param message The detail message
* @param cause The cause
*/
public InvalidResponseException(final String message, final Throwable cause) {
super(message, cause);
@ -74,8 +74,8 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode) {
@ -89,9 +89,9 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param cause the cause
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param cause The cause
* @since 0.6.2
*/
public InvalidResponseException(final String message, final Integer statusCode, final Throwable cause) {
@ -103,8 +103,8 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param response HTTP response string
* @since 0.6.2
*/
@ -121,10 +121,10 @@ public final class InvalidResponseException extends VaultConnectorException {
* <p>
* The HTTP status code can be retrieved by {@link #getStatusCode()} later.
*
* @param message the detail message
* @param statusCode status code of the HTTP response
* @param message The detail message
* @param statusCode Status code of the HTTP response
* @param response HTTP response string
* @param cause the cause
* @param cause The cause
* @since 0.6.2
*/
public InvalidResponseException(final String message,
@ -139,7 +139,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Specify the HTTP status code. Can be retrieved by {@link #getStatusCode()} later.
*
* @param statusCode the status code
* @param statusCode The status code
* @return self
* @deprecated as of 0.6.2, use constructor with status code argument instead
*/
@ -151,7 +151,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Specify the response string. Can be retrieved by {@link #getResponse()} later.
*
* @param response response text
* @param response Response text
* @return self
* @deprecated use constructor with response argument instead
*/
@ -163,7 +163,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Retrieve the HTTP status code.
*
* @return the status code or {@code null} if none specified.
* @return The status code or {@code null} if none specified.
*/
public Integer getStatusCode() {
return statusCode;
@ -172,7 +172,7 @@ public final class InvalidResponseException extends VaultConnectorException {
/**
* Retrieve the response text.
*
* @return the response text or {@code null} if none specified.
* @return The response text or {@code null} if none specified.
*/
public String getResponse() {
return response;

View File

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

View File

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

View File

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

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016-2019 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.
*/
/**
* Some custom exceptions for error handling.
*/
package de.stklcode.jvault.connector.exception;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,23 +17,11 @@
package de.stklcode.jvault.connector.factory;
import de.stklcode.jvault.connector.HTTPVaultConnector;
import de.stklcode.jvault.connector.exception.ConnectionException;
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.builder.HTTPVaultConnectorBuilder;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
/**
@ -41,38 +29,19 @@ import java.security.cert.X509Certificate;
*
* @author Stefan Kalscheuer
* @since 0.1
* @deprecated As of 0.8.0 please refer to {@link de.stklcode.jvault.connector.builder.HTTPVaultConnectorBuilder} with identical API.
*/
@Deprecated
public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
private static final String ENV_VAULT_ADDR = "VAULT_ADDR";
private static final String ENV_VAULT_CACERT = "VAULT_CACERT";
private static final String ENV_VAULT_TOKEN = "VAULT_TOKEN";
private static final String ENV_VAULT_MAX_RETRIES = "VAULT_MAX_RETRIES";
public static final String DEFAULT_HOST = "127.0.0.1";
public static final Integer DEFAULT_PORT = 8200;
public static final boolean DEFAULT_TLS = true;
public static final String DEFAULT_PREFIX = "/v1/";
public static final int DEFAULT_NUMBER_OF_RETRIES = 0;
private String host;
private Integer port;
private boolean tls;
private String prefix;
private SSLContext sslContext;
private int numberOfRetries;
private Integer timeout;
private String token;
private final HTTPVaultConnectorBuilder delegate;
/**
* Default empty constructor.
* Initializes factory with default values.
*/
public HTTPVaultConnectorFactory() {
host = DEFAULT_HOST;
port = DEFAULT_PORT;
tls = DEFAULT_TLS;
prefix = DEFAULT_PREFIX;
numberOfRetries = DEFAULT_NUMBER_OF_RETRIES;
delegate = new HTTPVaultConnectorBuilder();
}
/**
@ -82,7 +51,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @return self
*/
public HTTPVaultConnectorFactory withHost(final String host) {
this.host = host;
delegate.withHost(host);
return this;
}
@ -93,7 +62,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @return self
*/
public HTTPVaultConnectorFactory withPort(final Integer port) {
this.port = port;
delegate.withPort(port);
return this;
}
@ -104,7 +73,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @return self
*/
public HTTPVaultConnectorFactory withTLS(final boolean useTLS) {
this.tls = useTLS;
delegate.withTLS(useTLS);
return this;
}
@ -133,7 +102,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @return self
*/
public HTTPVaultConnectorFactory withPrefix(final String prefix) {
this.prefix = prefix;
delegate.withPrefix(prefix);
return this;
}
@ -146,8 +115,19 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @since 0.4.0
*/
public HTTPVaultConnectorFactory withTrustedCA(final Path cert) throws VaultConnectorException {
if (cert != null)
return withSslContext(createSslContext(cert));
delegate.withTrustedCA(cert);
return this;
}
/**
* Add a trusted CA certifiate for HTTPS connections.
*
* @param cert path to certificate file
* @return self
* @since 0.8.0
*/
public HTTPVaultConnectorFactory withTrustedCA(final X509Certificate cert) {
delegate.withTrustedCA(cert);
return this;
}
@ -158,10 +138,10 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @param sslContext the SSL context
* @return self
* @since 0.4.0
* @deprecated As of 0.8.0 this is no longer supported, please use {@link #withTrustedCA(Path)} or {@link #withTrustedCA(X509Certificate)}.
*/
public HTTPVaultConnectorFactory withSslContext(final SSLContext sslContext) {
this.sslContext = sslContext;
return this;
throw new UnsupportedOperationException("Use of deprecated method, please switch to withTrustedCA()");
}
/**
@ -172,7 +152,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withToken(final String token) {
this.token = token;
delegate.withToken(token);
return this;
}
@ -184,33 +164,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @since 0.6.0
*/
public HTTPVaultConnectorFactory fromEnv() throws VaultConnectorException {
/* Parse URL from environment variable */
if (System.getenv(ENV_VAULT_ADDR) != null && !System.getenv(ENV_VAULT_ADDR).trim().isEmpty()) {
try {
URL 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);
}
}
/* Read number of retries */
if (System.getenv(ENV_VAULT_MAX_RETRIES) != null) {
try {
numberOfRetries = Integer.parseInt(System.getenv(ENV_VAULT_MAX_RETRIES));
} catch (NumberFormatException ignored) {
}
}
/* Read token */
token = System.getenv(ENV_VAULT_TOKEN);
/* Parse certificate, if set */
if (System.getenv(ENV_VAULT_CACERT) != null && !System.getenv(ENV_VAULT_CACERT).trim().isEmpty()) {
return withTrustedCA(Paths.get(System.getenv(ENV_VAULT_CACERT)));
}
delegate.fromEnv();
return this;
}
@ -222,7 +176,7 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withNumberOfRetries(final int numberOfRetries) {
this.numberOfRetries = numberOfRetries;
delegate.withNumberOfRetries(numberOfRetries);
return this;
}
@ -234,78 +188,17 @@ public final class HTTPVaultConnectorFactory extends VaultConnectorFactory {
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withTimeout(final int milliseconds) {
this.timeout = milliseconds;
delegate.withTimeout(milliseconds);
return this;
}
@Override
public HTTPVaultConnector build() {
return new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
return delegate.build();
}
@Override
public HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
if (token == null)
throw new ConnectionException("No vault token provided, unable to authenticate.");
HTTPVaultConnector con = new HTTPVaultConnector(host, tls, port, prefix, sslContext, numberOfRetries, timeout);
con.authToken(token);
return con;
}
/**
* Create SSL Context trusting only provided certificate.
*
* @param trustedCert Path to trusted CA certificate
* @return SSL context
* @throws TlsException on errors
* @since 0.4.0
*/
private SSLContext createSslContext(final Path trustedCert) throws TlsException {
try {
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, createTrustManager(trustedCert), new SecureRandom());
return context;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new TlsException("Unable to intialize SSLContext", e);
}
}
/**
* Create a custom TrustManager for given CA certificate file.
*
* @param trustedCert Path to trusted CA certificate
* @return TrustManger
* @throws TlsException on error
* @since 0.4.0
*/
private TrustManager[] createTrustManager(final Path trustedCert) throws TlsException {
try {
/* Create Keystore with trusted certificate */
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("trustedCert", certificateFromFile(trustedCert));
/* Initialize TrustManager */
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
return tmf.getTrustManagers();
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e) {
throw new TlsException("Unable to initialize TrustManager", e);
}
}
/**
* Read given certificate file to X.509 certificate.
*
* @param certFile Path to certificate file
* @return X.509 Certificate object
* @throws TlsException on error
* @since 0.4.0
*/
private X509Certificate certificateFromFile(final Path certFile) throws TlsException {
try (InputStream is = Files.newInputStream(certFile)) {
return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
} catch (IOException | CertificateException e) {
throw new TlsException("Unable to read certificate.", e);
}
return delegate.buildAndAuth();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,8 +16,7 @@
package de.stklcode.jvault.connector.factory;
import de.stklcode.jvault.connector.VaultConnector;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.builder.VaultConnectorBuilder;
/**
* Abstract Vault Connector Factory interface.
@ -25,30 +24,19 @@ import de.stklcode.jvault.connector.exception.VaultConnectorException;
*
* @author Stefan Kalscheuer
* @since 0.1
* @deprecated As of 0.8.0 please refer to {@link VaultConnectorBuilder} with identical API.
*/
public abstract class VaultConnectorFactory {
@Deprecated
public abstract class VaultConnectorFactory implements VaultConnectorBuilder {
/**
* Get Factory implementation for HTTP Vault Connector.
*
* @return HTTP Connector Factory
* @deprecated As of 0.8.0 please refer to {@link VaultConnectorBuilder#http()}.
*/
@Deprecated
public static HTTPVaultConnectorFactory httpFactory() {
return new HTTPVaultConnectorFactory();
}
/**
* Build command, produces connector after initialization.
*
* @return Vault Connector instance.
*/
public abstract VaultConnector build();
/**
* Build connector and authenticate with token set in factory or from environment.
*
* @return Authenticated Vault connector instance.
* @throws VaultConnectorException if authentication failed
* @since 0.6.0
*/
public abstract VaultConnector buildAndAuth() throws VaultConnectorException;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2016-2019 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.
*/
/**
* This package contains the {@link de.stklcode.jvault.connector.factory.VaultConnectorFactory} to initialize a
* connector instance.
*
* @deprecated As of v0.8.0 please refer to {@link de.stklcode.jvault.connector.builder}.
*/
package de.stklcode.jvault.connector.factory;

View File

@ -0,0 +1,38 @@
/*
* Copyright 2016-2019 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.internal;
/**
* Utility class to bundle common error messages.
*
* @author Stefan Kalscheuer
* @since 0.8 Extracted from static inner class.
*/
final class Error {
static final String READ_RESPONSE = "Unable to read response";
static final String PARSE_RESPONSE = "Unable to parse response";
static final String UNEXPECTED_RESPONSE = "Received response where none was expected";
static final String URI_FORMAT = "Invalid URI format";
static final String RESPONSE_CODE = "Invalid response code";
static final String INIT_SSL_CONTEXT = "Unable to intialize SSLContext";
/**
* Constructor hidden, this class should not be instantiated.
*/
private Error() {
}
}

View File

@ -0,0 +1,433 @@
package de.stklcode.jvault.connector.internal;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.*;
import de.stklcode.jvault.connector.model.response.ErrorResponse;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Helper class to bundle Vault HTTP requests.
*
* @author Stefan Kalscheuer
* @since 0.8 Extracted methods from {@link de.stklcode.jvault.connector.HTTPVaultConnector}.
*/
public final class RequestHelper implements Serializable {
private static final String HEADER_VAULT_TOKEN = "X-Vault-Token";
private final String baseURL; // Base URL of Vault.
private final Integer timeout; // Timeout in milliseconds.
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 ObjectMapper jsonMapper;
/**
* Constructor of the request helper.
*
* @param baseURL The URL
* @param retries Number of retries on 5xx errors
* @param timeout Timeout for HTTP requests (milliseconds)
* @param tlsVersion TLS Version.
* @param trustedCaCert Trusted CA certificate
*/
public RequestHelper(final String baseURL,
final int retries,
final Integer timeout,
final String tlsVersion,
final X509Certificate trustedCaCert) {
this.baseURL = baseURL;
this.retries = retries;
this.timeout = timeout;
this.tlsVersion = tlsVersion;
this.trustedCaCert = trustedCaCert;
this.jsonMapper = new ObjectMapper();
}
/**
* Execute HTTP request using POST method.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8 Added {@code token} parameter.
*/
public String post(final String path, final Object payload, final String token) throws VaultConnectorException {
/* Initialize post */
HttpPost post = new HttpPost(baseURL + path);
/* generate JSON from payload */
StringEntity input;
try {
input = new StringEntity(jsonMapper.writeValueAsString(payload), StandardCharsets.UTF_8);
} catch (JsonProcessingException e) {
throw new InvalidRequestException(Error.PARSE_RESPONSE, e);
}
input.setContentEncoding("UTF-8");
input.setContentType("application/json");
post.setEntity(input);
/* Set X-Vault-Token header */
if (token != null) {
post.addHeader(HEADER_VAULT_TOKEN, token);
}
return request(post, retries);
}
/**
* Execute HTTP request using POST method and parse JSON result.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @param <T> Target type.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T post(final String path, final Object payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = post(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/**
* Execute HTTP request using POST method and expect empty (204) response.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @throws VaultConnectorException on connection error
* @since 0.8
*/
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);
}
}
/**
* Execute HTTP request using PUT method.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @return HTTP response
* @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 {
/* Initialize put */
HttpPut put = new HttpPut(baseURL + path);
/* generate JSON from payload */
StringEntity entity = null;
try {
entity = new StringEntity(jsonMapper.writeValueAsString(payload));
} catch (UnsupportedEncodingException | JsonProcessingException e) {
throw new InvalidRequestException("Payload serialization failed", e);
}
/* Parse parameters */
put.setEntity(entity);
/* Set X-Vault-Token header */
if (token != null) {
put.addHeader(HEADER_VAULT_TOKEN, token);
}
return request(put, retries);
}
/**
* Execute HTTP request using PUT method and parse JSON result.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @param <T> Target type.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T put(final String path, final Map<String, String> payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = put(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/**
* Execute HTTP request using PUT method and expect empty (204) response.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public void putWithoutResponse(final String path, final Map<String, String> payload, final String token)
throws VaultConnectorException {
if (!put(path, payload, token).isEmpty()) {
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
}
/**
* Execute HTTP request using DELETE method.
*
* @param path URL path (relative to base).
* @param token Vault token (may be {@code null}).
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8 Added {@code token} parameter.
*/
public String delete(final String path, final String token) throws VaultConnectorException {
/* Initialize delete */
HttpDelete delete = new HttpDelete(baseURL + path);
/* Set X-Vault-Token header */
if (token != null) {
delete.addHeader(HEADER_VAULT_TOKEN, token);
}
return request(delete, retries);
}
/**
* Execute HTTP request using DELETE method and expect empty (204) response.
*
* @param path URL path (relative to base).
* @param token Vault token (may be {@code null}).
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public void deleteWithoutResponse(final String path, final String token) throws VaultConnectorException {
if (!delete(path, token).isEmpty()) {
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
}
/**
* Execute HTTP request using GET method.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8 Added {@code token} parameter.
*/
public String get(final String path, final Map<String, String> payload, final String token)
throws VaultConnectorException {
HttpGet get;
try {
/* Add parameters to URI */
URIBuilder uriBuilder = new URIBuilder(baseURL + path);
payload.forEach(uriBuilder::addParameter);
/* Initialize request */
get = new HttpGet(uriBuilder.build());
} catch (URISyntaxException e) {
/* this should never occur and may leak sensible information */
throw new InvalidRequestException(Error.URI_FORMAT);
}
/* Set X-Vault-Token header */
if (token != null) {
get.addHeader(HEADER_VAULT_TOKEN, token);
}
return request(get, retries);
}
/**
* Execute HTTP request using GET method and parse JSON result to target class.
*
* @param path URL path (relative to base).
* @param payload Map of payload values (will be converted to JSON).
* @param token Vault token (may be {@code null}).
* @param target Target class.
* @param <T> Target type.
* @return HTTP response
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public <T> T get(final String path, final Map<String, String> payload, final String token, final Class<T> target)
throws VaultConnectorException {
try {
String response = get(path, payload, token);
return jsonMapper.readValue(response, target);
} catch (IOException e) {
throw new InvalidResponseException(Error.PARSE_RESPONSE, e);
}
}
/**
* Execute prepared HTTP request and return result.
*
* @param base Prepares Request
* @param retries number of retries
* @return HTTP response
* @throws VaultConnectorException on connection error
*/
private String request(final HttpRequestBase base, final int retries) throws VaultConnectorException {
/* Set JSON Header */
base.addHeader("accept", "application/json");
CloseableHttpResponse response = null;
try (CloseableHttpClient httpClient = HttpClientBuilder.create()
.setSSLSocketFactory(createSSLSocketFactory())
.build()) {
/* Set custom timeout, if defined */
if (this.timeout != null) {
base.setConfig(RequestConfig.copy(RequestConfig.DEFAULT).setConnectTimeout(timeout).build());
}
/* Execute request */
response = httpClient.execute(base);
/* Check if response is valid */
if (response == null) {
throw new InvalidResponseException("Response unavailable");
}
switch (response.getStatusLine().getStatusCode()) {
case 200:
return handleResult(response);
case 204:
return "";
case 403:
throw new PermissionDeniedException();
default:
if (response.getStatusLine().getStatusCode() >= 500
&& response.getStatusLine().getStatusCode() < 600 && retries > 0) {
/* Retry on 5xx errors */
return request(base, retries - 1);
} else {
/* Fail on different error code and/or no retries left */
handleError(response);
/* Throw exception withoud details, if response entity is empty. */
throw new InvalidResponseException(Error.RESPONSE_CODE,
response.getStatusLine().getStatusCode());
}
}
} catch (IOException e) {
throw new InvalidResponseException(Error.READ_RESPONSE, e);
} finally {
if (response != null && response.getEntity() != null) {
try {
EntityUtils.consume(response.getEntity());
} catch (IOException ignored) {
// Exception ignored.
}
}
}
}
/**
* Create a custom socket factory from trusted CA certificate.
*
* @return The factory.
* @throws TlsException An error occured during initialization of the SSL context.
* @since 0.8.0
*/
private SSLConnectionSocketFactory createSSLSocketFactory() throws TlsException {
try {
// Create Keystore with trusted certificate.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("trustedCert", trustedCaCert);
// Initialize TrustManager.
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
// Create context usint this TrustManager.
SSLContext context = SSLContext.getInstance(tlsVersion);
context.init(null, tmf.getTrustManagers(), new SecureRandom());
return new SSLConnectionSocketFactory(
context,
null,
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
);
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException e) {
throw new TlsException(Error.INIT_SSL_CONTEXT, e);
}
}
/**
* Handle successful result.
*
* @param response The raw HTTP response (assuming status code 200)
* @return Complete response body as String
* @throws InvalidResponseException on reading errors
*/
private String handleResult(final HttpResponse response) throws InvalidResponseException {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
return br.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
}
}
/**
* Handle unsuccessful response. Throw detailed exception if possible.
*
* @param response The raw HTTP response (assuming status code 5xx)
* @throws VaultConnectorException Expected exception with details to throw
*/
private void handleError(final HttpResponse response) throws VaultConnectorException {
if (response.getEntity() != null) {
try (BufferedReader br = new BufferedReader(
new InputStreamReader(response.getEntity().getContent()))) {
String responseString = br.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.getStatusLine().getStatusCode(), er.toString());
} catch (IOException ignored) {
// Exception ignored.
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +28,17 @@ import java.util.List;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRole {
/**
* Get {@link AppRoleBuilder} instance.
*
* @param name Role name.
* @return AppRole Builder.
* @since 0.8
*/
public static AppRoleBuilder builder(final String name) {
return new AppRoleBuilder(name);
}
@JsonProperty("role_name")
private String name;
@ -41,6 +52,8 @@ public final class AppRole {
private List<String> boundCidrList;
private List<String> secretIdBoundCidrs;
private List<String> policies;
@JsonProperty("secret_id_num_uses")
@ -73,24 +86,57 @@ public final class AppRole {
/**
* Construct complete {@link AppRole} object.
*
* @param name Role name (required)
* @param id Role ID (optional)
* @param bindSecretId Bind secret ID (optional)
* @param boundCidrList Whitelist of subnets in CIDR notation (optional)
* @param policies List of policies (optional)
* @param secretIdNumUses Maximum number of uses per secret (optional)
* @param secretIdTtl Maximum TTL in seconds for secrets (optional)
* @param tokenTtl Token TTL in seconds (optional)
* @param tokenMaxTtl Maximum token TTL in seconds, including renewals (optional)
* @param period Duration in seconds, if set the token is a periodic token (optional)
* @param name Role name (required)
* @param id Role ID (optional)
* @param bindSecretId Bind secret ID (optional)
* @param secretIdBoundCidrs Whitelist of subnets in CIDR notation (optional)
* @param policies List of policies (optional)
* @param secretIdNumUses Maximum number of uses per secret (optional)
* @param secretIdTtl Maximum TTL in seconds for secrets (optional)
* @param tokenTtl Token TTL in seconds (optional)
* @param tokenMaxTtl Maximum token TTL in seconds, including renewals (optional)
* @param period Duration in seconds, if set the token is a periodic token (optional)
*/
public AppRole(final String name, final String id, final Boolean bindSecretId, final List<String> boundCidrList,
public AppRole(final String name, final String id, final Boolean bindSecretId, final List<String> secretIdBoundCidrs,
final List<String> policies, final Integer secretIdNumUses, final Integer secretIdTtl,
final Integer tokenTtl, final Integer tokenMaxTtl, final Integer period) {
this.name = name;
this.id = id;
this.bindSecretId = bindSecretId;
this.secretIdBoundCidrs = secretIdBoundCidrs;
this.policies = policies;
this.secretIdNumUses = secretIdNumUses;
this.secretIdTtl = secretIdTtl;
this.tokenTtl = tokenTtl;
this.tokenMaxTtl = tokenMaxTtl;
this.period = period;
}
/**
* Construct complete {@link AppRole} object.
* <p>
* This constructor is used for transition from {@code bound_cidr_list} to {@code secret_id_bound_cidrs} only.
*
* @param name Role name (required)
* @param id Role ID (optional)
* @param bindSecretId Bind secret ID (optional)
* @param boundCidrList Whitelist of subnets in CIDR notation (optional)
* @param secretIdBoundCidrs Whitelist of subnets in CIDR notation (optional)
* @param policies List of policies (optional)
* @param secretIdNumUses Maximum number of uses per secret (optional)
* @param secretIdTtl Maximum TTL in seconds for secrets (optional)
* @param tokenTtl Token TTL in seconds (optional)
* @param tokenMaxTtl Maximum token TTL in seconds, including renewals (optional)
* @param period Duration in seconds, if set the token is a periodic token (optional)
*/
AppRole(final String name, final String id, final Boolean bindSecretId, final List<String> boundCidrList,
final List<String> secretIdBoundCidrs, final List<String> policies, final Integer secretIdNumUses,
final Integer secretIdTtl, final Integer tokenTtl, final Integer tokenMaxTtl, final Integer period) {
this.name = name;
this.id = id;
this.bindSecretId = bindSecretId;
this.boundCidrList = boundCidrList;
this.secretIdBoundCidrs = secretIdBoundCidrs;
this.policies = policies;
this.secretIdNumUses = secretIdNumUses;
this.secretIdTtl = secretIdTtl;
@ -122,14 +168,18 @@ public final class AppRole {
/**
* @return list of bound CIDR subnets
* @deprecated Use {@link #getSecretIdBoundCidrs()} instead, as this parameter is deprecated in Vault.
*/
@Deprecated
public List<String> getBoundCidrList() {
return boundCidrList;
}
/**
* @param boundCidrList list of subnets in CIDR notation to bind role to
* @deprecated Use {@link #setSecretIdBoundCidrs(List)} instead, as this parameter is deprecated in Vault.
*/
@Deprecated
@JsonSetter("bound_cidr_list")
public void setBoundCidrList(final List<String> boundCidrList) {
this.boundCidrList = boundCidrList;
@ -137,15 +187,48 @@ public final class AppRole {
/**
* @return list of subnets in CIDR notation as comma-separated {@link String}
* @deprecated Use {@link #getSecretIdBoundCidrsString()} instead, as this parameter is deprecated in Vault.
*/
@Deprecated
@JsonGetter("bound_cidr_list")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getBoundCidrListString() {
if (boundCidrList == null || boundCidrList.isEmpty())
if (boundCidrList == null || boundCidrList.isEmpty()) {
return "";
}
return String.join(",", boundCidrList);
}
/**
* @return list of bound CIDR subnets
* @since 0.8 replaces {@link #getBoundCidrList()}
*/
public List<String> getSecretIdBoundCidrs() {
return secretIdBoundCidrs;
}
/**
* @param secretIdBoundCidrs List of subnets in CIDR notation to bind secrets of this role to.
* @since 0.8 replaces {@link #setBoundCidrList(List)}
*/
@JsonSetter("secret_id_bound_cidrs")
public void setSecretIdBoundCidrs(final List<String> secretIdBoundCidrs) {
this.secretIdBoundCidrs = secretIdBoundCidrs;
}
/**
* @return List of subnets in CIDR notation as comma-separated {@link String}
* @since 0.8 replaces {@link #getBoundCidrListString()} ()}
*/
@JsonGetter("secret_id_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getSecretIdBoundCidrsString() {
if (secretIdBoundCidrs == null || secretIdBoundCidrs.isEmpty()) {
return "";
}
return String.join(",", secretIdBoundCidrs);
}
/**
* @return list of policies
*/
@ -167,8 +250,9 @@ public final class AppRole {
@JsonGetter("policies")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getPoliciesString() {
if (policies == null || policies.isEmpty())
if (policies == null || policies.isEmpty()) {
return "";
}
return String.join(",", policies);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,6 +30,7 @@ public final class AppRoleBuilder {
private String id;
private Boolean bindSecretId;
private List<String> boundCidrList;
private List<String> secretIdBoundCidrs;
private List<String> policies;
private Integer secretIdNumUses;
private Integer secretIdTtl;
@ -93,12 +94,26 @@ public final class AppRoleBuilder {
*
* @param boundCidrList List of CIDR blocks which can perform login
* @return self
* @deprecated Use {@link #withSecretIdBoundCidrs(List)} instead, as this parameter is deprecated in Vault.
*/
@Deprecated
public AppRoleBuilder withBoundCidrList(final List<String> boundCidrList) {
this.boundCidrList = boundCidrList;
return this;
}
/**
* Set bound CIDR blocks.
*
* @param secretIdBoundCidrs List of CIDR blocks which can perform login
* @return self
* @since 0.8 replaces {@link #withBoundCidrList(List)}
*/
public AppRoleBuilder withSecretIdBoundCidrs(final List<String> secretIdBoundCidrs) {
this.secretIdBoundCidrs = secretIdBoundCidrs;
return this;
}
/**
* Add a CIDR block to list of bound blocks.
*
@ -106,9 +121,15 @@ public final class AppRoleBuilder {
* @return self
*/
public AppRoleBuilder withCidrBlock(final String cidrBlock) {
if (boundCidrList == null)
if (boundCidrList == null) {
boundCidrList = new ArrayList<>();
}
boundCidrList.add(cidrBlock);
if (secretIdBoundCidrs == null) {
secretIdBoundCidrs = new ArrayList<>();
}
secretIdBoundCidrs.add(cidrBlock);
return this;
}
@ -119,8 +140,9 @@ public final class AppRoleBuilder {
* @return self
*/
public AppRoleBuilder withPolicies(final List<String> policies) {
if (this.policies == null)
if (this.policies == null) {
this.policies = new ArrayList<>();
}
this.policies.addAll(policies);
return this;
}
@ -132,8 +154,9 @@ public final class AppRoleBuilder {
* @return self
*/
public AppRoleBuilder withPolicy(final String policy) {
if (this.policies == null)
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.add(policy);
return this;
}
@ -204,6 +227,7 @@ public final class AppRoleBuilder {
id,
bindSecretId,
boundCidrList,
secretIdBoundCidrs,
policies,
secretIdNumUses,
secretIdTtl,

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -126,8 +126,9 @@ public final class AppRoleSecret {
*/
@JsonGetter("cidr_list")
public String getCidrListString() {
if (cidrList == null || cidrList.isEmpty())
if (cidrList == null || cidrList.isEmpty()) {
return "";
}
return String.join(",", cidrList);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -48,9 +48,11 @@ public enum AuthBackend {
* @return Auth backend value
*/
public static AuthBackend forType(final String type) {
for (AuthBackend v : values())
if (v.type.equalsIgnoreCase(type))
for (AuthBackend v : values()) {
if (v.type.equalsIgnoreCase(type)) {
return v;
}
}
return UNKNOWN;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -31,6 +31,16 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Token {
/**
* Get {@link TokenBuilder} instance.
*
* @return Token Builder.
* @since 0.8
*/
public static TokenBuilder builder() {
return new TokenBuilder();
}
@JsonProperty("id")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String id;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -159,8 +159,9 @@ public final class TokenBuilder {
* @return self
*/
public TokenBuilder withPolicies(final List<String> policies) {
if (this.policies == null)
if (this.policies == null) {
this.policies = new ArrayList<>();
}
this.policies.addAll(policies);
return this;
}
@ -172,8 +173,9 @@ public final class TokenBuilder {
* @return self
*/
public TokenBuilder withPolicy(final String policy) {
if (this.policies == null)
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.add(policy);
return this;
}
@ -185,8 +187,9 @@ public final class TokenBuilder {
* @return self
*/
public TokenBuilder withMeta(final Map<String, String> meta) {
if (this.meta == null)
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.putAll(meta);
return this;
}
@ -199,8 +202,9 @@ public final class TokenBuilder {
* @return self
*/
public TokenBuilder withMeta(final String key, final String value) {
if (this.meta == null)
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.put(key, value);
return this;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016-2019 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.
*/
/**
* Model classes for communication with the Vault API.
*/
package de.stklcode.jvault.connector.model;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -42,7 +42,9 @@ public final class AppRoleResponse extends VaultDataResponse {
/* null empty strings on list objects */
Map<String, Object> filteredData = new HashMap<>();
data.forEach((k, v) -> {
if (!(v instanceof String && ((String) v).isEmpty())) filteredData.put(k, v);
if (!(v instanceof String && ((String) v).isEmpty())) {
filteredData.put(k, v);
}
});
this.role = mapper.readValue(mapper.writeValueAsString(filteredData), AppRole.class);
} catch (IOException e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -42,7 +42,9 @@ public final class AppRoleSecretResponse extends VaultDataResponse {
/* null empty strings on list objects */
Map<String, Object> filteredData = new HashMap<>();
data.forEach((k, v) -> {
if (!(v instanceof String && ((String) v).isEmpty())) filteredData.put(k, v);
if (!(v instanceof String && ((String) v).isEmpty())) {
filteredData.put(k, v);
}
});
this.secret = mapper.readValue(mapper.writeValueAsString(filteredData), AppRoleSecret.class);
} catch (IOException e) {

View File

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

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,8 +32,9 @@ public final class CredentialsResponse extends SecretResponse {
*/
public String getUsername() {
Object username = get("username");
if (username != null)
if (username != null) {
return username.toString();
}
return null;
}
@ -42,8 +43,9 @@ public final class CredentialsResponse extends SecretResponse {
*/
public String getPassword() {
Object password = get("password");
if (password != null)
if (password != null) {
return password.toString();
}
return null;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,4 +38,13 @@ public final class ErrorResponse implements VaultResponse {
public List<String> getErrors() {
return errors;
}
@Override
public String toString() {
if (errors == null || errors.isEmpty()) {
return "error response";
} else {
return errors.get(0);
}
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2016-2019 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.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Vault response for health query.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class HealthResponse implements VaultResponse {
@JsonProperty("cluster_id")
private String clusterID;
@JsonProperty("cluster_name")
private String clusterName;
@JsonProperty("version")
private String version;
@JsonProperty("server_time_utc")
private Long serverTimeUTC;
@JsonProperty("standby")
private Boolean standby;
@JsonProperty("sealed")
private Boolean sealed;
@JsonProperty("initialized")
private Boolean initialized;
@JsonProperty("replication_perf_mode")
private String replicationPerfMode;
@JsonProperty("replication_dr_mode")
private String replicationDrMode;
@JsonProperty("performance_standby")
private Boolean performanceStandby;
/**
* @return The Cluster ID.
*/
public String getClusterID() {
return clusterID;
}
/**
* @return The Cluster name.
*/
public String getClusterName() {
return clusterName;
}
/**
* @return Vault version.
*/
public String getVersion() {
return version;
}
/**
* @return Server time UTC (timestamp).
*/
public Long getServerTimeUTC() {
return serverTimeUTC;
}
/**
* @return Server standby status.
*/
public Boolean isStandby() {
return standby;
}
/**
* @return Server seal status.
*/
public Boolean isSealed() {
return sealed;
}
/**
* @return Server initialization status.
*/
public Boolean isInitialized() {
return initialized;
}
/**
* @return Replication performance mode of the active node (since Vault 0.9.2).
* @since 0.8 (#21)
*/
public String getReplicationPerfMode() {
return replicationPerfMode;
}
/**
* @return Replication DR mode of the active node (since Vault 0.9.2).
* @since 0.8 (#21)
*/
public String getReplicationDrMode() {
return replicationDrMode;
}
/**
* @return Performance standby status.
* @since 0.8 (#21)
*/
public Boolean isPerformanceStandby() {
return performanceStandby;
}
}

View File

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

View File

@ -0,0 +1,56 @@
/*
* Copyright 2016-2019 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.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata;
import java.io.IOException;
import java.util.Map;
/**
* Vault response for secret metadata (KV v2).
*
* @author Stefan Kalscheuer
* @since 0.8
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MetadataResponse extends VaultDataResponse {
private SecretMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
this.metadata = mapper.readValue(mapper.writeValueAsString(data), SecretMetadata.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/**
* Get the actual metadata.
*
* @return Metadata.
*/
public SecretMetadata getMetadata() {
return metadata;
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,14 +22,20 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Vault response for seal status or unseal request.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SealResponse implements VaultResponse {
@JsonProperty("type")
private String type;
@JsonProperty("sealed")
private boolean sealed;
@JsonProperty("initialized")
private boolean initialized;
@JsonProperty("t")
private Integer threshold;
@ -39,6 +45,26 @@ public final class SealResponse implements VaultResponse {
@JsonProperty("progress")
private Integer progress;
@JsonProperty("version")
private String version;
@JsonProperty("nonce")
private String nonce;
@JsonProperty("cluster_name")
private String clusterName;
@JsonProperty("cluster_id")
private String clusterId;
/**
* @return Seal type.
* @since 0.8
*/
public String getType() {
return type;
}
/**
* @return Seal status
*/
@ -46,6 +72,14 @@ public final class SealResponse implements VaultResponse {
return sealed;
}
/**
* @return Vault initialization status (since Vault 0.11.2).
* @since 0.8
*/
public boolean isInitialized() {
return initialized;
}
/**
* @return Required threshold of secret shares
*/
@ -66,4 +100,36 @@ public final class SealResponse implements VaultResponse {
public Integer getProgress() {
return progress;
}
/**
* @return Vault version.
* @since 0.8
*/
public String getVersion() {
return version;
}
/**
* @return A random nonce.
* @since 0.8
*/
public String getNonce() {
return nonce;
}
/**
* @return Vault cluster name (only if unsealed).
* @since 0.8
*/
public String getClusterName() {
return clusterName;
}
/**
* @return Vault cluster ID (only if unsealed).
* @since 0.8
*/
public String getClusterId() {
return clusterId;
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,6 +19,7 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException;
import java.util.HashMap;
@ -32,11 +33,29 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretResponse extends VaultDataResponse {
private static final String KEY_DATA = "data";
private static final String KEY_METADATA = "metadata";
private Map<String, Object> data;
private VersionMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
this.data = data;
if (data.size() == 2
&& data.containsKey(KEY_DATA) && data.get(KEY_DATA) instanceof Map
&& data.containsKey(KEY_METADATA) && data.get(KEY_METADATA) instanceof Map) {
ObjectMapper mapper = new ObjectMapper();
try {
// This is apparently a KV v2 value.
this.data = (Map<String, Object>) data.get(KEY_DATA);
this.metadata = mapper.readValue(mapper.writeValueAsString(data.get(KEY_METADATA)), VersionMetadata.class);
} catch (ClassCastException | IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
} else {
// For KV v1 without metadata just store the data map.
this.data = data;
}
}
/**
@ -46,11 +65,22 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0
*/
public final Map<String, Object> getData() {
if (data == null)
if (data == null) {
return new HashMap<>();
}
return data;
}
/**
* Get secret metadata. This is only available for KV v2 secrets.
*
* @return Metadata of the secret.
* @since 0.8
*/
public final VersionMetadata getMetadata() {
return metadata;
}
/**
* Get a single value for given key.
*
@ -59,8 +89,9 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0
*/
public final Object get(final String key) {
if (data == null)
if (data == null) {
return null;
}
return getData().get(key);
}
@ -74,8 +105,9 @@ public class SecretResponse extends VaultDataResponse {
@Deprecated
public final String getValue() {
Object value = get("value");
if (value == null)
if (value == null) {
return null;
}
return value.toString();
}
@ -97,7 +129,7 @@ public class SecretResponse extends VaultDataResponse {
/**
* Get response parsed as JSON.
*
* @param key the key
* @param key the key
* @param type Class to parse response
* @param <T> Class to parse response
* @return Parsed object or {@code null} if absent
@ -107,8 +139,9 @@ public class SecretResponse extends VaultDataResponse {
public final <T> T get(final String key, final Class<T> type) throws InvalidResponseException {
try {
Object rawValue = get(key);
if (rawValue == null)
if (rawValue == null) {
return null;
}
return new ObjectMapper().readValue(rawValue.toString(), type);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());

View File

@ -0,0 +1,56 @@
/*
* Copyright 2016-2019 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.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException;
import java.util.Map;
/**
* Vault response for a single secret version metatada, i.e. after update (KV v2).
*
* @author Stefan Kalscheuer
* @since 0.8
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretVersionResponse extends VaultDataResponse {
private VersionMetadata metadata;
@Override
public final void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
this.metadata = mapper.readValue(mapper.writeValueAsString(data), VersionMetadata.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/**
* Get the actual metadata.
*
* @return Metadata.
*/
public VersionMetadata getMetadata() {
return metadata;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,127 @@
/*
* Copyright 2016-2019 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.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Map;
/**
* Embedded metadata for Key-Value v2 secrets.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretMetadata {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
@JsonProperty("created_time")
private String createdTimeString;
@JsonProperty("current_version")
private Integer currentVersion;
@JsonProperty("max_versions")
private Integer maxVersions;
@JsonProperty("oldest_version")
private Integer oldestVersion;
@JsonProperty("updated_time")
private String updatedTime;
@JsonProperty("versions")
private Map<Integer, VersionMetadata> versions;
/**
* @return Time of secret creation as raw string representation.
*/
public String getCreatedTimeString() {
return createdTimeString;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
* @return Current version number.
*/
public Integer getCurrentVersion() {
return currentVersion;
}
/**
* @return Maximum number of versions.
*/
public Integer getMaxVersions() {
return maxVersions;
}
/**
* @return Oldest available version number.
*/
public Integer getOldestVersion() {
return oldestVersion;
}
/**
* @return Time of secret update as raw string representation.
*/
public String getUpdatedTimeString() {
return updatedTime;
}
/**
* @return Time of secret update..
*/
public ZonedDateTime getUpdatedTime() {
if (updatedTime != null && !updatedTime.isEmpty()) {
try {
return ZonedDateTime.parse(updatedTime, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
* @return Version of the entry.
*/
public Map<Integer, VersionMetadata> getVersions() {
return versions;
}
}

View File

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

View File

@ -0,0 +1,106 @@
/*
* Copyright 2016-2019 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.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
/**
* Embedded metadata for a single Key-Value v2 version.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class VersionMetadata {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
@JsonProperty("created_time")
private String createdTimeString;
@JsonProperty("deletion_time")
private String deletionTimeString;
@JsonProperty("destroyed")
private boolean destroyed;
@JsonProperty("version")
private Integer version;
/**
* @return Time of secret creation as raw string representation.
*/
public String getCreatedTimeString() {
return createdTimeString;
}
/**
* @return Time of secret creation.
*/
public ZonedDateTime getCreatedTime() {
if (createdTimeString != null && !createdTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(createdTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
* @return Time for secret deletion as raw string representation.
*/
public String getDeletionTimeString() {
return deletionTimeString;
}
/**
* @return Time for secret deletion.
*/
public ZonedDateTime getDeletionTime() {
if (deletionTimeString != null && !deletionTimeString.isEmpty()) {
try {
return ZonedDateTime.parse(deletionTimeString, TIME_FORMAT);
} catch (DateTimeParseException e) {
// Ignore.
}
}
return null;
}
/**
* @return Whether the secret is destroyed.
*/
public boolean isDestroyed() {
return destroyed;
}
/**
* @return Version of the entry.
*/
public Integer getVersion() {
return version;
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016-2019 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.
*/
/**
* Embedded data classes for responses from the Vault API.
*/
package de.stklcode.jvault.connector.model.response.embedded;

View File

@ -0,0 +1,20 @@
/*
* Copyright 2016-2019 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.
*/
/**
* Model classes for responses from the Vault API.
*/
package de.stklcode.jvault.connector.model.response;

View File

@ -0,0 +1,21 @@
/*
* Copyright 2016-2019 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.
*/
/**
* Java Vault Connector base package - contains {@link de.stklcode.jvault.connector.VaultConnector} interface and
* default implementation.
*/
package de.stklcode.jvault.connector;

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>API Overview</title>
</head>
<body>
<p>Java Vault Connector is a connector library for Vault by Hashicorp written in Java.</p>
<p>The connector allows simple usage of Vault's secret store in own applications.</p>
<p>It features a default implementation for the HTTP(S) interface and supports various authorization methods including
AppRole, token and secret handling.</p>
</body>
</html>

View File

@ -0,0 +1,504 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.exception.PermissionDeniedException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicStatusLine;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Collections;
import static net.bytebuddy.implementation.MethodDelegation.to;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* JUnit test for HTTP Vault connector.
* This test suite contains tests that do not require connection to an actual Vault instance.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
public class HTTPVaultConnectorOfflineTest {
private static final String INVALID_URL = "foo:/\\1nv4l1d_UrL";
private static CloseableHttpClient httpMock = mock(CloseableHttpClient.class);
private CloseableHttpResponse responseMock = mock(CloseableHttpResponse.class);
@BeforeAll
public static void initByteBuddy() {
// Install ByteBuddy Agent.
ByteBuddyAgent.install();
}
/**
* Helper method for redefinition of {@link HttpClientBuilder#create()} from {@link #initHttpMock()}.
*
* @return Mocked HTTP client builder.
*/
public static HttpClientBuilder create() {
return new MockedHttpClientBuilder();
}
@BeforeEach
public void initHttpMock() {
// Redefine static method to return Mock on HttpClientBuilder creation.
new ByteBuddy().redefine(HttpClientBuilder.class)
.method(named("create"))
.intercept(to(HTTPVaultConnectorOfflineTest.class))
.make()
.load(HttpClientBuilder.class.getClassLoader(), ClassReloadingStrategy.fromInstalledAgent());
// Re-initialize HTTP mock to ensure fresh (empty) results.
httpMock = mock(CloseableHttpClient.class);
}
/**
* Test exceptions thrown during request.
*/
@Test
public void requestExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("http://127.0.0.1", null, 0, 250);
// Test invalid response code.
final int responseCode = 400;
mockResponse(responseCode, "", ContentType.APPLICATION_JSON);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid response code"));
assertThat("Unexpected status code in exception", ((InvalidResponseException) e).getStatusCode(), is(responseCode));
assertThat("Response message where none was expected", ((InvalidResponseException) e).getResponse(), is(nullValue()));
}
// Simulate permission denied response.
mockResponse(responseCode, "{\"errors\":[\"permission denied\"]}", ContentType.APPLICATION_JSON);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(PermissionDeniedException.class));
}
// Test exception thrown during request.
when(httpMock.execute(any())).thenThrow(new IOException("Test Exception"));
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to read response"));
assertThat("Unexpected cause", e.getCause(), instanceOf(IOException.class));
}
// Now simulate a failing request that succeeds on second try.
connector = new HTTPVaultConnector("https://127.0.0.1", null, 1, 250);
doReturn(responseMock).doReturn(responseMock).when(httpMock).execute(any());
doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, ""))
.doReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, ""))
.when(responseMock).getStatusLine();
when(responseMock.getEntity()).thenReturn(new StringEntity("{}", ContentType.APPLICATION_JSON));
try {
connector.getHealth();
} catch (Exception e) {
fail("Request failed unexpectedly: " + e.getMessage());
}
}
/**
* Test constductors of the {@link HTTPVaultConnector} class.
*/
@Test
public void constructorTest() throws IOException, CertificateException {
final String url = "https://vault.example.net/test/";
final String hostname = "vault.example.com";
final Integer port = 1337;
final String prefix = "/custom/prefix/";
final int retries = 42;
final String expectedNoTls = "http://" + hostname + "/v1/";
final String expectedCustomPort = "https://" + hostname + ":" + port + "/v1/";
final String expectedCustomPrefix = "https://" + hostname + ":" + port + prefix;
X509Certificate trustedCaCert;
try (InputStream is = getClass().getResourceAsStream("/tls/ca.pem")) {
trustedCaCert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
}
// Most basic constructor expects complete URL.
HTTPVaultConnector connector = new HTTPVaultConnector(url);
assertThat("Unexpected base URL", getRequestHelperPrivate(connector, "baseURL"), is(url));
// Now override TLS usage.
connector = new HTTPVaultConnector(hostname, false);
assertThat("Unexpected base URL with TLS disabled", getRequestHelperPrivate(connector, "baseURL"), is(expectedNoTls));
// Specify custom port.
connector = new HTTPVaultConnector(hostname, true, port);
assertThat("Unexpected base URL with custom port", getRequestHelperPrivate(connector, "baseURL"), is(expectedCustomPort));
// Specify custom prefix.
connector = new HTTPVaultConnector(hostname, true, port, prefix);
assertThat("Unexpected base URL with custom prefix", getRequestHelperPrivate(connector, "baseURL"), is(expectedCustomPrefix));
assertThat("Trusted CA cert set, but not specified", getRequestHelperPrivate(connector, "trustedCaCert"), is(nullValue()));
// Provide custom SSL context.
connector = new HTTPVaultConnector(hostname, true, port, prefix, trustedCaCert);
assertThat("Unexpected base URL with custom prefix", getRequestHelperPrivate(connector, "baseURL"), is(expectedCustomPrefix));
assertThat("Trusted CA cert not filled correctly", getRequestHelperPrivate(connector, "trustedCaCert"), is(trustedCaCert));
// Specify number of retries.
connector = new HTTPVaultConnector(url, trustedCaCert, retries);
assertThat("Number of retries not set correctly", getRequestHelperPrivate(connector, "retries"), is(retries));
// Test TLS version (#22).
assertThat("TLS version should be 1.2 if not specified", getRequestHelperPrivate(connector, "tlsVersion"), is("TLSv1.2"));
// Now override.
connector = new HTTPVaultConnector(url, trustedCaCert, retries, null, "TLSv1.1");
assertThat("Overridden TLS version 1.1 not correct", getRequestHelperPrivate(connector, "tlsVersion"), is("TLSv1.1"));
}
/**
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
public void sealExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
try {
connector.sealStatus();
fail("Querying seal status succeeded on invalid URL");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidRequestException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
}
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate NULL response (mock not supplied with data).
try {
connector.sealStatus();
fail("Querying seal status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
}
}
/**
* This test is designed to test exceptions caught and thrown by seal-methods if Vault is not reachable.
*/
@Test
public void healthExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector(INVALID_URL);
try {
connector.getHealth();
fail("Querying health status succeeded on invalid URL");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidRequestException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Invalid URI format"));
}
connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Simulate NULL response (mock not supplied with data).
try {
connector.getHealth();
fail("Querying health status succeeded on invalid instance");
} catch (Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Response unavailable"));
}
}
/**
* Test behavior on unparsable responses.
*/
@Test
public void parseExceptionTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "invalid", ContentType.APPLICATION_JSON);
// Now test the methods.
try {
connector.sealStatus();
fail("sealStatus() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.unseal("key");
fail("unseal() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getHealth();
fail("getHealth() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getAuthBackends();
fail("getAuthBackends() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.authToken("token");
fail("authToken() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupAppRole("roleName");
fail("lookupAppRole() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.getAppRoleID("roleName");
fail("getAppRoleID() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.createAppRoleSecret("roleName");
fail("createAppRoleSecret() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupAppRoleSecret("roleName", "secretID");
fail("lookupAppRoleSecret() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.listAppRoles();
fail("listAppRoles() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.listAppRoleSecrets("roleName");
fail("listAppRoleSecrets() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.read("key");
fail("read() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.list("path");
fail("list() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.renew("leaseID");
fail("renew() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
try {
connector.lookupToken("token");
fail("lookupToken() succeeded on invalid instance");
} catch (Exception e) {
assertParseError(e);
}
}
private void assertParseError(Exception e) {
assertThat("Unexpected type of exception", e, instanceOf(InvalidResponseException.class));
assertThat("Unexpected exception message", e.getMessage(), is("Unable to parse response"));
}
/**
* Test requests that expect an empty response with code 204, but receive a 200 body.
*/
@Test
public void nonEmpty204ResponseTest() throws IOException {
HTTPVaultConnector connector = new HTTPVaultConnector("https://127.0.0.1", null, 0, 250);
// Mock authorization.
setPrivate(connector, "authorized", true);
// Mock response.
mockResponse(200, "{}", ContentType.APPLICATION_JSON);
// Now test the methods expecting a 204.
try {
connector.registerAppId("appID", "policy", "displayName");
fail("registerAppId() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.registerUserId("appID", "userID");
fail("registerUserId() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.createAppRole("appID", Collections.singletonList("policy"));
fail("createAppRole() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.deleteAppRole("roleName");
fail("deleteAppRole() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.setAppRoleID("roleName", "roleID");
fail("setAppRoleID() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.destroyAppRoleSecret("roleName", "secretID");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.destroyAppRoleSecret("roleName", "secretUD");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.delete("key");
fail("delete() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
try {
connector.revoke("leaseID");
fail("destroyAppRoleSecret() with 200 response succeeded");
} catch (VaultConnectorException e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
}
private Object getRequestHelperPrivate(HTTPVaultConnector connector, String fieldName) {
try {
return getPrivate(getPrivate(connector, "request"), fieldName);
} catch (NoSuchFieldException | IllegalAccessException e) {
return null;
}
}
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
return field.get(target);
}
field.setAccessible(true);
Object value = field.get(target);
field.setAccessible(false);
return value;
}
private void setPrivate(Object target, String fieldName, Object value) {
try {
Field field = target.getClass().getDeclaredField(fieldName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
field.set(target, value);
field.setAccessible(accessible);
} catch (NoSuchFieldException | IllegalAccessException e) {
// Should not occur, to be taken care of in test code.
}
}
private void mockResponse(int status, String body, ContentType type) throws IOException {
when(httpMock.execute(any())).thenReturn(responseMock);
when(responseMock.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), status, ""));
when(responseMock.getEntity()).thenReturn(new StringEntity(body, type));
}
/**
* Mocked {@link HttpClientBuilder} that always returns the mocked client.
*/
private static class MockedHttpClientBuilder extends HttpClientBuilder {
@Override
public CloseableHttpClient build() {
return httpMock;
}
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2016-2019 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.builder;
import de.stklcode.jvault.connector.HTTPVaultConnector;
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import org.junit.Rule;
import org.junit.contrib.java.lang.system.EnvironmentVariables;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.NoSuchFileException;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit test for HTTP Vault connector factory
*
* @author Stefan Kalscheuer
* @since 0.8.0
*/
@EnableRuleMigrationSupport
public class HTTPVaultConnectorBuilderTest {
private static final String VAULT_ADDR = "https://localhost:8201";
private static final Integer VAULT_MAX_RETRIES = 13;
private static final String VAULT_TOKEN = "00001111-2222-3333-4444-555566667777";
@TempDir
File tempDir;
@Rule
public final EnvironmentVariables environment = new EnvironmentVariables();
/**
* Test building from environment variables
*/
@Test
void testFromEnv() throws NoSuchFieldException, IllegalAccessException, IOException {
/* Provide address only should be enough */
setenv(VAULT_ADDR, null, null, null);
HTTPVaultConnectorBuilder factory = null;
HTTPVaultConnector connector;
try {
factory = VaultConnectorBuilder.http().fromEnv();
} catch (VaultConnectorException e) {
fail("Factory creation from minimal environment failed");
}
connector = factory.build();
assertThat("URL nor set correctly", getRequestHelperPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("Trusted CA cert set when no cert provided", getRequestHelperPrivate(connector, "trustedCaCert"), is(nullValue()));
assertThat("Non-default number of retries, when none set", getRequestHelperPrivate(connector, "retries"), is(0));
/* Provide address and number of retries */
setenv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), null);
try {
factory = VaultConnectorBuilder.http().fromEnv();
} catch (VaultConnectorException e) {
fail("Factory creation from environment failed");
}
connector = factory.build();
assertThat("URL nor set correctly", getRequestHelperPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("Trusted CA cert set when no cert provided", getRequestHelperPrivate(connector, "trustedCaCert"), is(nullValue()));
assertThat("Number of retries not set correctly", getRequestHelperPrivate(connector, "retries"), is(VAULT_MAX_RETRIES));
/* Provide CA certificate */
String VAULT_CACERT = tempDir.toString() + "/doesnotexist";
setenv(VAULT_ADDR, VAULT_CACERT, VAULT_MAX_RETRIES.toString(), null);
try {
VaultConnectorBuilder.http().fromEnv();
fail("Creation with unknown cert path failed.");
} catch (VaultConnectorException e) {
assertThat(e, is(instanceOf(TlsException.class)));
assertThat(e.getCause(), is(instanceOf(NoSuchFileException.class)));
assertThat(((NoSuchFileException) e.getCause()).getFile(), is(VAULT_CACERT));
}
/* Automatic authentication */
setenv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN);
try {
factory = VaultConnectorBuilder.http().fromEnv();
} catch (VaultConnectorException e) {
fail("Factory creation from minimal environment failed");
}
assertThat("Token nor set correctly", getPrivate(factory, "token"), is(equalTo(VAULT_TOKEN)));
}
private void setenv(String vault_addr, String vault_cacert, String vault_max_retries, String vault_token) {
environment.set("VAULT_ADDR", vault_addr);
environment.set("VAULT_CACERT", vault_cacert);
environment.set("VAULT_MAX_RETRIES", vault_max_retries);
environment.set("VAULT_TOKEN", vault_token);
}
private Object getRequestHelperPrivate(HTTPVaultConnector connector, String fieldName) throws NoSuchFieldException, IllegalAccessException {
return getPrivate(getPrivate(connector, "request"), fieldName);
}
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getDeclaredField(fieldName);
if (field.isAccessible()) {
return field.get(target);
}
field.setAccessible(true);
Object value = field.get(target);
field.setAccessible(false);
return value;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,12 +16,12 @@
package de.stklcode.jvault.connector.exception;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
/**
* Common JUnit test for Exceptions extending {@link VaultConnectorException}.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,18 +19,20 @@ package de.stklcode.jvault.connector.factory;
import de.stklcode.jvault.connector.HTTPVaultConnector;
import de.stklcode.jvault.connector.exception.TlsException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.EnvironmentVariables;
import org.junit.rules.TemporaryFolder;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.migrationsupport.rules.EnableRuleMigrationSupport;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.NoSuchFileException;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit test for HTTP Vault connector factory
@ -38,13 +40,14 @@ import static org.junit.Assert.*;
* @author Stefan Kalscheuer
* @since 0.6.0
*/
@EnableRuleMigrationSupport
public class HTTPVaultConnectorFactoryTest {
private static String VAULT_ADDR = "https://localhost:8201";
private static Integer VAULT_MAX_RETRIES = 13;
private static String VAULT_TOKEN = "00001111-2222-3333-4444-555566667777";
@Rule
public TemporaryFolder tmpDir = new TemporaryFolder();
@TempDir
File tempDir;
@Rule
public final EnvironmentVariables environment = new EnvironmentVariables();
@ -66,9 +69,9 @@ public class HTTPVaultConnectorFactoryTest {
}
connector = factory.build();
assertThat("URL nor set correctly", getPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("SSL context set when no cert provided", getPrivate(connector, "sslContext"), is(nullValue()));
assertThat("Non-default number of retries, when none set", getPrivate(connector, "retries"), is(0));
assertThat("URL nor set correctly", getRequestHelperPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("Trusted CA cert set when no cert provided", getRequestHelperPrivate(connector, "trustedCaCert"), is(nullValue()));
assertThat("Non-default number of retries, when none set", getRequestHelperPrivate(connector, "retries"), is(0));
/* Provide address and number of retries */
setenv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), null);
@ -80,12 +83,12 @@ public class HTTPVaultConnectorFactoryTest {
}
connector = factory.build();
assertThat("URL nor set correctly", getPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("SSL context set when no cert provided", getPrivate(connector, "sslContext"), is(nullValue()));
assertThat("Number of retries not set correctly", getPrivate(connector, "retries"), is(VAULT_MAX_RETRIES));
assertThat("URL nor set correctly", getRequestHelperPrivate(connector, "baseURL"), is(equalTo(VAULT_ADDR + "/v1/")));
assertThat("Trusted CA cert set when no cert provided", getRequestHelperPrivate(connector, "trustedCaCert"), is(nullValue()));
assertThat("Number of retries not set correctly", getRequestHelperPrivate(connector, "retries"), is(VAULT_MAX_RETRIES));
/* Provide CA certificate */
String VAULT_CACERT = tmpDir.newFolder().toString() + "/doesnotexist";
String VAULT_CACERT = tempDir.toString() + "/doesnotexist";
setenv(VAULT_ADDR, VAULT_CACERT, VAULT_MAX_RETRIES.toString(), null);
try {
@ -94,7 +97,7 @@ public class HTTPVaultConnectorFactoryTest {
} catch (VaultConnectorException e) {
assertThat(e, is(instanceOf(TlsException.class)));
assertThat(e.getCause(), is(instanceOf(NoSuchFileException.class)));
assertThat(((NoSuchFileException)e.getCause()).getFile(), is(VAULT_CACERT));
assertThat(((NoSuchFileException) e.getCause()).getFile(), is(VAULT_CACERT));
}
/* Automatic authentication */
@ -105,7 +108,7 @@ public class HTTPVaultConnectorFactoryTest {
} catch (VaultConnectorException e) {
fail("Factory creation from minimal environment failed");
}
assertThat("Token nor set correctly", getPrivate(factory, "token"), is(equalTo(VAULT_TOKEN)));
assertThat("Token nor set correctly", getPrivate(getPrivate(factory, "delegate"), "token"), is(equalTo(VAULT_TOKEN)));
}
private void setenv(String vault_addr, String vault_cacert, String vault_max_retries, String vault_token) {
@ -115,10 +118,15 @@ public class HTTPVaultConnectorFactoryTest {
environment.set("VAULT_TOKEN", vault_token);
}
private Object getRequestHelperPrivate(HTTPVaultConnector connector, String fieldName) throws NoSuchFieldException, IllegalAccessException {
return getPrivate(getPrivate(connector, "request"), fieldName);
}
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getDeclaredField(fieldName);
if (field.isAccessible())
if (field.isAccessible()) {
return field.get(target);
}
field.setAccessible(true);
Object value = field.get(target);
field.setAccessible(false);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,13 +18,14 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
/**
* JUnit Test for AppRole Builder.
@ -50,10 +51,10 @@ public class AppRoleBuilderTest {
private static final Integer TOKEN_MAX_TTL = 9600;
private static final Integer PERIOD = 1234;
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,\"bound_cidr_list\":\"%s\",\"policies\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"token_ttl\":%d,\"token_max_ttl\":%d,\"period\":%d}",
NAME, ID, BIND_SECRET_ID, CIDR_1, POLICY, SECRET_ID_NUM_USES, SECRET_ID_TTL, TOKEN_TTL, TOKEN_MAX_TTL, PERIOD);
private static final String JSON_FULL = String.format("{\"role_name\":\"%s\",\"role_id\":\"%s\",\"bind_secret_id\":%s,\"bound_cidr_list\":\"%s\",\"secret_id_bound_cidrs\":\"%s\",\"policies\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"token_ttl\":%d,\"token_max_ttl\":%d,\"period\":%d}",
NAME, ID, BIND_SECRET_ID, CIDR_1, CIDR_1, POLICY, SECRET_ID_NUM_USES, SECRET_ID_TTL, TOKEN_TTL, TOKEN_MAX_TTL, PERIOD);
@BeforeClass
@BeforeAll
public static void init() {
BOUND_CIDR_LIST.add(CIDR_1);
POLICIES.add(POLICY);
@ -68,6 +69,7 @@ public class AppRoleBuilderTest {
assertThat(role.getId(), is(nullValue()));
assertThat(role.getBindSecretId(), is(nullValue()));
assertThat(role.getBoundCidrList(), is(nullValue()));
assertThat(role.getSecretIdBoundCidrs(), is(nullValue()));
assertThat(role.getPolicies(), is(nullValue()));
assertThat(role.getSecretIdNumUses(), is(nullValue()));
assertThat(role.getSecretIdTtl(), is(nullValue()));
@ -88,6 +90,7 @@ public class AppRoleBuilderTest {
.withId(ID)
.withBindSecretID(BIND_SECRET_ID)
.withBoundCidrList(BOUND_CIDR_LIST)
.withSecretIdBoundCidrs(BOUND_CIDR_LIST)
.withPolicies(POLICIES)
.withSecretIdNumUses(SECRET_ID_NUM_USES)
.withSecretIdTtl(SECRET_ID_TTL)
@ -99,6 +102,7 @@ public class AppRoleBuilderTest {
assertThat(role.getId(), is(ID));
assertThat(role.getBindSecretId(), is(BIND_SECRET_ID));
assertThat(role.getBoundCidrList(), is(BOUND_CIDR_LIST));
assertThat(role.getSecretIdBoundCidrs(), is(BOUND_CIDR_LIST));
assertThat(role.getPolicies(), is(POLICIES));
assertThat(role.getSecretIdNumUses(), is(SECRET_ID_NUM_USES));
assertThat(role.getSecretIdTtl(), is(SECRET_ID_TTL));
@ -127,12 +131,16 @@ public class AppRoleBuilderTest {
role = new AppRoleBuilder(NAME).withCidrBlock(CIDR_2).build();
assertThat(role.getBoundCidrList(), hasSize(1));
assertThat(role.getBoundCidrList(), contains(CIDR_2));
assertThat(role.getSecretIdBoundCidrs(), hasSize(1));
assertThat(role.getSecretIdBoundCidrs(), contains(CIDR_2));
role = new AppRoleBuilder(NAME)
.withBoundCidrList(BOUND_CIDR_LIST)
.withSecretIdBoundCidrs(BOUND_CIDR_LIST)
.withCidrBlock(CIDR_2)
.build();
assertThat(role.getBoundCidrList(), hasSize(2));
assertThat(role.getBoundCidrList(), contains(CIDR_1, CIDR_2));
assertThat(role.getBoundCidrList(), hasSize(1));
assertThat(role.getBoundCidrList(), contains(CIDR_2));
assertThat(role.getSecretIdBoundCidrs(), hasSize(2));
assertThat(role.getSecretIdBoundCidrs(), contains(CIDR_1, CIDR_2));
/* Add single policy */
role = new AppRoleBuilder(NAME).withPolicy(POLICY_2).build();

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,11 +16,9 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.Field;
@ -29,12 +27,11 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.Matchers.emptyString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.junit.MatcherAssume.assumeThat;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for AppRoleSecret model.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 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,10 +16,10 @@
package de.stklcode.jvault.connector.model;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
/**
* JUnit Test for AuthBackend model.

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,16 +18,16 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
/**
* JUnit Test for Token Builder.
@ -55,7 +55,7 @@ public class TokenBuilderTest {
private static final Boolean RENEWABLE = true;
private static final String JSON_FULL = "{\"id\":\"test-id\",\"display_name\":\"display-name\",\"no_parent\":false,\"no_default_policy\":false,\"ttl\":123,\"num_uses\":4,\"policies\":[\"policy\"],\"meta\":{\"key\":\"value\"},\"renewable\":true}";
@BeforeClass
@BeforeAll
public static void init() {
POLICIES.add(POLICY);
META.put(META_KEY, META_VALUE);

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AppRole;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AppRoleResponse} model.

View File

@ -1,20 +1,34 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthMethodsResponse} model.

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthResponse} model.

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link CredentialsResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
public class CredentialsResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
private static final String VAL_USER = "testUserName";
private static final String VAL_PASS = "5up3r5ecr3tP455";
static {
DATA.put("username", VAL_USER);
DATA.put("password", VAL_PASS);
}
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
public void getCredentialsTest() throws InvalidResponseException {
// Create empty Object.
CredentialsResponse res = new CredentialsResponse();
assertThat("Username not present in data map should not return anything", res.getUsername(), is(nullValue()));
assertThat("Password not present in data map should not return anything", res.getPassword(), is(nullValue()));
// Fill data map.
res.setData(DATA);
assertThat("Incorrect username", res.getUsername(), is(VAL_USER));
assertThat("Incorrect password", res.getPassword(), is(VAL_PASS));
}
}

View File

@ -0,0 +1,81 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link AuthResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.7.0
*/
public class HealthResponseTest {
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.9.2";
private static final Long SERVER_TIME_UTC = 1469555798L;
private static final Boolean STANDBY = false;
private static final Boolean SEALED = false;
private static final Boolean INITIALIZED = true;
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 String RES_JSON = "{\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
" \"cluster_name\": \"" + CLUSTER_NAME + "\",\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"server_time_utc\": " + SERVER_TIME_UTC + ",\n" +
" \"standby\": " + STANDBY + ",\n" +
" \"sealed\": " + SEALED + ",\n" +
" \"initialized\": " + INITIALIZED + ",\n" +
" \"replication_perf_mode\": \"" + REPL_PERF_MODE + "\",\n" +
" \"replication_dr_mode\": \"" + REPL_DR_MODE + "\",\n" +
" \"performance_standby\": " + PERF_STANDBY + "\n" +
"}";
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
HealthResponse res = new ObjectMapper().readValue(RES_JSON, HealthResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect cluster ID", res.getClusterID(), is(CLUSTER_ID));
assertThat("Incorrect cluster name", res.getClusterName(), is(CLUSTER_NAME));
assertThat("Incorrect version", res.getVersion(), is(VERSION));
assertThat("Incorrect server time", res.getServerTimeUTC(), is(SERVER_TIME_UTC));
assertThat("Incorrect standby state", res.isStandby(), is(STANDBY));
assertThat("Incorrect seal state", res.isSealed(), is(SEALED));
assertThat("Incorrect initialization state", res.isInitialized(), is(INITIALIZED));
assertThat("Incorrect performance standby state", res.isPerformanceStandby(), is(PERF_STANDBY));
assertThat("Incorrect replication perf mode", res.getReplicationPerfMode(), is(REPL_PERF_MODE));
assertThat("Incorrect replication DR mode", res.getReplicationDrMode(), is(REPL_DR_MODE));
} catch (IOException e) {
fail("Health deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link MetadataResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
public class MetadataResponseTest {
private static final String V1_TIME = "2018-03-22T02:24:06.945319214Z";
private static final String V3_TIME = "2018-03-22T02:36:43.986212308Z";
private static final String V2_TIME = "2018-03-22T02:36:33.954880664Z";
private static final Integer CURRENT_VERSION = 3;
private static final Integer MAX_VERSIONS = 0;
private static final Integer OLDEST_VERSION = 1;
private static final String META_JSON = "{\n" +
" \"data\": {\n" +
" \"created_time\": \"" + V1_TIME + "\",\n" +
" \"current_version\": " + CURRENT_VERSION + ",\n" +
" \"max_versions\": " + MAX_VERSIONS + ",\n" +
" \"oldest_version\": " + OLDEST_VERSION + ",\n" +
" \"updated_time\": \"" + V3_TIME + "\",\n" +
" \"versions\": {\n" +
" \"1\": {\n" +
" \"created_time\": \"" + V1_TIME + "\",\n" +
" \"deletion_time\": \"" + V2_TIME + "\",\n" +
" \"destroyed\": true\n" +
" },\n" +
" \"2\": {\n" +
" \"created_time\": \"" + V2_TIME + "\",\n" +
" \"deletion_time\": \"\",\n" +
" \"destroyed\": false\n" +
" },\n" +
" \"3\": {\n" +
" \"created_time\": \"" + V3_TIME + "\",\n" +
" \"deletion_time\": \"\",\n" +
" \"destroyed\": false\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
MetadataResponse res = new ObjectMapper().readValue(META_JSON, MetadataResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Parsed metadatra is NULL", res.getMetadata(), is(notNullValue()));
assertThat("Incorrect created time", res.getMetadata().getCreatedTimeString(), is(V1_TIME));
assertThat("Parting created time failed", res.getMetadata().getCreatedTime(), is(notNullValue()));
assertThat("Incorrect current version", res.getMetadata().getCurrentVersion(), is(CURRENT_VERSION));
assertThat("Incorrect max versions", res.getMetadata().getMaxVersions(), is(MAX_VERSIONS));
assertThat("Incorrect oldest version", res.getMetadata().getOldestVersion(), is(OLDEST_VERSION));
assertThat("Incorrect updated time", res.getMetadata().getUpdatedTimeString(), is(V3_TIME));
assertThat("Parting updated time failed", res.getMetadata().getUpdatedTime(), is(notNullValue()));
assertThat("Incorrect number of versions", res.getMetadata().getVersions().size(), is(3));
assertThat("Incorrect version 1 delete time", res.getMetadata().getVersions().get(1).getDeletionTimeString(), is(V2_TIME));
assertThat("Parsion version delete time failed", res.getMetadata().getVersions().get(1).getDeletionTime(), is(notNullValue()));
assertThat("Incorrect version 1 destroyed state", res.getMetadata().getVersions().get(1).isDestroyed(), is(true));
assertThat("Incorrect version 2 created time", res.getMetadata().getVersions().get(2).getCreatedTimeString(), is(V2_TIME));
assertThat("Parsion version created failed", res.getMetadata().getVersions().get(2).getCreatedTime(), is(notNullValue()));
assertThat("Incorrect version 3 destroyed state", res.getMetadata().getVersions().get(3).isDestroyed(), is(false));
} catch (IOException e) {
fail("MetadataResoponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link SealResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SealResponseTest {
private static final String TYPE = "shamir";
private static final Integer THRESHOLD = 3;
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 = "0.11.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";
private static final String RES_SEALED = "{\n" +
" \"type\": \"" + TYPE + "\",\n" +
" \"sealed\": true,\n" +
" \"initialized\": true,\n" +
" \"t\": " + THRESHOLD + ",\n" +
" \"n\": " + SHARES + ",\n" +
" \"progress\": " + PROGRESS_SEALED + ",\n" +
" \"nonce\": \"\",\n" +
" \"version\": \"" + VERSION + "\"\n" +
"}";
private static final String RES_UNSEALED = "{\n" +
" \"type\": \"" + TYPE + "\",\n" +
" \"sealed\": false,\n" +
" \"initialized\": true,\n" +
" \"t\": " + THRESHOLD + ",\n" +
" \"n\": " + SHARES + ",\n" +
" \"progress\": " + PROGRESS_UNSEALED + ",\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"cluster_name\": \"" + CLUSTER_NAME + "\",\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
" \"nonce\": \"" + NONCE + "\"\n" +
"}";
/**
* Test creation from JSON value as returned by Vault when sealed (JSON example close to Vault documentation).
*/
@Test
public void jsonRoundtripSealed() {
// First test sealed Vault's response.
try {
SealResponse res = new ObjectMapper().readValue(RES_SEALED, SealResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect seal type", res.getType(), is(TYPE));
assertThat("Incorrect seal status", res.isSealed(), is(true));
assertThat("Incorrect initialization status", res.isInitialized(), is(true));
assertThat("Incorrect threshold", res.getThreshold(), is(THRESHOLD));
assertThat("Incorrect number of shares", res.getNumberOfShares(), is(SHARES));
assertThat("Incorrect progress", res.getProgress(), is(PROGRESS_SEALED));
assertThat("Nonce not empty", res.getNonce(), is(""));
assertThat("Incorrect version", res.getVersion(), is(VERSION));
// And the fields, that should not be filled.
assertThat("Cluster name should not be populated", res.getClusterName(), is(nullValue()));
assertThat("Cluster ID should not be populated", res.getClusterId(), is(nullValue()));
} catch (IOException e) {
fail("TokenResponse deserialization failed: " + e.getMessage());
}
// Not test unsealed Vault's response.
try {
SealResponse res = new ObjectMapper().readValue(RES_UNSEALED, SealResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect seal type", res.getType(), is(TYPE));
assertThat("Incorrect seal status", res.isSealed(), is(false));
assertThat("Incorrect initialization status", res.isInitialized(), is(true));
assertThat("Incorrect threshold", res.getThreshold(), is(THRESHOLD));
assertThat("Incorrect number of shares", res.getNumberOfShares(), is(SHARES));
assertThat("Incorrect progress", res.getProgress(), is(PROGRESS_UNSEALED));
assertThat("Incorrect nonce", res.getNonce(), is(NONCE));
assertThat("Incorrect version", res.getVersion(), is(VERSION));
assertThat("Incorrect cluster name", res.getClusterName(), is(CLUSTER_NAME));
assertThat("Incorrect cluster ID", res.getClusterId(), is(CLUSTER_ID));
} catch (IOException e) {
fail("TokenResponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2016-2019 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 de.stklcode.jvault.connector.exception.InvalidResponseException;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.fail;
/**
* JUnit Test for {@link SecretListResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SecretListResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
private static final String KEY1 = "key1";
private static final String KEY2 = "key-2";
private static final List<String> KEYS = Arrays.asList(KEY1, KEY2);
static {
DATA.put("keys", KEYS);
}
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
public void getKeysTest() throws InvalidResponseException {
// Create empty Object.
SecretListResponse res = new SecretListResponse();
assertThat("Keys should be null without initialization", res.getKeys(), is(nullValue()));
// Provoke internal ClassCastException.
try {
Map<String, Object> invalidData = new HashMap<>();
invalidData.put("keys", "some string");
res.setData(invalidData);
fail("Setting incorrect class succeeded");
} catch (Exception e) {
assertThat("Unexpected exception type", e, instanceOf(InvalidResponseException.class));
}
// Fill correct data.
res.setData(DATA);
assertThat("Keys should be filled here", res.getKeys(), is(notNullValue()));
assertThat("Unexpected number of keys", res.getKeys(), hasSize(2));
assertThat("Unexpected keys", res.getKeys(), contains(KEY1, KEY2));
}
}

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link SecretResponse} model.
@ -37,6 +53,8 @@ public class SecretResponseTest {
private static final String SECRET_DATA_V1 = "yes";
private static final String SECRET_DATA_K2 = "value";
private static final String SECRET_DATA_V2 = "world";
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 SECRET_JSON = "{\n" +
" \"request_id\": \"" + SECRET_REQUEST_ID + "\",\n" +
@ -49,6 +67,44 @@ public class SecretResponseTest {
" },\n" +
" \"warnings\": " + SECRET_WARNINGS + "\n" +
"}";
private static final String SECRET_JSON_V2 = "{\n" +
" \"request_id\": \"" + SECRET_REQUEST_ID + "\",\n" +
" \"lease_id\": \"" + SECRET_LEASE_ID + "\",\n" +
" \"lease_duration\": " + SECRET_LEASE_DURATION + ",\n" +
" \"renewable\": " + SECRET_RENEWABLE + ",\n" +
" \"data\": {\n" +
" \"data\": {\n" +
" \"" + SECRET_DATA_K1 + "\": \"" + SECRET_DATA_V1 + "\",\n" +
" \"" + SECRET_DATA_K2 + "\": \"" + SECRET_DATA_V2 + "\"\n" +
" },\n" +
" \"metadata\": {\n" +
" \"created_time\": \"" + SECRET_META_CREATED + "\",\n" +
" \"deletion_time\": \"\",\n" +
" \"destroyed\": false,\n" +
" \"version\": 1\n" +
" }\n" +
" },\n" +
" \"warnings\": " + SECRET_WARNINGS + "\n" +
"}";
private static final String SECRET_JSON_V2_2 = "{\n" +
" \"request_id\": \"" + SECRET_REQUEST_ID + "\",\n" +
" \"lease_id\": \"" + SECRET_LEASE_ID + "\",\n" +
" \"lease_duration\": " + SECRET_LEASE_DURATION + ",\n" +
" \"renewable\": " + SECRET_RENEWABLE + ",\n" +
" \"data\": {\n" +
" \"data\": {\n" +
" \"" + SECRET_DATA_K1 + "\": \"" + SECRET_DATA_V1 + "\",\n" +
" \"" + SECRET_DATA_K2 + "\": \"" + SECRET_DATA_V2 + "\"\n" +
" },\n" +
" \"metadata\": {\n" +
" \"created_time\": \"" + SECRET_META_CREATED + "\",\n" +
" \"deletion_time\": \"" + SECRET_META_DELETED + "\",\n" +
" \"destroyed\": true,\n" +
" \"version\": 2\n" +
" }\n" +
" },\n" +
" \"warnings\": " + SECRET_WARNINGS + "\n" +
"}";
static {
@ -102,16 +158,49 @@ public class SecretResponseTest {
@Test
public void jsonRoundtrip() {
try {
SecretResponse res = new ObjectMapper().readValue(SECRET_JSON, SecretResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect lease ID", res.getLeaseId(), is(SECRET_LEASE_ID));
assertThat("Incorrect lease duration", res.getLeaseDuration(), is(SECRET_LEASE_DURATION));
assertThat("Incorrect renewable status", res.isRenewable(), is(SECRET_RENEWABLE));
assertThat("Incorrect warnings", res.getWarnings(), is(SECRET_WARNINGS));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K1), is(SECRET_DATA_V1));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K2), is(SECRET_DATA_V2));
assertSecretData(new ObjectMapper().readValue(SECRET_JSON, SecretResponse.class));
} catch (IOException e) {
fail("SecretResponse deserialization failed: " + e.getMessage());
}
// KV v2 secret.
try {
SecretResponse res = new ObjectMapper().readValue(SECRET_JSON_V2, SecretResponse.class);
assertSecretData(res);
assertThat("SecretResponse does not contain metadata", res.getMetadata(), is(notNullValue()));
assertThat("Incorrect creation date string", res.getMetadata().getCreatedTimeString(), is(SECRET_META_CREATED));
assertThat("Creation date parsing failed", res.getMetadata().getCreatedTime(), is(notNullValue()));
assertThat("Incorrect deletion date string", res.getMetadata().getDeletionTimeString(), is(emptyString()));
assertThat("Incorrect deletion date", res.getMetadata().getDeletionTime(), is(nullValue()));
assertThat("Secret destroyed when not expected", res.getMetadata().isDestroyed(), is(false));
assertThat("Incorrect secret version", res.getMetadata().getVersion(), is(1));
} catch (IOException e) {
fail("SecretResponse deserialization failed: " + e.getMessage());
}
// Deleted KV v2 secret.
try {
SecretResponse res = new ObjectMapper().readValue(SECRET_JSON_V2_2, SecretResponse.class);
assertSecretData(res);
assertThat("SecretResponse does not contain metadata", res.getMetadata(), is(notNullValue()));
assertThat("Incorrect creation date string", res.getMetadata().getCreatedTimeString(), is(SECRET_META_CREATED));
assertThat("Creation date parsing failed", res.getMetadata().getCreatedTime(), is(notNullValue()));
assertThat("Incorrect deletion date string", res.getMetadata().getDeletionTimeString(), is(SECRET_META_DELETED));
assertThat("Incorrect deletion date", res.getMetadata().getDeletionTime(), is(notNullValue()));
assertThat("Secret destroyed when not expected", res.getMetadata().isDestroyed(), is(true));
assertThat("Incorrect secret version", res.getMetadata().getVersion(), is(2));
} catch (IOException e) {
fail("SecretResponse deserialization failed: " + e.getMessage());
}
}
private void assertSecretData(SecretResponse res) {
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect lease ID", res.getLeaseId(), is(SECRET_LEASE_ID));
assertThat("Incorrect lease duration", res.getLeaseDuration(), is(SECRET_LEASE_DURATION));
assertThat("Incorrect renewable status", res.isRenewable(), is(SECRET_RENEWABLE));
assertThat("Incorrect warnings", res.getWarnings(), is(SECRET_WARNINGS));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K1), is(SECRET_DATA_V1));
assertThat("Response does not contain correct data", res.get(SECRET_DATA_K2), is(SECRET_DATA_V2));
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link SecretVersionResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SecretVersionResponseTest {
private static final String CREATION_TIME = "2018-03-22T02:24:06.945319214Z";
private static final String DELETION_TIME = "2018-03-22T02:36:43.986212308Z";
private static final Integer VERSION = 42;
private static final String META_JSON = "{\n" +
" \"data\": {\n" +
" \"created_time\": \"" + CREATION_TIME + "\",\n" +
" \"deletion_time\": \"" + DELETION_TIME + "\",\n" +
" \"destroyed\": false,\n" +
" \"version\": " + VERSION + "\n" +
" }\n" +
"}";
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
SecretVersionResponse res = new ObjectMapper().readValue(META_JSON, SecretVersionResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Parsed metadatra is NULL", res.getMetadata(), is(notNullValue()));
assertThat("Incorrect created time", res.getMetadata().getCreatedTimeString(), is(CREATION_TIME));
assertThat("Incorrect deletion time", res.getMetadata().getDeletionTimeString(), is(DELETION_TIME));
assertThat("Incorrect destroyed state", res.getMetadata().isDestroyed(), is(false));
assertThat("Incorrect version", res.getMetadata().getVersion(), is(VERSION));
} catch (IOException e) {
fail("SecretVersionResponse deserialization failed: " + e.getMessage());
}
}
}

View File

@ -1,17 +1,33 @@
/*
* Copyright 2016-2019 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.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.fail;
/**
* JUnit Test for {@link TokenResponse} model.

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2017 Stefan Kalscheuer
* Copyright 2016-2019 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -92,7 +92,7 @@ public class VaultConfiguration {
@Override
public String toString() {
return "backend \"file\" {\n" +
return "storage \"file\" {\n" +
" path = \"" + dataLocation + "\"\n" +
"}\n" +
"listener \"tcp\" {\n" +

View File

@ -1 +0,0 @@
{"Key":"auth/20e9c2e6-5b1f-b9c9-5a99-21667e0a899d/salt","Value":"AAAAAQJUsuXXEpmdNY5aIh5HdzZRTFpOUIgyKLGiw65DBwSXW6yGAYe/zhN/Ow+vyRZxG4temgnTjN7RVGjyzXGG5yLY"}

View File

@ -1 +0,0 @@
{"Key":"auth/20e9c2e6-5b1f-b9c9-5a99-21667e0a899d/struct/map/app-id/19d90b9adcec2bf5088304034622a169a148ff43","Value":"AAAAAQJuuRcCRinyawQ05brruZQY7ypgs1mOsFHI16XLwYB4dzwJob71wW+74RjvK4FVL4qPfgyMPKEtV2uO9+4hr2mC6BrcN///Ksxv+ns8FMVlBOMJpQ=="}

View File

@ -1 +0,0 @@
{"Key":"auth/20e9c2e6-5b1f-b9c9-5a99-21667e0a899d/struct/map/user-id/55a852babe045b5980fc8ac4a13af27021dbbfd4","Value":"AAAAAQICaFIxG2xAq0AuJryVn1XghDulkVdQicXvhEL45K2S48aZcvMEsrDUXm9o427Bp6eMiq0Hw070nosnB9SWSQJEFUfPmM6I7Jhsou6CKmocs/AmocxY3Du4Lg=="}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQINZKQEssY4IzHI/0k27nBtxSvnC6LkivYrqky6CblcjyAmQIg/4/cKQIBCXzmrWEv/SqMQbLw+4Lp63Xu1niF+U0NbyqDmFaPqnD2yfPs7meXvZr21+P9E/0APZMHQaSR7DIEY46zedHRjQ/pkhR2Axcjuy5gdfzBzC2XvUcNqdyR0pQwcDwGhAIdO0gxJfZCeBuvv8ceYS+aPs4gDHtIlA3szi+5qAQ8HvPBTDKQn1lHVYnzTdNbMS7v3mtzCyG8AeMkaUw=="}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQJiN0bHxM8aNJpY7aHGZ/p3qOhJbd7JIXwFMEI4LtKmO6pP5Oa4P5z+2LK+2qzZhhX/iDeM4u+nR+lxt/GsBPKf"}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQIZ5rvzLtBcBQvWqwwDoRADwUo6W0ECKgmcvXejbLKiYcbO0hP8fceCqB12J41wxcMViQ8vvWoIgyOX2HwcZS09GGCqQbjvyVfz/w+kyox9dJzr845f26tJjHVYlHX2YFsnxytwe5qCKdCsD5QP9kyz8J0="}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQJIKXgvJ2Prr92Fn7qZK/WZxb+Q+vJsXyjMo44en/+zqbbnLfs5m7uH7hEUOfDM/MjzTrhfkOWxf6qcrC4MR3ocrFJwtbJ6j+QrNLg61gVIiLBUVBkvh8ErqCFnSjNIqhaIMQhuZXWANsgGF9K2+HBGJerWGe26C1rdWRZV4J0M23HnxLBf8aYDlOfkHe24I30rHUqXIk9/BKEuekcnhETw9Tx4Fk7KxxxEdGmHPbg+4G14c27wVj8IrpWHBpyLmt55Qdb/y24i5RzFYv1FQ2kQZIO6TQiYkwAUxJ5cIdCiAsEDNt+rBJ9zX1MvkEfkVj/WvzC9pxB7ad+j5vYIJgCUD1o09t0w2ChjBojGoOAWDSvNAeY8kr5PDKwYk+gBY5M2JHDI6ELvOVu8gsW+sqk9St9V3VisShTfU3+qln7TOWw/LQ+fUzGvYrAWG6UyPVpIEYeNmN0iGCIM8rzjQYvVC3KcQMG96MwK3ZlSQzgPpvTGxNxFm2omTn3ue7QHn7Ni6+c/K6xVy0bbJ+jS4yyfl2wFBm1tF1l3BITLqw3TiP3LEw=="}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQIUt2iYYy9zOwkx1mtNMHt69RjdHbUmcN8zydVQTMGjhv1kjEW+d4AaBv1qE22rPTs0xL3pJ1AjIvkBXXVBAuc/FE63t5dE81Fa+MvSY4tBeMtl6i09ykkAYyQUeeV2HlbjRpMUwPyq2QIslYw3d4lc73yT0S82s5I3MfjodKmDpheWMOgg5hGes/wstBHN5HEZkKV8gOPRZ/BsTM7tMXH1piM/JT8sNfsDh6TAGD1OEsS+N2QlKvS4yImNzcKrH0EgdkXB4sRZ9e/SmMaEVaagB1n0M5LukC+pyExgC7eK4EU8o2Xye3iij3YMWBaGollDzJBJFP5aSO4E5u+NnRc5/ZbLRCbqgfQj8IY86WF9hya31aJxbc8Pg28Yfez8hbGRJZZws/ojIUgEz+VtH3OyaW2Wohnycop7i4fK8xlJ2gYOGvlw43czOH6Y6joTce+QBZWI7KR6ugB0dI8pnK2eFy14OZeww1NEew7r1u7PgD10Obg8okIJSD8cGkxUHu/oOLxvKKOAJBLSPfKnJfKEiKrqYED7EPkmgP/t7okvo4c95qeuWy1BLtKfxw5lkv0="}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQKv0Yr+QFSWxYe8o51TBwGz/yAhNYFmkNHPISEK6EbIVGkpEJMHFYvHWxTXUzF7f2/a"}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQKs2/ICwQPLv6siBGDbBnB52fBVo52BkSKGvm74p4oHrdMEvejJ4cJljOADYyDT2QYa"}

View File

@ -0,0 +1 @@
{"Value":"AAAAAQIq05o3NmsucipTxPrcRbT1sXpAJ8w2PpiShnof74Kuzf/4kkHj3AZL5AObGFLAkYUvUrv3RRmYBIhw6Jk4FCbgdQyJAjPNVUTwBun/kQVyzP5sQ9hUFgHJwINomtVDiDgPkOc92zk8ydr1hfnMmTAtS71G3xloHDn6CF/1Y9WI1PkHdSkZ8d+yBNxr+qjGyewrV3QVmQvAfpY56uQ6AOztItD9NgiPrtNP+clbCczsieY6Y9Ce2FZawmuKFi9svMcBtnEcMILV/SGt4iCiMgFwkCJ9gQsGEdWPifu6ITPB92LgT4Ccw4gVRO31QVcPl6S+FG6iCeN6lk2yRXYjyhBuU+GklouEZIsA6SoxlIXPZuvauyS1MWwMxtSOQUFVYr3kvtXzCpcpEHDyBOEUdxPaYUZXHNdhGtMr/JuJCN50t0ng5mEAqfhjoJfJ/tBTqAjySj4zmEHuY0RnqYLPmsp203Q="}

View File

@ -1 +0,0 @@
{"Key":"auth/6802ec63-11b0-0ccc-280a-982ad0a90621/user/validuser","Value":"AAAAAQJwFKMpgopAFjJaftTVY/iiawMw4Yj0S3pPDkzMPAfLxxaM3sCjOJt0q/07ozjTharT52wBv+s2ZEurPpr7VKDDzgy4xTMxFJbJs+0VkG3cjxRYEfW3bOIVAHhjLjmxZwYEATh0UUG7bQRNt56+/622bwR99ifWZ6e9zyRDGEwIn74JFN/3dY44qLQZmqfvDUrRQP5RfqDxqVdzbwse61s692Vy/QvlPsRFVRTkZHlNPqxT+OXd"}

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
{"Key":"auth/ac4e0527-a7b2-1b40-1148-dc0dfaf01990/salt","Value":"AAAAAQKDLmmb/XlhfVJ45oKGyYwneS9s3tcQUenB8bTcxuDmAMUWnwG8oNNJFs0mSCF9Yv1KOq3Twxj4qPp05viFnP0z"}

View File

@ -1 +0,0 @@
{"Key":"auth/ac4e0527-a7b2-1b40-1148-dc0dfaf01990/accessor/da42ddc9a483efd8ddeae4ab38428f73d42ad7f6320705f333555fed8593cbe2","Value":"AAAAAQLCu78fbRRgGWG++5XDCfaO/8NTg7LMAJL7aCsrn6c1WHJ5yrAAmWmSs1euhNd7yKUd0lQ0aknCKdPAZFBlAsqgOdnN8JLFe/H9lISaWdU6lRIfgTH9whEXWT0VK25FcS4r5yVe3Qoxg0DfT8FhjuzOa70="}

View File

@ -1 +0,0 @@
{"Key":"auth/ac4e0527-a7b2-1b40-1148-dc0dfaf01990/accessor/e83aed0dd0b867f09aa1dbc88b965eafba6030458d6555712e82c479cee3d2d7","Value":"AAAAAQL7t56z9Fr92ztubIfZPPkV3X1Aljnn95Y/tDXOxn8vjbjf21Fhyj3UnLwWyzK/9ip/6+x2DJBXikBOvXoCqKLXGegZ4JN9Z9UMiQ88aE9Z978r13E/rNbhIUa/PhT5NGwCbOl6vtK2hL06BHxKb+4+goM="}

View File

@ -1 +0,0 @@
{"Key":"auth/ac4e0527-a7b2-1b40-1148-dc0dfaf01990/accessor/e96c348451147331101ad48e157e8056ca1b039ee63a6aafd2d66446c94bcad8","Value":"AAAAAQIcvMn5QMtwELRDXZD9nNf7y/8O6z7u0NUZqyJrBb2OYDRvGpSuPS3CIareSxl8y5F4xtadvhyhunCGBUd289H9foMjfGbVVsM1mbM5i6FDTW0sFOPmXX44mQV29PVNMO+fcLuSWb0+qU4erqylpcvdLW8="}

View File

@ -1 +0,0 @@
{"Key":"auth/ac4e0527-a7b2-1b40-1148-dc0dfaf01990/role/testrole1","Value":"AAAAAQLyV03lH8m3IYxoZKLf+/suZ+2wwKAyIHqrR3QeJZK+68wslLXy0XZ35bPrdc3jzAFhTizqILlgTBHVccdM/pydtTtbsvGHQlWstLaC79GUTM32gS/jwSrbwfa9j0q/Yrdo2LSa9IM5lw2tmYy+xR9c3ZKcm+VADZMZy3+6UmbQ1t0lniZ4uuVmqu2gl3y0732UtdMSxJepPWMjfvVq5+tynhgvEZNGgZCPc9lsV1fcBVFswtBUeATNnSJPmTnxQflXyhitPOpEM+5L+gnEsSNsyinRjv5cSbIHCP5yDzvpiWtwZ5Q0psVRSh/WJppBHcovwbJsTLK/tZ1wtFl1OgU9NLONEpgDJYiDyU0ACeFJ7r+DhjIDrQkr+WITnfBBwI+65wpOPYboqGgd4qZy84PE2s/VhWS5hjpxgpM="}

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