176 Commits

Author SHA1 Message Date
be23129441 prepare release of v1.1.5
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-19 11:10:57 +02:00
199dfd30ca update test dependencies and plugins
All checks were successful
continuous-integration/drone/push Build is passing
2023-08-19 11:06:43 +02:00
b72298f2a8 bump Vault versions in integration tests to 1.14.0 and 1.11.12
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-23 17:43:22 +02:00
226b6ad6c4 split lines above 120 characters
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-17 15:17:06 +02:00
d81fc4e5af reorder members and elements to comply with language recommendations 2023-06-17 15:23:49 +02:00
1195b447a2 fix regression from redundant String mapping in SecretResponse getter
All checks were successful
continuous-integration/drone/push Build is passing
Mapping a JSON string into String using a JSON parser will fail, so we
should use the string directly instead of applying double conversion.

Fixes: f3e1f01e38
2023-06-16 18:18:55 +02:00
f3e1f01e38 fix type conversion in SecretResponse getter method (#67)
Some checks failed
continuous-integration/drone/push Build is failing
Converting the payload using toString() is not an appropriate way to
feed a JSON parser. We now use JSON roundtrip for type mapping and
introduce shortcuts of the type already matches the target type.
2023-06-16 17:43:30 +02:00
622b13f508 prepare release of v1.1.4
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-15 18:08:24 +02:00
0f0b008f2f bump Vault versions in integration tests to 1.13.3 and 1.11.1
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-15 18:03:59 +02:00
34322b389c fix timezone pattern in datetime parsing 2023-06-15 17:58:41 +02:00
7a813cdda3 use common ObjectMapper instance in model unit tests
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-13 21:12:36 +02:00
d9dbdad75b use explicit utf-8 encoding for parsing responses
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-13 18:51:07 +02:00
c97308fb4f deps: update jackson to 2.15.2
Plus some minor dev-dependency updates
2023-06-13 18:51:05 +02:00
fea27910bb minor dev-dependency updates
All checks were successful
continuous-integration/drone/push Build is passing
2023-05-18 18:35:59 +02:00
4d79ef6634 ci: use SONAR_TOKEN env variable instead of sonar.login property
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-29 10:22:53 +02:00
402567a0db deps: update jackson to 2.15.0 2023-04-29 10:21:34 +02:00
00422ea00c ci: build and test with JDK 20
All checks were successful
continuous-integration/drone/push Build is passing
2023-04-06 17:32:50 +02:00
01e1c6e8c7 minor dev-dependency updates 2023-04-06 17:32:01 +02:00
e322a74818 test against Vault 1.13.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-03 20:25:14 +01:00
f9fe57230f prepare release of v1.1.3
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-31 13:56:17 +01:00
e0c33b51e4 update .gitignore
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-29 11:51:52 +01:00
5e6dcc64cd test against Vault 1.12.2
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-29 11:25:00 +01:00
70b7338f3e update copyright notice to 2023 2023-01-29 11:20:59 +01:00
489e450dc8 deps: update jackson to 2.14.2 and mockito to 5.0.0
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-29 11:16:58 +01:00
909eab762c mark deprecated AppID components for removal
All checks were successful
continuous-integration/drone/push Build is passing
The AppID backend is no longer available in the latest Vault releases,
so we plan removal with a following major release.
2022-11-21 12:01:26 +01:00
abba4dff93 ci: enable SonarQube analysis for pull requests
All checks were successful
continuous-integration/drone/push Build is passing
2022-11-21 11:48:11 +01:00
712fab04e1 combine payload map creation into subroutines
We create payload maps with conditional argument sets in several
places. Combine the generation into subroutines, to keep the actual
endpoint calls short and clear.
2022-11-21 11:40:53 +01:00
39ac32a2f6 minor rework of internal path constants 2022-11-21 11:33:47 +01:00
31e20385d4 prepare release of v1.1.2
All checks were successful
continuous-integration/drone/push Build is passing
2022-10-26 10:18:39 +02:00
30996e9fc9 ci: build and test with JDK 19
All checks were successful
continuous-integration/drone/push Build is passing
2022-10-26 10:14:11 +02:00
81decd1a4c deps: update jackson dependency to 2.13.4.2 2022-10-26 10:11:19 +02:00
adedc277fa test against older Vault versions to provide a supported range
Some checks reported errors
continuous-integration/drone/push Build was killed
We've only tested the connector against the latest Vault version
available at release time. However, we do support a wider range.
Test against the oldest supported release, the latest with AppID
and the actual latest.
2022-10-13 11:01:14 +02:00
0c56813121 test against Vault 1.12.0, conditionally disable AppID tests
The AppID auth backend is removed in Vault 1.12 and prevents the process
from starting. We now enable the legacy tests conditionally depending on
the actual Vault version.
2022-10-13 10:46:45 +02:00
2412a291f6 prepare release of v1.1.1
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-29 10:02:41 +02:00
b5ed7704e3 test: scan for "Vault server started" instead of fixed delay
All checks were successful
continuous-integration/drone/push Build is passing
2022-08-29 09:42:23 +02:00
a1626aa1c7 ci: fix drone CI pipeline
All checks were successful
continuous-integration/drone/push Build is passing
The Ubuntu Focal base image does not ship "unzip" by default, so using
the current Maven/JDK image we cannot extract the Vault binary.
Add an additional setup step to solve this problem.
2022-08-16 16:56:28 +02:00
3fb8454711 ci: update CI pipelines and test dependencies
Some checks failed
continuous-integration/drone/push Build is failing
2022-08-14 20:35:54 +02:00
ecf18881b9 test: extend assertion for warnings creating token with custom ID
All checks were successful
continuous-integration/drone/push Build is passing
With Vault 1.11 a second warning is raised. We accept "at least one"
for now.
2022-06-23 18:29:23 +02:00
69287f7196 deps: update jackson dependency to 2.13.2 2022-06-23 18:28:17 +02:00
4b1b4399e1 test against Vault 1.11.0 2022-06-23 18:16:40 +02:00
9ce819b1d0 prepare release of v1.1.0
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-24 17:54:04 +02:00
64e55eddd8 model: add request ID to data response models
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-24 16:44:50 +02:00
feb6e147fe model: add support for (dis)allowed policy glob patterns in TokenRole
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-24 16:40:33 +02:00
b0d2b038eb model: add missing fields to AuthMethod model
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-24 16:32:49 +02:00
f3cc16f44a test: bundle serialization tests into abstract test case
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-19 17:24:17 +02:00
4f3cb4b330 model: eliminate double-mapping of generic data in response classes
Explicitly declare mapping of the "data" field in response classes.
Therefore, the JSON setter setData() is no longer used. SecretResponse
is split into subclasses for secret with and without metadata.
2022-04-19 17:24:16 +02:00
021421a54c model: implement Serializable with model classes
implement equals() and hashCode()
2022-04-19 17:24:09 +02:00
18cb89ace4 test: update to JRE8+ version of Wiremock with Junit5 extension
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-12 21:01:40 +02:00
85aa5c3c30 deps: update Jackson Databind to 2.13.2.2 + minor plugin updates
All checks were successful
continuous-integration/drone/push Build is passing
2022-04-02 10:25:23 +02:00
912b9ec61b test: minor adjustments for integration tests against Vault 1.10
All checks were successful
continuous-integration/drone/push Build is passing
Accept token prefixes "hvb." and "b." as valid results for creation
without specific preferences.
2022-03-24 20:43:00 +01:00
bd0c5b08fe model: add wrap_info to data response model
All checks were successful
continuous-integration/drone/push Build is passing
2022-03-06 18:11:31 +01:00
5f3a36e2c5 update copyright notice to 2022 2022-03-06 18:10:33 +01:00
deb03cc2b0 minor dependency updates 2022-03-06 17:56:52 +01:00
f6180c4f90 model: add migration, recovery and storage type fields to seal status
All checks were successful
continuous-integration/drone/push Build is passing
2022-02-05 13:01:57 +01:00
dc4b62496a model: use correct "replication_performance_mode" JSON field
All checks were successful
continuous-integration/drone/push Build is passing
The official docs incorrectly state "replication_perf_mode" which was
renamed to "replication_performance_mode" way back in Vault 0.9. We now
use the correct field name that is emitted by the API.
2021-12-27 18:54:49 +01:00
dbb21f85bf prepare release of v1.0.1
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-21 11:42:16 +01:00
61dcfc79d3 update test and build dependencies
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-21 11:37:32 +01:00
63e7af552f make system-lambda dependency test-only (#58)
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-21 11:36:35 +01:00
5e2d37797e rename test classes
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-21 11:20:04 +01:00
b4a822bf10 use failsafe plugin for integration tests instead of "offline" profile 2021-11-21 11:19:08 +01:00
4045b1a4fd test against Vault 1.9.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-11-19 20:19:33 +01:00
6a73bc39d3 connector: remove unused DEFAULT_TLS_VERSION field
All checks were successful
continuous-integration/drone/push Build is passing
This constant was left over from the RequestHelper refactoring. It is
not used anywhere in our code and likely not used by any downstream
project, so we remove it now.
2021-10-26 20:05:37 +02:00
75561a0540 passthrough null as port number in builder (#56)
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-12 20:58:59 +02:00
fdda685f6f prepare release of v1.0.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-02 15:46:48 +02:00
6e19e8514f ci: migrate adopt to temruin distribution
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-02 15:14:56 +02:00
1c31b7a5fe auto format JavaDocs
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-02 15:04:00 +02:00
f918f85d20 Merge branch 'main' into develop 2021-10-02 15:02:00 +02:00
7f153df136 add since and removal flags to deprecation annotations 2021-10-02 14:45:26 +02:00
7793b4fc77 open java.util to unnamed module for testing
All checks were successful
continuous-integration/drone/push Build is passing
Illegal access permission is no longer available with JDK 17.
To allow System Lambda to mock the environment, we open the java.util
package explicitly for testing.
2021-10-02 14:40:31 +02:00
7701f9f768 test against Vault 1.8.3 and JDK 17
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-02 14:16:47 +02:00
c60580481b update dependencies to Jackson 2.13
All checks were successful
continuous-integration/drone/push Build is passing
2021-10-02 14:12:56 +02:00
bbceee35f2 test against Vault 1.8.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-28 19:38:06 +02:00
3a920fe960 prepare release 0.9.5
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-28 19:34:48 +02:00
eed61c4569 minor dependency updates
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-27 21:25:20 +02:00
1cd1b63f8d minor dependency updates
All checks were successful
continuous-integration/drone/push Build is passing
2021-07-10 15:08:33 +02:00
e81dd87fe1 clean up assertions and messages in unit tests
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-16 20:20:09 +02:00
f6037e31bb introduce modularity (#55) 2021-06-15 21:44:16 +02:00
74092bba9a use plain JUnit for test assertions
All checks were successful
continuous-integration/drone/push Build is passing
Hamcrest is a beautiful library, but we try to keep things simple here
and switch to plain JUnit 5 assertions for testing.
2021-06-12 13:10:11 +02:00
3c11fe912b enforce use of builder to create a new HTTPVaultConnector (#54)
All checks were successful
continuous-integration/drone/push Build is passing
Remove constructors of HTTPVaultConnector and make the builder
constructor package-private to enforce use of .builder()....build()

For convenience we add direct builder constructors with a full URI
argument to allow a one-line initialization if necessary.
2021-06-12 12:01:52 +02:00
53d954ea12 deprecate all convenience methods to interact with "secret/" mount
All checks were successful
continuous-integration/drone/push Build is passing
Follow-up deprecation for the not yet deprecated wrapper methods.
2021-06-12 10:46:10 +02:00
71564e87e8 remove convenience methods to interact with "secret/" mount (#53)
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-12 10:44:41 +02:00
e578591a49 deprecate convenience methods to interact with "secret/" mount (#52)
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-11 21:33:59 +02:00
de17f48be2 move builder into main package, introduce new invocation method (#51)
Some checks failed
continuous-integration/drone/push Build is failing
The builder is target of major refactoring in the 1.0 development branch
so we introduce some delegate classes and methods to prepare migration.
2021-06-11 21:15:49 +02:00
ce24de7347 move builder class into main package, deprecate interface
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-11 20:49:38 +02:00
f783286909 pass builder as constructor parameter directly
With increasing number of options the constructors become quite overloaded.
We now pass the builder as only argument instead.
2021-06-11 20:49:38 +02:00
9ef709e3eb specify version and removal flag for remaining deprecations 2021-06-08 18:35:01 +02:00
ce28b8eb60 use local variable type inference where reasonable
Local variables with obvious type on both sides of their declaration
use type inference now for more concise code. Some variable names are
given a more precise name though.
2021-06-08 18:24:19 +02:00
587c6cde0a add CONTRIBUTING.md [skip ci] 2021-06-06 15:05:21 +02:00
dab42816a7 add CONTRIBUTING.md [skip ci] 2021-06-06 15:02:47 +02:00
9346619237 remove deprecated SecretResponse#getValue() convenience method
All checks were successful
continuous-integration/drone/push Build is passing
This method was deprecated since 0.5 and is basically a wrapper for
the more generic get("value").
2021-06-06 14:47:56 +02:00
df466a4dd2 remove deprecated AppRole- and TokenBuilder
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-06 12:56:36 +02:00
258a852f5c Merge branch 'main' into develop
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-06 12:34:42 +02:00
5f9950e048 prepare release 0.9.4
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-06 12:22:13 +02:00
e2c439379e switch to "main" as default branch name
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-06 12:09:28 +02:00
ce33d37396 minor dependency updates; test against Vault 1.7.2
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-06 12:07:02 +02:00
bdf4fc4b83 fix typo in method AppRole.Builder#wit0hTokenPeriod (#49) 2021-06-06 12:02:01 +02:00
0f3ebc0bde remove deprecated builder-style methods in InvalidResponseException
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-03 14:08:10 +02:00
ec4fbc5d3f remove deprecated factory classes (#46)
All checks were successful
continuous-integration/drone/push Build is passing
VaultConnectorFactory and its implementation have been deprecated since
0.8 in favor of VaultConnectorBuilder. Finally remove the old classes.
2021-06-03 11:46:24 +02:00
4e2b8857e9 use GitHub actions for CI (#48)
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-03 11:40:09 +02:00
3485839553 plugin and dependency updates
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-01 20:47:58 +02:00
36102326db use WireMock for offline tests
All checks were successful
continuous-integration/drone/push Build is passing
2021-06-01 18:54:16 +02:00
60d94fc5bb use immutable Map.of() for fixed-size payloads 2021-06-01 18:54:16 +02:00
8dfcf02a0a refactor RequestHelper use Java 11 instead of Apache HTTPClient 2021-06-01 18:54:16 +02:00
c45dbf014e raise language level to Java 11 2021-06-01 18:54:16 +02:00
f7d6f9384d switch to "main" as default branch name
All checks were successful
continuous-integration/drone/push Build is passing
2021-05-01 19:48:33 +02:00
50d485fab8 clean up unused imports
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-02 11:27:24 +02:00
5b508374d9 prepare release 0.9.3
All checks were successful
continuous-integration/drone/push Build is passing
2021-04-02 11:16:04 +02:00
fbc61e065f fix argline for JDK 1.8 unit tests
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-29 21:13:34 +02:00
56a52cb22a fix argline for JDK 16 unit tests
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-29 20:50:16 +02:00
c43ec190ca use SystemLambda instead of custom environment mocks 2021-03-29 20:49:44 +02:00
639d0e3c5b Jackson 2.12.2, test against Vault 1.7.0
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-29 18:56:24 +02:00
8e97f3c1dd build with JDK 16, test against Vault 1.6.3
All checks were successful
continuous-integration/drone/push Build is passing
2021-03-20 12:13:19 +01:00
c04d940a80 test: close static mocks
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-28 13:03:25 +01:00
76a5ea4fe9 test: use assertThrows instead of try-catch blocks
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-28 12:59:06 +01:00
2b0f458da3 use pre-sized maps for fixed-size payloads
All checks were successful
continuous-integration/drone/push Build is passing
2021-02-28 10:52:36 +01:00
63278c09c8 constructors of abstract VaultConnectorException protected
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 14:57:14 +01:00
600f3d0d0f test classes package-private; use mockStatic()
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 14:52:48 +01:00
1a19eaa87d release 0.9.2
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 12:26:29 +01:00
a2dde38348 Jackson 2.12.1
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 12:24:09 +01:00
dfb6d0a37c only initialize trust managers if CA certificate is provided (#43)
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-24 12:20:45 +01:00
b46b59e4a0 update copyright notice to 2021
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-03 11:56:33 +01:00
79ec536876 prepare release of v0.9.1
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-03 11:48:51 +01:00
00d4e9acef test against Vault 1.6.1
All checks were successful
continuous-integration/drone/push Build is passing
2021-01-03 11:46:40 +01:00
48eccedad0 introduce maven profile for dependency analysis 2021-01-03 11:44:34 +01:00
ec4c4ac868 rework Travis CI configuration
Separate stages do not work as intended.
Add LTS and latest JDK (8, 11, 15) to build roster and run analysis with JDK 11 only.
2020-12-12 11:52:43 +01:00
90835aea92 dependency updates
All checks were successful
continuous-integration/drone/push Build is passing
Jackson 2.12, HTTPClient 4.5.13 and some test dependencies
2020-12-12 11:18:17 +01:00
32eed75de8 use batch mode in CI builds
All checks were successful
continuous-integration/drone/push Build is passing
Do not bloat the log with progress output.
2020-12-02 10:14:16 +01:00
c8ca5c4091 test against Vault 1.6.0
All checks were successful
continuous-integration/drone/push Build is passing
2020-11-12 19:48:08 +01:00
4ba81492d5 docs: update Travis CI badge
All checks were successful
continuous-integration/drone/push Build is passing
2020-09-08 21:28:16 +02:00
84b9877ca9 test against Vault 1.5.0 2020-07-26 14:12:13 +02:00
b5c9a3b35b minor CI configuration adjustments
All checks were successful
continuous-integration/drone/push Build is passing
2020-04-29 16:44:55 +02:00
9d7f501b1a Merge branch 'release/0.9.0' 2020-04-29 15:53:06 +02:00
aab76273a5 last minute JavaDoc corrections 2020-04-29 15:47:20 +02:00
4fb63f0977 prepare release 0.9.0 2020-04-29 15:24:16 +02:00
1d5db0c365 add missing fields to Token model and builder (#41)
* explicit_max_ttl
* period
* entity_alias
2020-04-26 18:04:35 +02:00
9f80a7dada typo fixes 2020-04-25 13:04:39 +02:00
46461f482a add coverage profile to maven project 2020-04-25 12:33:22 +02:00
be7aa865d8 rework CI configuration for multi-stage job 2020-04-25 11:39:50 +02:00
fa7036921a move builders into model classes and deprecate constructors
Enforces use of builder pattern in future releases. Builder API is
unchanged despite the class itself.
2020-04-15 17:29:50 +02:00
e0cbe34881 minor JavaDoc correction
[skip ci]
2020-04-15 16:29:42 +02:00
dcb8d6067a update AppRole model and builder to current API
Add missing JSON fields and remove unprefixed, already deprecated fields
2020-04-15 16:28:14 +02:00
fc9e429bd1 add support for token roles (#27) (#37) 2020-04-13 17:12:55 +02:00
94d1d2c80b test against Vault 1.4.0 2020-04-08 14:25:16 +02:00
edb9194153 Update copyright notice to 2020
Better late than never, at least before the next release... [skip ci]
2020-04-08 14:23:48 +02:00
c0708bd288 implement methods for token role handling (#27)
Create, update, read, delete and list token roles is now possible.
2020-04-06 18:36:42 +02:00
f54ba38cf5 implement TokenRole metamodel and corresponding builder 2020-04-06 17:58:11 +02:00
8f10bbfed7 add missing fields to token data
* entity_id
* expire_time
* explicit_max_ttl
* issue_time
* renewable
* type
2020-03-29 14:29:06 +02:00
a4a0e13904 add missing fields to auth response
* token_policies
* entity_id
* token_type
* orphan
2020-03-29 14:29:06 +02:00
df696e9f17 add token type to model and builder classes 2020-03-29 14:29:06 +02:00
83a05fcd40 correctly map token policies on lookup (close #35)
Remove superfluous "role" flag and add "policies" list instead.
2020-03-29 13:59:06 +02:00
d564ba9365 update dependencies and plugins for JDK 14 build environments 2020-03-29 13:01:25 +02:00
071eeda423 correclty map token meta in lookup response (fix #34) 2020-03-29 12:56:06 +02:00
4788fa7272 Test against Vault 1.4.0-rc1 2020-03-25 10:21:10 +01:00
21d544c2c7 Test against Vault 1.3.0 2019-11-20 09:43:44 +01:00
a944fce6c5 Test against Vault 1.3.0 2019-11-20 09:43:11 +01:00
9710d59821 prepare release of v0.8.2 2019-10-20 14:04:47 +02:00
30d426303e add distribution profiles to POM 2019-10-20 14:04:47 +02:00
ca9da6bd0f minor dependency updated and cleanup 2019-10-20 13:49:14 +02:00
9fedb3f88b replace legacy JUnit 4 rule for system mock by custom helper 2019-10-20 13:48:41 +02:00
596a097707 fix API endpoint for token lookup 2019-10-16 18:00:45 +02:00
d51421cb14 update Jackson dependency to 2.10 2019-10-03 11:45:08 +02:00
ffea9bfbfe update test environment for JDK 13 builds 2019-10-03 11:42:14 +02:00
ffdc0f6a27 Test against Vault 1.2.2 2019-10-03 11:09:39 +02:00
9b5c537e55 fix SonarCloud link in ReadMe [skip ci] 2019-08-17 14:57:24 +02:00
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
145 changed files with 7744 additions and 5364 deletions

View File

@ -1,33 +1,50 @@
kind: pipeline
name: default
clone:
disable: true
steps:
- name: clone
image: plugins/git
settings:
depth: 10
skip_verify: true
- name: test-online
image: maven:3-jdk-11-slim
commands:
- export VAULT_VERSION=1.1.0
- 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
- mv vault /bin/
- mvn clean test
when:
branch:
- master
- name: test-offline
image: maven:3-jdk-11-slim
commands:
- mvn clean test -Dtest='!HTTPVaultConnectorTest'
when:
branch:
- develop
- feature/*
- name: compile
image: maven:3-eclipse-temurin-17
commands:
- mvn -B clean compile
when:
branch:
- main
- develop
- feature/*
- fix/*
- release/*
- name: unit-tests
image: maven:3-eclipse-temurin-17
commands:
- mvn -B test
when:
branch:
- develop
- feature/*
- fix/*
- name: setup-vault
image: alpine:latest
environment:
VAULT_VERSION: 1.14.0
commands:
- wget -q -O vault_$${VAULT_VERSION}_linux_amd64.zip https://releases.hashicorp.com/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_linux_amd64.zip
- wget -q -O - https://releases.hashicorp.com/vault/$${VAULT_VERSION}/vault_$${VAULT_VERSION}_SHA256SUMS | grep linux_amd64 | sha256sum -c
- unzip vault_$${VAULT_VERSION}_linux_amd64.zip
- rm vault_$${VAULT_VERSION}_linux_amd64.zip
- mkdir -p .bin
- mv vault .bin/
when:
branch:
- main
- release/*
- name: unit-integration-tests
image: maven:3-eclipse-temurin-17
environment:
VAULT_VERSION: 1.14.0
commands:
- export PATH=.bin:$${PATH}
- mvn -B -P integration-test verify
when:
branch:
- main
- release/*

50
.github/workflows/ci.yml vendored Normal file
View File

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

21
.gitignore vendored
View File

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

View File

@ -1,28 +0,0 @@
branches:
only:
- master
language: java
jdk:
- openjdk11
install: true
addons:
sonarcloud:
organization: "stklcode-github"
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:. VAULT_VERSION=1.1.0
before_script:
- 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
cache:
directories:
- '$HOME/.m2/repository'
- '$HOME/.sonar/cache'
script:
- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar
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,3 +1,211 @@
## 1.1.5 (2023-08-19)
### Fix
* Fixed JSON type conversion in `SecretResponse#get(String, Class)` (#67)
## 1.1.4 (2023-06-15)
### Fix
* Use `[+-]XX:XX` notation for timezone in date/time parsing
### Improvements
* Use explicit UTF-8 encoding for parsing responses
### Dependencies
* Updated Jackson to 2.15.2
### Test
* Tested against Vault 1.2.0 to 1.13.3
## 1.1.3 (2023-01-31)
### Deprecations
* AppID components (deprecated since 0.4) are marked for removal with the next major release
### Dependencies
* Updated Jackson to 2.14.2
### Improvements
* Minor internal refactoring
### Test
* Tested against Vault 1.2.0 to 1.12.2
## 1.1.2 (2022-10-26)
### Dependencies
* Updated Jackson to 2.13.4.2
### Test
* Tested against Vault 1.2.0 to 1.12.0
* Disable AppID tests for Vault 1.12 and above (auth method removed)
* Tested with Java 19
## 1.1.1 (2022-08-29)
### Dependencies
* Updated Jackson to 2.13.3
### Test
* Tested against Vault 1.11.2
* Tested with Java 18
## 1.1.0 (2022-04-24)
### Fix
* Use `replication_performance_mode` instead of `replication_perf_mode` in health response.
### Improvements
* Add `migration`, `recovery_seal` and `storage_type` fields to `SealReponse` model
* Add support for `wrap_info` in data response models
* Dependency updates
* Model and response classes implement `Serializable` (#57)
* Split `SercretResponse` into `PlainSecretResponse` and `MetaSecretResponse` subclasses (common API unchanged)
* Add missing fields to `AuthMethod` model
* Add support for (dis)allowed policy glob patterns in `TokenRole`
* Add request ID to data response models
### Test
* Tested against Vault 1.10.1
## 1.0.1 (2021-11-21)
### Fix
* Make `HTTPVaultConnectorBuilder#withPort(Integer)` null-safe (#56)
* Make system-lambda dependency test-only (#58)
### Test
* Tested against Vault 1.9.0
## 1.0.0 (2021-10-02)
### Breaking
* Requires Java 11 or later
* Builder invocation has changed, use `HTTPVaultConnector.builder()....build()`
### Removal
* Remove deprecated `VaultConnectorFactory` in favor of `VaultConnectorBuilder` with identical API
* Remove deprecated `AppRoleBuilder` and `TokenBuilder` in favor of `AppRole.Builder` and `Token.Builder`
* Remove deprecated `Period`, `Policy` and `Policies` methods from `AppRole` in favor of `Token`-prefixed versions
* Remove deprecated `SecretResponse#getValue()` method, use `get("value")` instead
* Remove deprecated convenience methods for interaction with "secret" mount
### Improvements
* Use pre-sized map objects for fixed-size payloads
* Remove Apache HTTP Client dependency in favor of Java 11 HTTP
* Introduce Java module descriptor
### Test
* Tested against Vault 1.8.3
## 0.9.5 (2021-07-28)
### Deprecations
* Deprecate ` {read,write,delete}Secret()` convenience methods. Use `{read,write,delete}("secret/...")` instead (#52)
* Deprecated builder invocation `VaultConnectorBuilder.http()` in favor of `HTTPVaultConnector.builder()` (#51)
* Deprecated `de.stklcode.jvault.connector.builder.HTTPVaultConnectorBuilder` in favor of `de.stklcode.jvault.connector.HTTPVaultConnectorBuilder` (only package changed) (#51)
Old builders will be removed in 1.0
### Improvements
* Minor dependency updates
### Test
* Tested against Vault 1.8.0
## 0.9.4 (2021-06-06)
### Deprecations
* `AppRole.Builder#wit0hTokenPeriod()` is deprecated in favor of `#withTokenPeriod()` (#49)
### Improvements
* Minor dependency updates
### Test
* Tested against Vault 1.7.2
## 0.9.3 (2021-04-02)
### Improvements
* Use pre-sized map objects for fixed-size payloads
* Minor dependency updates
* Unit test adjustments for JDK 16 build environments
### Test
* Tested against Vault 1.7.0
## 0.9.2 (2021-01-24)
### Fixes
* Only initialize custom trust managers, if CA certificate is actually provided (#43)
### Improvements
* Minor dependency updates
## 0.9.1 (2021-01-03)
### Improvements
* Dependency updates
### Test
* Tested against Vault 1.6.1
## 0.9.0 (2020-04-29)
### Fixes
* Correctly parse Map field for token metadata (#34)
* Correctly map token policies on token lookup (#35)
### Features
* Support for token types (#26)
* Support for token role handling (#27) (#37)
### Improvements
* Added `entity_id`, `token_policies`, `token_type` and `orphan` flags to auth response
* Added `entity_id`, `expire_time`, `explicit_max_ttl`, `issue_time`, `renewable` and `type` flags to token data
* Added `explicit_max_ttl`, `period` and `entity_alias` flags to _Token_ model (#41)
* Added `enable_local_secret_ids`, `token_bound_cidrs`, `token_explicit_max_ttl`, `token_no_default_policy`,
`token_num_uses`, `token_period` and `token_type` flags to _AppRole_ model
* Minor dependency updates
### Deprecations
* `AppRole#getPolicies()` and `#setPolicies()` are deprecated in favor of `#getTokenPolicies()` and `#setTokenPolicies()`
* `AppRole#getPeriod()` is deprecated in favor of `#getTokenPeriod()`
* `AppRoleBuilder` and `TokenBuilder` in favor of `AppRole.Builder` and `Token.Builder`
* All-arg constructors of `AppRole` and `Token` in favor of `.builder()....build()` introduced in 0.8
### Removals
* Deprecated methods `AppRole#getBoundCidrList()`, `#setBoundCidrList()` and `getBoundCidrListString()` have been removed.
### Test
* Tested against Vault 1.4.0
## 0.8.2 (2019-10-20)
### Fixes
* Fixed token lookup (#31)
### Improvements
* Updated dependencies
## 0.8.1 (2019-08-16)
### Fixes
* Removed compile dependency to JUnit library (#30)
### Improvements
* Updated dependencies
### Test
* Tested against Vault 1.2.2
## 0.8.0 (2019-03-24)
### Breaking
* Moved Maven artifact to `de.stklcode.jvault:jvault-connector` (#28)

112
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,112 @@
# How to contribute
As for all great Open Source projects, contributions in form of bug reports and code are welcome and important to keep the project alive.
In general, this project follows the [GitHub Flow](https://guides.github.com/introduction/flow/).
Fork the project, commit your changes to your branch, open a pull request and it will probably be merged.
However, to ensure maintainability and quality of the code, there are some guidelines you might be more or less familiar with.
For that purpose, this document describes the important points.
## Opening an Issue
If you experience any issues with the library or the code, don't hesitate to file an issue.
### Bug Reports
Think you found a bug?
Please clearly state what happens and describe your environment to help tracking down the issue.
* Which version of the connector are you running?
* Which version of Java (architecture and OS if relevant)?
* Which version of Vault?
### Feature Requests
Missing a feature or like to have certain functionality enhanced?
No problem, please open an issue and describe what and why you think this change is required.
## Pull Requests
If you want to contribute your code to solve an issue or implement a desired feature yourself, you might open a pull request.
If the changes introduce new functionality or affect major parts of existing code, please consider opening an issue for discussion first.
Extending or adapting JUnit test cases would be nice (no hard criterion though).
The `main` branch also be target for most pull requests.
However, if it features new functionality you might want to target the `develop` branch instead (see next section for details on branches).
### Branches
The `main` branch represents the current, more or less stable state of development.
Please ensure your initial code is up to date with it at the time you start development.
In addition, this project features a `develop` branch, which holds bleeding edge developments, not necessarily considered stable or even compatible.
Do not expect this code to run smoothly, but you might have a look into the history to see if some work on an issue has already been started there.
For fixes and features, there might be additional branches, likely prefixed by `fix/` or `feature/` followed by an issue number (if applicable) and/or a title.
Feel free to adapt this naming scheme to your forks.
### Merge Requirements
To be merged into the main branch, your code has to pass the automated continuous integration tests, to ensure compatibility.
In addition, your code has to be approved by a project member.
#### What if my code fails the tests?
Don't worry, you can submit your PR anyway.
The reviewing process might help you to solve remaining issues.
### Commit messages
Please use speaking titles and messages for your commits, to ensure a transparent history.
If your patch fixes an issue, reference the ID in the first line.
If you feel like you have to _briefly_ explain your changes, do it (for long explanations and discussion, consider opening an issue or describe in the PR).
**Example commit:**
```text
Fix nasty bug from #1337
This example commit fixes the issue that some people write non-speaking commit messages like 'done magic'.
A short description is helpful sometimes.
```
You might sign your work, although that's no must.
### When will it be merged?
Short answer: When it makes sense.
Bugfixes should be merged in time - assuming they pass the above criteria.
New features might be assigned to a certain milestone and as a result of this be scheduled according to the planned release cycle.
## Compatibility
To ensure usability for a wide range of users, please take note on the software requirements stated in the `README`.
This includes especially Java versions and a minimum version of _Vault_.
If you are unsure if your code matches these versions, the test will probably tell you.
In case you think, your change is more important than maintaining backwards compatibility, please start a discussion to see,
if we might increase the minimum version or find a workaround for legacy systems.
## Build Environment
All you need to start off - besides your favorite IDE and a JDK of course - is [Maven](https://maven.apache.org/).
## Unit Tests
The code is tested by JUnit tests.
For standalone testing against mocked APIs the _Maven_ profile `offline-test` should be used.
Otherwise, there is a test suite that requires an actual _Vault_ binary in the executable path to start a real server instance.
## Continuous Integration
Automated tests are run using [GitHub Actions](https://github.com/features/actions) for every commit including pull requests.
Tests usually run against the minimal supported version, all supported LTS versions and the latest version of Java.
There is an automated code quality analysis pushing results to [SonarCloud](https://sonarcloud.io/dashboard?id=de.stklcode.jvault%3Ajvault-connector).
## Still Open Questions?
If anything is still left unanswered and you're unsure if you got it right, don't hesitate to contact a team member.
In any case you might submit your request/issue anyway, we won't refuse good code only for formal reasons.

View File

@ -1,18 +1,18 @@
# 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/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)
[![CI Status](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml/badge.svg)](https://github.com/stklcode/jvaultconnector/actions/workflows/ci.yml)
[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=de.stklcode.jvault%3Ajvault-connector&metric=alert_status)](https://sonarcloud.io/dashboard?id=de.stklcode.jvault%3Ajvault-connector)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/stklcode/jvaultconnector/blob/main/LICENSE.txt)
[![Maven Central](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)
![Logo](https://raw.githubusercontent.com/stklcode/jvaultconnector/main/assets/logo.png)
Java Vault Connector is a connector library for [Vault](https://www.vaultproject.io) by [Hashicorp](https://www.hashicorp.com) written in Java. The connector allows simple usage of Vault's secret store in own applications.
## Features:
* HTTP(S) backend connector
* Ability to provide or enforce custom CA certificate
* Ability to provide or enforce custom CA certificate
* Optional initialization from environment variables
* Authorization methods
* Token
@ -20,8 +20,8 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* AppRole (register and authenticate)
* AppID (register and authenticate) [_deprecated_]
* Tokens
* Creation and lookup of tokens
* TokenBuilder for speaking creation of complex configuraitons
* Creation and lookup of tokens and token roles
* TokenBuilder for speaking creation of complex configurations
* Secrets
* Read secrets
* Write secrets
@ -32,7 +32,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
* SQL secret handling
* KV v1 and v2 support
* Connector Factory with builder pattern
* Tested against Vault 1.1.0
* Tested against Vault 1.2 to 1.14
## Maven Artifact
@ -40,7 +40,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
<dependency>
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>0.8.0</version>
<version>1.1.5</version>
</dependency>
```
@ -50,21 +50,19 @@ 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 = VaultConnectorBuilder.http()
VaultConnector vault = HTTPVaultConnector.builder()
.withHost("127.0.0.1")
.withPort(8200)
.withTLS()
.build();
// Instantiate with custom SSL context
VaultConnector vault = VaultConnectorBuilder.http()
.withHost("example.com")
.withPort(8200)
VaultConnector vault = HTTPVaultConnector.builder("https://example.com:8200/v1/")
.withTrustedCA(Paths.get("/path/to/CA.pem"))
.build();
// Initialization from environment variables
VaultConnector vault = VaultConnectorBuilder.http()
VaultConnector vault = HTTPVaultConnector.builder()
.fromEnv()
.build();
```
@ -86,20 +84,20 @@ vault.authAppRole("01234567-89ab-cdef-0123-456789abcdef", "fedcba98-7654-3210-fe
```java
// Retrieve secret (prefix "secret/" assumed, use read() to read arbitrary paths)
String secret = vault.readSecret("some/secret/key").get("value", String.class);
String secret = vault.read("secret/some/key").get("value", String.class);
// Complex secret.
Map<String, Object> secretData = vault.readSecret("another/secret/key").getData();
Map<String, Object> secretData = vault.read("secret/another/key").getData();
// Write simple secret.
vault.writeSecret("new/secret/key", "secret value");
vault.write("secret/new/key", "secret value");
// Write complex data to arbitraty path.
// Write complex data.
Map<String, Object> map = ...;
vault.write("any/path/to/write", map);
vault.write("path/to/write", map);
// Delete secret.
vault.delete("any/path/to/write");
vault.delete("path/to/delete");
```
### Token and role creation
@ -120,10 +118,10 @@ AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
## Links
[Project Page](http://jvault.stklcode.de)
[Project Page](https://jvault.stklcode.de)
[JavaDoc API](http://jvault.stklcode.de/apidocs/)
[JavaDoc API](https://jvault.stklcode.de/apidocs/)
## License
The project is licensed under [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
The project is licensed under [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).

335
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>de.stklcode.jvault</groupId>
<artifactId>jvault-connector</artifactId>
<version>0.8.0</version>
<version>1.1.5</version>
<packaging>jar</packaging>
@ -16,20 +16,16 @@
<licenses>
<license>
<name>Apache License 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<url>https://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<developers>
<developer>
<name>Stefan Kalscheuer</name>
<email>stefan@stklcode.de</email>
<timezone>+1</timezone>
<timezone>Europe/Berlin</timezone>
</developer>
</developers>
@ -44,34 +40,104 @@
<url>https://github.com/stklcode/jvaultconnector/issues</url>
</issueManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<argLine></argLine>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.4.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-lambda</artifactId>
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8</artifactId>
<version>2.35.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>nl.jqno.equalsverifier</groupId>
<artifactId>equalsverifier</artifactId>
<version>3.15.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</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>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
<artifactId>maven-clean-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<argLine>
@{argLine}
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.databind
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>3.1.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.1</version>
<version>3.3.0</version>
<configuration>
<archive>
<manifestEntries>
@ -82,87 +148,45 @@
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
<version>3.1.2</version>
<configuration>
<reuseForks>false</reuseForks>
<argLine>
@{argLine}
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.exception=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.model=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.model.response=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.model.response.embedded=ALL-UNNAMED
--add-opens de.stklcode.jvault.connector/de.stklcode.jvault.connector.test=com.fasterxml.jackson.databind
</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.10</version>
</plugin>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
</plugins>
</pluginManagement>
</build>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.11</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.4.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-migrationsupport</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-junit</artifactId>
<version>2.0.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.stefanbirkner</groupId>
<artifactId>system-rules</artifactId>
<version>1.17.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.25.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>2.25.1</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>
@ -174,7 +198,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<executions>
<execution>
<id>attach-sources</id>
@ -198,7 +221,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.1.0</version>
<version>3.5.0</version>
<configuration>
<source>11</source>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
@ -211,5 +237,124 @@
</plugins>
</build>
</profile>
<profile>
<id>sign</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<keyname>${gpg.keyname}</keyname>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>coverage</id>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<execution>
<id>default-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>default-report</id>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>integration-test</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>dependency-check</id>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>8.3.1</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>sonatype</id>
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
</profile>
<profile>
<id>local</id>
<distributionManagement>
<repository>
<id>stklcode</id>
<url>${dist.repo.local}</url>
</repository>
<snapshotRepository>
<id>stklcode</id>
<url>${dist.repo.local.snapshot}</url>
</snapshotRepository>
</distributionManagement>
</profile>
</profiles>
</project>

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,50 +20,55 @@ import de.stklcode.jvault.connector.exception.AuthorizationRequiredException;
import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.internal.RequestHelper;
import de.stklcode.jvault.connector.model.AppRole;
import de.stklcode.jvault.connector.model.AppRoleSecret;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.Token;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import java.security.cert.X509Certificate;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
/**
* Vault Connector implementatin using Vault's HTTP API.
* Vault Connector implementation using Vault's HTTP API.
*
* @author Stefan Kalscheuer
* @since 0.1
*/
public class HTTPVaultConnector implements VaultConnector {
private static final String PATH_PREFIX = "/v1/";
private static final String PATH_SEAL_STATUS = "sys/seal-status";
private static final String PATH_SEAL = "sys/seal";
private static final String PATH_UNSEAL = "sys/unseal";
private static final String PATH_RENEW = "sys/leases/renew";
private static final String PATH_AUTH = "sys/auth";
private static final String PATH_TOKEN = "auth/token";
private static final String PATH_SYS = "sys";
private static final String PATH_SYS_AUTH = PATH_SYS + "/auth";
private static final String PATH_RENEW = PATH_SYS + "/leases/renew";
private static final String PATH_REVOKE = PATH_SYS + "/leases/revoke/";
private static final String PATH_HEALTH = PATH_SYS + "/health";
private static final String PATH_SEAL = PATH_SYS + "/seal";
private static final String PATH_SEAL_STATUS = PATH_SYS + "/seal-status";
private static final String PATH_UNSEAL = PATH_SYS + "/unseal";
private static final String PATH_AUTH = "auth";
private static final String PATH_AUTH_TOKEN = PATH_AUTH + "/token";
private static final String PATH_LOOKUP = "/lookup";
private static final String PATH_CREATE = "/create";
private static final String PATH_ROLES = "/roles";
private static final String PATH_CREATE_ORPHAN = "/create-orphan";
private static final String PATH_AUTH_USERPASS = "auth/userpass/login/";
private static final String PATH_AUTH_APPID = "auth/app-id/";
private static final String PATH_AUTH_APPROLE = "auth/approle/";
private static final String PATH_AUTH_APPROLE_ROLE = "auth/approle/role/%s%s";
private static final String PATH_REVOKE = "sys/leases/revoke/";
private static final String PATH_HEALTH = "sys/health";
private static final String PATH_AUTH_USERPASS = PATH_AUTH + "/userpass/login/";
private static final String PATH_AUTH_APPID = PATH_AUTH + "/app-id";
private static final String PATH_AUTH_APPROLE = PATH_AUTH + "/approle";
private static final String PATH_AUTH_APPROLE_ROLE = PATH_AUTH_APPROLE + "/role/%s%s";
private static final String PATH_DATA = "/data/";
private static final String PATH_METADATA = "/metadata/";
private static final String PATH_LOGIN = "/login";
private static final String PATH_DELETE = "/delete/";
private static final String PATH_UNDELETE = "/undelete/";
private static final String PATH_DESTROY = "/destroy/";
public static final String DEFAULT_TLS_VERSION = "TLSv1.2";
private final RequestHelper request;
private boolean authorized = false; // Authorization status.
@ -71,148 +76,54 @@ public class HTTPVaultConnector implements VaultConnector {
private long tokenTTL = 0; // Expiration time for current token.
/**
* Create connector using hostname and schema.
* Create connector using a {@link HTTPVaultConnectorBuilder}.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param builder The builder.
*/
public HTTPVaultConnector(final String hostname, final boolean useTLS) {
this(hostname, useTLS, null);
HTTPVaultConnector(final HTTPVaultConnectorBuilder builder) {
this.request = new RequestHelper(
((builder.isWithTLS()) ? "https" : "http") + "://" +
builder.getHost() +
((builder.getPort() != null) ? ":" + builder.getPort() : "") +
builder.getPrefix(),
builder.getNumberOfRetries(),
builder.getTimeout(),
builder.getTlsVersion(),
builder.getTrustedCA()
);
}
/**
* Create connector using hostname, schema and port.
* Get a new builder for a connector.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param port The port
* @return Builder instance.
* @since 0.9.5
*/
public HTTPVaultConnector(final String hostname, final boolean useTLS, final Integer port) {
this(hostname, useTLS, port, PATH_PREFIX);
public static HTTPVaultConnectorBuilder builder() {
return new HTTPVaultConnectorBuilder();
}
/**
* Create connector using hostname, schema, port and path.
* Get a new builder for a connector.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param port The port
* @param prefix HTTP API prefix (default: /v1/)
* @param baseURL Base URL.
* @return Builder instance.
* @throws URISyntaxException Invalid URI syntax.
* @since 1.0
*/
public HTTPVaultConnector(final String hostname, final boolean useTLS, final Integer port, final String prefix) {
this(((useTLS) ? "https" : "http")
+ "://" + hostname
+ ((port != null) ? ":" + port : "")
+ prefix);
public static HTTPVaultConnectorBuilder builder(String baseURL) throws URISyntaxException {
return new HTTPVaultConnectorBuilder().withBaseURL(baseURL);
}
/**
* Create connector using hostname, schema, port, path and trusted certificate.
* Get a new builder for a connector.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param port The port
* @param prefix HTTP API prefix (default: /v1/)
* @param trustedCaCert Trusted CA certificate
* @param baseURL Base URL.
* @return Builder instance.
* @since 1.0
*/
public HTTPVaultConnector(final String hostname,
final boolean useTLS,
final Integer port,
final String prefix,
final X509Certificate trustedCaCert) {
this(hostname, useTLS, DEFAULT_TLS_VERSION, port, prefix, trustedCaCert, 0, null);
}
/**
* Create connector using hostname, schema, port, path and trusted certificate.
*
* @param hostname The hostname
* @param useTLS If TRUE, use HTTPS, otherwise HTTP
* @param tlsVersion TLS version
* @param port The port
* @param prefix HTTP API prefix (default: /v1/)
* @param trustedCaCert Trusted CA certificate
* @param numberOfRetries Number of retries on 5xx errors
* @param timeout Timeout for HTTP requests (milliseconds)
*/
public HTTPVaultConnector(final String hostname,
final boolean useTLS,
final String tlsVersion,
final Integer port,
final String prefix,
final X509Certificate trustedCaCert,
final int numberOfRetries,
final Integer timeout) {
this(((useTLS) ? "https" : "http")
+ "://" + hostname
+ ((port != null) ? ":" + port : "")
+ prefix,
trustedCaCert,
numberOfRetries,
timeout,
tlsVersion);
}
/**
* Create connector using full URL.
*
* @param baseURL The URL
*/
public HTTPVaultConnector(final String baseURL) {
this(baseURL, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param trustedCaCert Trusted CA certificate
*/
public HTTPVaultConnector(final String baseURL, final X509Certificate trustedCaCert) {
this(baseURL, trustedCaCert, 0, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param trustedCaCert Trusted CA certificate
* @param numberOfRetries Number of retries on 5xx errors
*/
public HTTPVaultConnector(final String baseURL, final X509Certificate trustedCaCert, final int numberOfRetries) {
this(baseURL, trustedCaCert, numberOfRetries, null);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param trustedCaCert Trusted CA certificate
* @param numberOfRetries Number of retries on 5xx errors
* @param timeout Timeout for HTTP requests (milliseconds)
*/
public HTTPVaultConnector(final String baseURL,
final X509Certificate trustedCaCert,
final int numberOfRetries,
final Integer timeout) {
this(baseURL, trustedCaCert, numberOfRetries, timeout, DEFAULT_TLS_VERSION);
}
/**
* Create connector using full URL and trusted certificate.
*
* @param baseURL The URL
* @param trustedCaCert Trusted CA certificate
* @param numberOfRetries Number of retries on 5xx errors
* @param timeout Timeout for HTTP requests (milliseconds)
* @param tlsVersion TLS Version.
*/
public HTTPVaultConnector(final String baseURL,
final X509Certificate trustedCaCert,
final int numberOfRetries,
final Integer timeout,
final String tlsVersion) {
this.request = new RequestHelper(baseURL, numberOfRetries, timeout, tlsVersion, trustedCaCert);
public static HTTPVaultConnectorBuilder builder(URI baseURL) {
return new HTTPVaultConnectorBuilder().withBaseURL(baseURL);
}
@Override
@ -224,34 +135,38 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final SealResponse sealStatus() throws VaultConnectorException {
return request.get(PATH_SEAL_STATUS, new HashMap<>(), token, SealResponse.class);
return request.get(PATH_SEAL_STATUS, emptyMap(), token, SealResponse.class);
}
@Override
public final void seal() throws VaultConnectorException {
request.put(PATH_SEAL, new HashMap<>(), token);
request.put(PATH_SEAL, emptyMap(), token);
}
@Override
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
Map<String, String> param = new HashMap<>();
param.put("key", key);
if (reset != null) {
param.put("reset", reset.toString());
}
Map<String, String> param = mapOfStrings(
"key", key,
"reset", reset
);
return request.put(PATH_UNSEAL, param, token, SealResponse.class);
}
@Override
public HealthResponse getHealth() throws VaultConnectorException {
/* Force status code to be 200, so we don't need to modify the request sequence. */
Map<String, String> param = new HashMap<>();
param.put("standbycode", "200"); // Default: 429.
param.put("sealedcode", "200"); // Default: 503.
param.put("uninitcode", "200"); // Default: 501.
return request.get(PATH_HEALTH, param, token, HealthResponse.class);
return request.get(
PATH_HEALTH,
// Force status code to be 200, so we don't need to modify the request sequence.
Map.of(
"standbycode", "200", // Default: 429.
"sealedcode", "200", // Default: 503.
"uninitcode", "200" // Default: 501.
),
token,
HealthResponse.class
);
}
@Override
@ -262,7 +177,7 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
/* Issue request and parse response */
AuthMethodsResponse amr = request.get(PATH_AUTH, new HashMap<>(), token, AuthMethodsResponse.class);
AuthMethodsResponse amr = request.get(PATH_SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
}
@ -272,7 +187,7 @@ public class HTTPVaultConnector implements VaultConnector {
/* set token */
this.token = token;
this.tokenTTL = 0;
TokenResponse res = request.post(PATH_TOKEN + PATH_LOOKUP, new HashMap<>(), token, TokenResponse.class);
TokenResponse res = request.post(PATH_AUTH_TOKEN + PATH_LOOKUP, emptyMap(), token, TokenResponse.class);
authorized = true;
return res;
@ -281,28 +196,29 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final AuthResponse authUserPass(final String username, final String password)
throws VaultConnectorException {
final Map<String, String> payload = new HashMap<>();
payload.put("password", password);
final Map<String, String> payload = singletonMap("password", password);
return queryAuth(PATH_AUTH_USERPASS + username, payload);
}
@Override
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
public final AuthResponse authAppId(final String appID, final String userID) throws VaultConnectorException {
final Map<String, String> payload = new HashMap<>();
payload.put("app_id", appID);
payload.put("user_id", userID);
return queryAuth(PATH_AUTH_APPID + "login", payload);
return queryAuth(
PATH_AUTH_APPID + PATH_LOGIN,
Map.of(
"app_id", appID,
"user_id", userID
)
);
}
@Override
public final AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException {
final Map<String, String> payload = new HashMap<>();
payload.put("role_id", roleID);
if (secretID != null) {
payload.put("secret_id", secretID);
}
return queryAuth(PATH_AUTH_APPROLE + "login", payload);
final Map<String, String> payload = mapOfStrings(
"role_id", roleID,
"secret_id", secretID
);
return queryAuth(PATH_AUTH_APPROLE + PATH_LOGIN, payload);
}
/**
@ -326,29 +242,35 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
public final boolean registerAppId(final String appID, final String policy, final String displayName)
throws VaultConnectorException {
requireAuth();
Map<String, String> payload = new HashMap<>();
payload.put("value", policy);
payload.put("display_name", displayName);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(PATH_AUTH_APPID + "map/app-id/" + appID, payload, token);
request.postWithoutResponse(
PATH_AUTH_APPID + "/map/app-id/" + appID,
Map.of(
"value", policy,
"display_name", displayName
),
token
);
return true;
}
@Override
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
public final boolean registerUserId(final String appID, final String userID) throws VaultConnectorException {
requireAuth();
Map<String, String> payload = new HashMap<>();
payload.put("value", appID);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(PATH_AUTH_APPID + "map/user-id/" + userID, payload, token);
request.postWithoutResponse(
PATH_AUTH_APPID + "/map/user-id/" + userID,
singletonMap("value", appID),
token
);
return true;
}
@ -368,7 +290,12 @@ public class HTTPVaultConnector implements VaultConnector {
public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""), new HashMap<>(), token, AppRoleResponse.class);
return request.get(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, ""),
emptyMap(),
token,
AppRoleResponse.class
);
}
@Override
@ -387,7 +314,7 @@ public class HTTPVaultConnector implements VaultConnector {
/* Issue request, parse response and extract Role ID */
return request.get(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"),
new HashMap<>(),
emptyMap(),
token,
RawDataResponse.class
).getData().get("role_id").toString();
@ -396,12 +323,13 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final boolean setAppRoleID(final String roleName, final String roleID) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse Secret */
Map<String, String> payload = new HashMap<>();
payload.put("role_id", roleID);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"), payload, token);
request.postWithoutResponse(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/role-id"),
singletonMap("role_id", roleID),
token
);
return true;
}
@ -459,7 +387,13 @@ public class HTTPVaultConnector implements VaultConnector {
public final List<String> listAppRoles() throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(PATH_AUTH_APPROLE + "role?list=true", new HashMap<>(), token, SecretListResponse.class);
SecretListResponse secrets = request.get(
PATH_AUTH_APPROLE + "/role?list=true",
emptyMap(),
token,
SecretListResponse.class
);
return secrets.getKeys();
}
@ -469,7 +403,7 @@ public class HTTPVaultConnector implements VaultConnector {
SecretListResponse secrets = request.get(
String.format(PATH_AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
new HashMap<>(),
emptyMap(),
token,
SecretListResponse.class
);
@ -481,44 +415,48 @@ public class HTTPVaultConnector implements VaultConnector {
public final SecretResponse read(final String key) throws VaultConnectorException {
requireAuth();
/* Issue request and parse secret response */
return request.get(key, new HashMap<>(), token, SecretResponse.class);
return request.get(key, emptyMap(), token, PlainSecretResponse.class);
}
@Override
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version) throws VaultConnectorException {
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
Map<String, String> args = new HashMap<>();
if (version != null) {
args.put("version", version.toString());
}
Map<String, String> args = mapOfStrings("version", version);
return request.get(mount + PATH_DATA + key, args, token, SecretResponse.class);
return request.get(mount + PATH_DATA + key, args, token, MetaSecretResponse.class);
}
@Override
public final MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException {
public final MetadataResponse readSecretMetadata(final String mount, final String key)
throws VaultConnectorException {
requireAuth();
/* Request HTTP response and parse secret metadata */
return request.get(mount + PATH_METADATA + key, new HashMap<>(), token, MetadataResponse.class);
return request.get(mount + PATH_METADATA + key, emptyMap(), token, MetadataResponse.class);
}
@Override
public void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException {
public void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException {
requireAuth();
Map<String, Object> payload = new HashMap<>();
if (maxVersions != null) {
payload.put("max_versions", maxVersions);
}
payload.put("cas_required", casRequired);
Map<String, Object> payload = mapOf(
"max_versions", maxVersions,
"cas_required", casRequired
);
write(mount + PATH_METADATA + key, payload);
}
@Override
public final SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data, final Integer cas) throws VaultConnectorException {
public final SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException {
requireAuth();
if (key == null || key.isEmpty()) {
@ -526,30 +464,32 @@ public class HTTPVaultConnector implements VaultConnector {
}
// Add CAS value to options map if present.
Map<String, Object> options = new HashMap<>();
if (cas != null) {
options.put("cas", cas);
}
Map<String, Object> payload = new HashMap<>();
payload.put("data", data);
payload.put("options", options);
Map<String, Object> options = mapOf("cas", cas);
/* Issue request and parse metadata response */
return request.post(mount + PATH_DATA + key, payload, token, SecretVersionResponse.class);
return request.post(
mount + PATH_DATA + key,
Map.of(
"data", data,
"options", options
),
token,
SecretVersionResponse.class
);
}
@Override
public final List<String> list(final String path) throws VaultConnectorException {
requireAuth();
SecretListResponse secrets = request.get(path + "/?list=true", new HashMap<>(), token, SecretListResponse.class);
SecretListResponse secrets = request.get(path + "/?list=true", emptyMap(), token, SecretListResponse.class);
return secrets.getKeys();
}
@Override
public final void write(final String key, final Map<String, Object> data, final Map<String, Object> options) throws VaultConnectorException {
public final void write(final String key, final Map<String, Object> data, final Map<String, Object> options)
throws VaultConnectorException {
requireAuth();
if (key == null || key.isEmpty()) {
@ -561,10 +501,10 @@ public class HTTPVaultConnector implements VaultConnector {
// If options are given, split payload in two parts.
if (options != null) {
Map<String, Object> payloadMap = new HashMap<>();
payloadMap.put("data", data);
payloadMap.put("options", options);
payload = payloadMap;
payload = Map.of(
"data", data,
"options", options
);
}
/* Issue request and expect code 204 with empty response */
@ -590,36 +530,41 @@ public class HTTPVaultConnector implements VaultConnector {
}
@Override
public final void deleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_DELETE, key, versions);
}
@Override
public final void undeleteSecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_UNDELETE, key, versions);
}
@Override
public final void destroySecretVersions(final String mount, final String key, final int... versions) throws VaultConnectorException {
public final void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException {
handleSecretVersions(mount, PATH_DESTROY, key, versions);
}
/**
* Common method to bundle secret version operations.
*
* @param mount Secret store mountpoint (without leading or trailing slash).
* @param mount Secret store mount point (without leading or trailing slash).
* @param pathPart Path part to query.
* @param key Secret key.
* @param versions Versions to handle.
* @throws VaultConnectorException on error
* @since 0.8
*/
private void handleSecretVersions(final String mount, final String pathPart, final String key, final int... versions) throws VaultConnectorException {
private void handleSecretVersions(final String mount,
final String pathPart,
final String key,
final int... versions) throws VaultConnectorException {
requireAuth();
/* Request HTTP response and expect empty result */
Map<String, Object> payload = new HashMap<>();
payload.put("versions", versions);
Map<String, Object> payload = singletonMap("versions", versions);
/* Issue request and expect code 204 with empty response */
request.postWithoutResponse(mount + pathPart + key, payload, token);
@ -630,18 +575,17 @@ public class HTTPVaultConnector implements VaultConnector {
requireAuth();
/* Issue request and expect code 204 with empty response */
request.putWithoutResponse(PATH_REVOKE + leaseID, new HashMap<>(), token);
request.putWithoutResponse(PATH_REVOKE + leaseID, emptyMap(), token);
}
@Override
public final SecretResponse renew(final String leaseID, final Integer increment) throws VaultConnectorException {
requireAuth();
Map<String, String> payload = new HashMap<>();
payload.put("lease_id", leaseID);
if (increment != null) {
payload.put("increment", increment.toString());
}
Map<String, String> payload = mapOfStrings(
"lease_id", leaseID,
"increment", increment
);
/* Issue request and parse secret response */
return request.put(PATH_RENEW, payload, token, SecretResponse.class);
@ -649,12 +593,12 @@ public class HTTPVaultConnector implements VaultConnector {
@Override
public final AuthResponse createToken(final Token token) throws VaultConnectorException {
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE);
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE);
}
@Override
public final AuthResponse createToken(final Token token, final boolean orphan) throws VaultConnectorException {
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE_ORPHAN);
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE_ORPHAN);
}
@Override
@ -662,7 +606,7 @@ public class HTTPVaultConnector implements VaultConnector {
if (role == null || role.isEmpty()) {
throw new InvalidRequestException("No role name specified.");
}
return createTokenInternal(token, PATH_TOKEN + PATH_CREATE + "/" + role);
return createTokenInternal(token, PATH_AUTH_TOKEN + PATH_CREATE + "/" + role);
}
@Override
@ -696,7 +640,57 @@ public class HTTPVaultConnector implements VaultConnector {
requireAuth();
/* Request HTTP response and parse Secret */
return request.get(PATH_TOKEN + "/lookup/" + token, new HashMap<>(), token, TokenResponse.class);
return request.get(
PATH_AUTH_TOKEN + PATH_LOOKUP,
singletonMap("token", token),
token,
TokenResponse.class
);
}
@Override
public boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
} else if (role == null) {
throw new InvalidRequestException("Role must be provided.");
}
// Issue request and expect code 204 with empty response.
request.postWithoutResponse(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, role, token);
return true;
}
@Override
public TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException {
requireAuth();
// Request HTTP response and parse response.
return request.get(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
}
@Override
public List<String> listTokenRoles() throws VaultConnectorException {
requireAuth();
return list(PATH_AUTH_TOKEN + PATH_ROLES);
}
@Override
public boolean deleteTokenRole(final String name) throws VaultConnectorException {
requireAuth();
if (name == null) {
throw new InvalidRequestException("Role name must be provided.");
}
// Issue request and expect code 204 with empty response.
request.deleteWithoutResponse(PATH_AUTH_TOKEN + PATH_ROLES + "/" + name, token);
return true;
}
/**
@ -710,4 +704,42 @@ public class HTTPVaultConnector implements VaultConnector {
throw new AuthorizationRequiredException();
}
}
/**
* Generate a map of non-null {@link String} keys and values
*
* @param keyValues Key-value tuples as vararg.
* @return The map of non-null keys and values.
*/
private static Map<String, String> mapOfStrings(Object... keyValues) {
Map<String, String> map = new HashMap<>(keyValues.length / 2, 1);
for (int i = 0; i < keyValues.length - 1; i = i + 2) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (key instanceof String && val != null) {
map.put((String) key, val.toString());
}
}
return map;
}
/**
* Generate a map of non-null {@link String} keys and {@link Object} values
*
* @param keyValues Key-value tuples as vararg.
* @return The map of non-null keys and values.
*/
private static Map<String, Object> mapOf(Object... keyValues) {
Map<String, Object> map = new HashMap<>(keyValues.length / 2, 1);
for (int i = 0; i < keyValues.length; i = i + 2) {
Object key = keyValues[i];
Object val = keyValues[i + 1];
if (key instanceof String && val != null) {
map.put((String) key, val);
}
}
return map;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,16 +14,16 @@
* limitations under the License.
*/
package de.stklcode.jvault.connector.builder;
package de.stklcode.jvault.connector;
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.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
@ -31,14 +31,16 @@ import java.nio.file.Paths;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Objects;
/**
* Vault Connector Factory implementation for HTTP Vault connectors.
* Vault Connector Builder implementation for HTTP Vault connectors.
*
* @author Stefan Kalscheuer
* @since 0.8.0
* @since 0.9.5 Package {@link de.stklcode.jvault.connector}
*/
public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
public final class HTTPVaultConnectorBuilder {
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";
@ -65,7 +67,7 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
* Default empty constructor.
* Initializes factory with default values.
*/
public HTTPVaultConnectorBuilder() {
HTTPVaultConnectorBuilder() {
host = DEFAULT_HOST;
port = DEFAULT_PORT;
tls = DEFAULT_TLS;
@ -74,6 +76,32 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
numberOfRetries = DEFAULT_NUMBER_OF_RETRIES;
}
/**
* Set base URL, e.g. "protocol://host:port/prefix".
*
* @param baseURL Base URL
* @return self
* @throws URISyntaxException Invalid URI syntax.
* @since 1.0
*/
public HTTPVaultConnectorBuilder withBaseURL(final String baseURL) throws URISyntaxException {
return withBaseURL(new URI(baseURL));
}
/**
* Set base URL, e.g. "protocol://host:port/prefix".
*
* @param baseURL Base URL
* @return self
* @since 1.0
*/
public HTTPVaultConnectorBuilder withBaseURL(final URI baseURL) {
return withTLS(!("http".equalsIgnoreCase(Objects.requireNonNullElse(baseURL.getScheme(), ""))))
.withHost(baseURL.getHost())
.withPort(baseURL.getPort())
.withPrefix(baseURL.getPath());
}
/**
* Set hostname (default: 127.0.0.1).
*
@ -85,17 +113,43 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
return this;
}
/**
* Get hostname.
*
* @return Hostname or IP address
*/
String getHost() {
return this.host;
}
/**
* Set port (default: 8200).
* A value of {@code null} or {@code -1} indicates that no port is specified, i.e. the protocol default is used.
* Otherwise, a valid port number between 1 and 65535 is expected.
*
* @param port Vault TCP port
* @return self
*/
public HTTPVaultConnectorBuilder withPort(final Integer port) {
this.port = port;
if (port == null || port < 0) {
this.port = null;
} else if (port < 1 || port > 65535) {
throw new IllegalArgumentException("Port number " + port + " out of range");
} else {
this.port = port;
}
return this;
}
/**
* Set port..
*
* @return Vault TCP port
*/
Integer getPort() {
return this.port;
}
/**
* Set TLS usage (default: TRUE).
*
@ -107,6 +161,24 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
return this;
}
/**
* Get TLS usage flag.
*
* @return use TLS or not
*/
boolean isWithTLS() {
return this.tls;
}
/**
* Get TLS version.
*
* @return TLS version.
*/
String getTlsVersion() {
return this.tlsVersion;
}
/**
* Set TLS usage (default: TRUE).
*
@ -153,7 +225,7 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
/**
* Set API prefix. Default is "/v1/" and changes should not be necessary for current state of development.
*
* @param prefix Vault API prefix (default: "/v1/"
* @param prefix Vault API prefix (default: "/v1/")
* @return self
*/
public HTTPVaultConnectorBuilder withPrefix(final String prefix) {
@ -162,7 +234,16 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
}
/**
* Add a trusted CA certifiate for HTTPS connections.
* Get API prefix.
*
* @return Vault API prefix.
*/
String getPrefix() {
return this.prefix;
}
/**
* Add a trusted CA certificate for HTTPS connections.
*
* @param cert path to certificate file
* @return self
@ -179,7 +260,7 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
}
/**
* Add a trusted CA certifiate for HTTPS connections.
* Add a trusted CA certificate for HTTPS connections.
*
* @param cert path to certificate file
* @return self
@ -190,6 +271,15 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
return this;
}
/**
* Get the trusted CA certificate for HTTPS connections.
*
* @return path to certificate file, if specified.
*/
X509Certificate getTrustedCA() {
return this.trustedCA;
}
/**
* Set token for automatic authentication, using {@link #buildAndAuth()}.
*
@ -213,7 +303,7 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
/* 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));
var url = new URL(System.getenv(ENV_VAULT_ADDR));
this.host = url.getHost();
this.port = url.getPort();
this.tls = url.getProtocol().equals("https");
@ -253,6 +343,15 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
return this;
}
/**
* Get the number of retries to attempt on 5xx errors.
*
* @return The number of retries to attempt on 5xx errors (default: 0)
*/
int getNumberOfRetries() {
return this.numberOfRetries;
}
/**
* Define a custom timeout for the HTTP connection.
*
@ -265,12 +364,31 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
return this;
}
@Override
public HTTPVaultConnector build() {
return new HTTPVaultConnector(host, tls, tlsVersion, port, prefix, trustedCA, numberOfRetries, timeout);
/**
* Get custom timeout for the HTTP connection.
*
* @return Timeout value in milliseconds.
*/
Integer getTimeout() {
return this.timeout;
}
@Override
/**
* Build command, produces connector after initialization.
*
* @return Vault Connector instance.
*/
public HTTPVaultConnector build() {
return new HTTPVaultConnector(this);
}
/**
* 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 HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
if (token == null) {
throw new ConnectionException("No vault token provided, unable to authenticate.");
@ -289,7 +407,7 @@ public final class HTTPVaultConnectorBuilder implements VaultConnectorBuilder {
* @since 0.4.0
*/
private X509Certificate certificateFromFile(final Path certFile) throws TlsException {
try (InputStream is = Files.newInputStream(certFile)) {
try (var 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

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,13 @@
package de.stklcode.jvault.connector;
import de.stklcode.jvault.connector.exception.InvalidRequestException;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import de.stklcode.jvault.connector.model.*;
import de.stklcode.jvault.connector.model.response.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -35,10 +34,6 @@ import java.util.Map;
* @since 0.1
*/
public interface VaultConnector extends AutoCloseable, Serializable {
/**
* Default sub-path for Vault secrets.
*/
String PATH_SECRET = "secret";
/**
* Reset authorization information.
@ -91,7 +86,7 @@ public interface VaultConnector extends AutoCloseable, Serializable {
HealthResponse getHealth() throws VaultConnectorException;
/**
* Get all availale authentication backends.
* Get all available authentication backends.
*
* @return List of backends
* @throws VaultConnectorException on error
@ -124,9 +119,10 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @param userID The User ID
* @return The {@link AuthResponse}
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #authAppRole} instead.
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. App-ID was removed in Vault 1.12.
* Consider using {@link #authAppRole} instead.
*/
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
AuthResponse authAppId(final String appID, final String userID) throws VaultConnectorException;
/**
@ -160,9 +156,10 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @param displayName Arbitrary name to display
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. Consider using {@link #createAppRole} instead.
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. App-ID was removed in Vault 1.12.
* Consider using {@link #createAppRole} instead.
*/
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
boolean registerAppId(final String appID, final String policy, final String displayName)
throws VaultConnectorException;
@ -226,14 +223,14 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
default boolean createAppRole(final String roleName, final List<String> policies, final String roleID)
throws VaultConnectorException {
return createAppRole(new AppRoleBuilder(roleName).withPolicies(policies).withId(roleID).build());
return createAppRole(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
}
/**
* Delete AppRole role from Vault.
*
* @param roleName The role anme
* @return {@code true} on succevss
* @param roleName The role name
* @return {@code true} on success
* @throws VaultConnectorException on error
*/
boolean deleteAppRole(final String roleName) throws VaultConnectorException;
@ -354,10 +351,10 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @param userID The User-ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. App-ID was removed in Vault 1.12.
* Consider using {@link #createAppRoleSecret} instead.
*/
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
boolean registerUserId(final String appID, final String userID) throws VaultConnectorException;
/**
@ -369,9 +366,9 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @param userID The User-ID
* @return {@code true} on success
* @throws VaultConnectorException on error
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole.
* @deprecated As of Vault 0.6.1 App-ID is superseded by AppRole. App-ID was removed in Vault 1.12.
*/
@Deprecated
@Deprecated(since = "0.4", forRemoval = true)
default boolean registerAppUserId(final String appID,
final String policy,
final String displayName,
@ -396,41 +393,13 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
SecretResponse read(final String key) throws VaultConnectorException;
/**
* Retrieve secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to key.
*
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
*/
default SecretResponse readSecret(final String key) throws VaultConnectorException {
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 mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Secret response
* @throws VaultConnectorException on error
@ -440,36 +409,22 @@ public interface VaultConnector extends AutoCloseable, Serializable {
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.
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
default SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data) throws VaultConnectorException {
default SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data) throws VaultConnectorException {
return writeSecretData(mount, key, data, null);
}
@ -479,15 +434,18 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* 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.
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param data Secret content. Value must be be JSON serializable.
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
* @return Metadata for the created/updated secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
SecretVersionResponse writeSecretData(final String mount, final String key, final Map<String, Object> data, final Integer cas) throws VaultConnectorException;
SecretVersionResponse writeSecretData(final String mount,
final String key,
final Map<String, Object> data,
final Integer cas) throws VaultConnectorException;
/**
* Retrieve secret data from Vault.
@ -495,60 +453,15 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* Path {@code <mount>/data/<key>} is read here.
* Only available for KV v2 secrets.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
* @return Secret response
* @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);
}
SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
throws VaultConnectorException;
/**
* Retrieve secret metadata from Vault.
@ -556,7 +469,7 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* 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 mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @return Metadata response
* @throws VaultConnectorException on error
@ -570,14 +483,17 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* 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 mount Secret store mount point (without leading or trailing slash).
* @param key Secret identifier
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
* @param casRequired Specify if Check-And-Set is required for this secret.
* @throws VaultConnectorException on error
* @since 0.8
*/
void updateSecretMetadata(final String mount, final String key, final Integer maxVersions, final boolean casRequired) throws VaultConnectorException;
void updateSecretMetadata(final String mount,
final String key,
final Integer maxVersions,
final boolean casRequired) throws VaultConnectorException;
/**
* List available nodes from Vault.
@ -589,19 +505,6 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
List<String> list(final String path) throws VaultConnectorException;
/**
* List available secrets from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param path Root path to search
* @return List of secret keys
* @throws VaultConnectorException on error
*/
default List<String> listSecrets(final String path) throws VaultConnectorException {
return list(PATH_SECRET + "/" + path);
}
/**
* Write simple value to Vault.
*
@ -611,9 +514,7 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @since 0.5.0
*/
default void write(final String key, final String value) throws VaultConnectorException {
Map<String, Object> param = new HashMap<>();
param.put("value", value);
write(key, param);
write(key, Collections.singletonMap("value", value));
}
/**
@ -637,39 +538,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* @throws VaultConnectorException on error
* @since 0.8 {@code options} parameter added
*/
void write(final String key, final Map<String, Object> data, final Map<String, Object> options) throws VaultConnectorException;
/**
* Write secret to Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @param value Secret value
* @throws VaultConnectorException on error
*/
default void writeSecret(final String key, final String value) throws VaultConnectorException {
Map<String, Object> param = new HashMap<>();
param.put("value", value);
writeSecret(key, param);
}
/**
* Write secret to Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @param data Secret content. Value must be be JSON serializable.
* @throws VaultConnectorException on error
* @since 0.5.0
*/
default void writeSecret(final String key, final Map<String, Object> data) throws VaultConnectorException {
if (key == null || key.isEmpty()) {
throw new InvalidRequestException("Secret path must not be empty.");
}
write(PATH_SECRET + "/" + key, data);
}
void write(final String key, final Map<String, Object> data, final Map<String, Object> options)
throws VaultConnectorException;
/**
* Delete key from Vault.
@ -680,37 +550,12 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
void delete(final String key) throws VaultConnectorException;
/**
* Delete secret from Vault.
* <br>
* Prefix {@code secret/} is automatically added to path.
*
* @param key Secret path
* @throws VaultConnectorException on error
*/
default void deleteSecret(final String key) throws VaultConnectorException {
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 mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
@ -723,22 +568,8 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* 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.
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @throws VaultConnectorException on error
* @since 0.8
*/
@ -749,77 +580,40 @@ public interface VaultConnector extends AutoCloseable, Serializable {
* <br>
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to delete.
* @throws VaultConnectorException on error
* @since 0.8
*/
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;
void deleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Undelete (restore) secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to undelete.
* @throws VaultConnectorException on error
* @since 0.8
*/
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;
void undeleteSecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Destroy secret versions from Vault.
* Only available for KV v2 stores.
*
* @param mount Secret store mount point (without leading or trailing slash).
* @param key Secret path.
* @param versions Versions of the secret to destroy.
* @throws VaultConnectorException on error
* @since 0.8
*/
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;
void destroySecretVersions(final String mount, final String key, final int... versions)
throws VaultConnectorException;
/**
* Revoke given lease immediately.
@ -888,7 +682,57 @@ public interface VaultConnector extends AutoCloseable, Serializable {
*/
TokenResponse lookupToken(final String token) throws VaultConnectorException;
/**
* Create a new or update an existing token role.
*
* @param role the role entity (name must be set)
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
default boolean createOrUpdateTokenRole(final TokenRole role) throws VaultConnectorException {
return createOrUpdateTokenRole(role.getName(), role);
}
/**
* Create a new or update an existing token role.
*
* @param name the role name (overrides name possibly set in role entity)
* @param role the role entity
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException;
/**
* Lookup token information.
*
* @param name the role name
* @return the result response
* @throws VaultConnectorException on error
* @since 0.9
*/
TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException;
/**
* List available token roles from Vault.
*
* @return List of token roles
* @throws VaultConnectorException on error
* @since 0.9
*/
List<String> listTokenRoles() throws VaultConnectorException;
/**
* Delete a token role.
*
* @param name the role name to delete
* @return {@code true} on success
* @throws VaultConnectorException on error
* @since 0.9
*/
boolean deleteTokenRole(final String name) throws VaultConnectorException;
/**
* Read credentials for MySQL backend at default mount point.

View File

@ -1,54 +0,0 @@
/*
* 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

@ -1,21 +0,0 @@
/*
* 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-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ package de.stklcode.jvault.connector.exception;
/**
* Exception thrown trying to do a request without any authorization handles.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
public class AuthorizationRequiredException extends VaultConnectorException {
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ package de.stklcode.jvault.connector.exception;
/**
* Exception thrown when trying to send malformed request.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
public class InvalidRequestException extends VaultConnectorException {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -136,30 +136,6 @@ public final class InvalidResponseException extends VaultConnectorException {
this.response = response;
}
/**
* Specify the HTTP status code. Can be retrieved by {@link #getStatusCode()} later.
*
* @param statusCode The status code
* @return self
* @deprecated as of 0.6.2, use constructor with status code argument instead
*/
@Deprecated
public InvalidResponseException withStatusCode(final Integer statusCode) {
return new InvalidResponseException(getMessage(), statusCode, getResponse(), getCause());
}
/**
* Specify the response string. Can be retrieved by {@link #getResponse()} later.
*
* @param response Response text
* @return self
* @deprecated use constructor with response argument instead
*/
@Deprecated
public InvalidResponseException withResponse(final String response) {
return new InvalidResponseException(getMessage(), getStatusCode(), response, getCause());
}
/**
* Retrieve the HTTP status code.
*

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ package de.stklcode.jvault.connector.exception;
/**
* Exception thrown when trying to access a path the current user/token does not have permission to access.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
public class PermissionDeniedException extends VaultConnectorException {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,8 +19,8 @@ package de.stklcode.jvault.connector.exception;
/**
* Exception thrown on errors with TLS connection.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public class TlsException extends VaultConnectorException {
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,14 @@ package de.stklcode.jvault.connector.exception;
/**
* Abstract Exception class for Vault Connector internal exceptions.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
public abstract class VaultConnectorException extends Exception {
/**
* Constructs a new empty exception.
*/
public VaultConnectorException() {
protected VaultConnectorException() {
}
/**
@ -34,7 +34,7 @@ public abstract class VaultConnectorException extends Exception {
*
* @param message the detail message
*/
public VaultConnectorException(final String message) {
protected VaultConnectorException(final String message) {
super(message);
}
@ -43,7 +43,7 @@ public abstract class VaultConnectorException extends Exception {
*
* @param cause the cause
*/
public VaultConnectorException(final Throwable cause) {
protected VaultConnectorException(final Throwable cause) {
super(cause);
}
@ -53,7 +53,7 @@ public abstract class VaultConnectorException extends Exception {
* @param message the detail message
* @param cause the cause
*/
public VaultConnectorException(final String message, final Throwable cause) {
protected VaultConnectorException(final String message, final Throwable cause) {
super(message, cause);
}
}

View File

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

View File

@ -1,204 +0,0 @@
/*
* 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.factory;
import de.stklcode.jvault.connector.HTTPVaultConnector;
import de.stklcode.jvault.connector.builder.HTTPVaultConnectorBuilder;
import de.stklcode.jvault.connector.exception.VaultConnectorException;
import javax.net.ssl.SSLContext;
import java.nio.file.Path;
import java.security.cert.X509Certificate;
/**
* Vault Connector Factory implementation for HTTP Vault connectors.
*
* @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 final HTTPVaultConnectorBuilder delegate;
/**
* Default empty constructor.
* Initializes factory with default values.
*/
public HTTPVaultConnectorFactory() {
delegate = new HTTPVaultConnectorBuilder();
}
/**
* Set hostname (default: 127.0.0.1).
*
* @param host Hostname or IP address
* @return self
*/
public HTTPVaultConnectorFactory withHost(final String host) {
delegate.withHost(host);
return this;
}
/**
* Set port (default: 8200).
*
* @param port Vault TCP port
* @return self
*/
public HTTPVaultConnectorFactory withPort(final Integer port) {
delegate.withPort(port);
return this;
}
/**
* Set TLS usage (default: TRUE).
*
* @param useTLS use TLS or not
* @return self
*/
public HTTPVaultConnectorFactory withTLS(final boolean useTLS) {
delegate.withTLS(useTLS);
return this;
}
/**
* Convenience Method for TLS usage (enabled by default).
*
* @return self
*/
public HTTPVaultConnectorFactory withTLS() {
return withTLS(true);
}
/**
* Convenience Method for NOT using TLS.
*
* @return self
*/
public HTTPVaultConnectorFactory 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 HTTPVaultConnectorFactory withPrefix(final String prefix) {
delegate.withPrefix(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 HTTPVaultConnectorFactory withTrustedCA(final Path cert) throws VaultConnectorException {
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;
}
/**
* Add a custom SSL context.
* Overwrites certificates set by {@link #withTrustedCA}.
*
* @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) {
throw new UnsupportedOperationException("Use of deprecated method, please switch to withTrustedCA()");
}
/**
* Set token for automatic authentication, using {@link #buildAndAuth()}.
*
* @param token Vault token
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withToken(final String token) {
delegate.withToken(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 HTTPVaultConnectorFactory fromEnv() throws VaultConnectorException {
delegate.fromEnv();
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 HTTPVaultConnectorFactory withNumberOfRetries(final int numberOfRetries) {
delegate.withNumberOfRetries(numberOfRetries);
return this;
}
/**
* Define a custom timeout for the HTTP connection.
*
* @param milliseconds Timeout value in milliseconds.
* @return self
* @since 0.6.0
*/
public HTTPVaultConnectorFactory withTimeout(final int milliseconds) {
delegate.withTimeout(milliseconds);
return this;
}
@Override
public HTTPVaultConnector build() {
return delegate.build();
}
@Override
public HTTPVaultConnector buildAndAuth() throws VaultConnectorException {
return delegate.buildAndAuth();
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.factory;
import de.stklcode.jvault.connector.builder.VaultConnectorBuilder;
/**
* Abstract Vault Connector Factory interface.
* Provides builder pattern style factory for Vault connectors.
*
* @author Stefan Kalscheuer
* @since 0.1
* @deprecated As of 0.8.0 please refer to {@link VaultConnectorBuilder} with identical API.
*/
@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();
}
}

View File

@ -1,23 +0,0 @@
/*
* 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

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,7 +28,8 @@ final class Error {
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";
static final String INIT_SSL_CONTEXT = "Unable to initialize SSLContext";
static final String CONNECTION = "Unable to connect to Vault server";
/**
* Constructor hidden, this class should not be instantiated.

View File

@ -4,27 +4,29 @@ 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.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* Helper class to bundle Vault HTTP requests.
*
@ -55,7 +57,7 @@ public final class RequestHelper implements Serializable {
final Integer timeout,
final String tlsVersion,
final X509Certificate trustedCaCert) {
this.baseURL = baseURL;
this.baseURL = baseURL + (baseURL.endsWith("/") ? "" : "/");
this.retries = retries;
this.timeout = timeout;
this.tlsVersion = tlsVersion;
@ -74,26 +76,24 @@ public final class RequestHelper implements Serializable {
* @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);
// Initialize POST.
var req = HttpRequest.newBuilder(URI.create(baseURL + path));
/* generate JSON from payload */
StringEntity input;
// Generate JSON from payload.
try {
input = new StringEntity(jsonMapper.writeValueAsString(payload), StandardCharsets.UTF_8);
req.POST(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), 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 */
req.setHeader("Content-Type", "application/json; charset=utf-8");
// Set X-Vault-Token header.
if (token != null) {
post.addHeader(HEADER_VAULT_TOKEN, token);
req.setHeader(HEADER_VAULT_TOKEN, token);
}
return request(post, retries);
return request(req, retries);
}
/**
@ -127,7 +127,8 @@ public final class RequestHelper implements Serializable {
* @throws VaultConnectorException on connection error
* @since 0.8
*/
public void postWithoutResponse(final String path, final Object payload, final String token) throws VaultConnectorException {
public void postWithoutResponse(final String path, final Object payload, final String token)
throws VaultConnectorException {
if (!post(path, payload, token).isEmpty()) {
throw new InvalidResponseException(Error.UNEXPECTED_RESPONSE);
}
@ -143,27 +144,26 @@ public final class RequestHelper implements Serializable {
* @throws VaultConnectorException on connection error
* @since 0.8 Added {@code token} parameter.
*/
public String put(final String path, final Map<String, String> payload, final String token) throws VaultConnectorException {
/* Initialize put */
HttpPut put = new HttpPut(baseURL + path);
public String put(final String path, final Map<String, String> payload, final String token)
throws VaultConnectorException {
// Initialize PUT.
var req = HttpRequest.newBuilder(URI.create(baseURL + path));
/* generate JSON from payload */
StringEntity entity = null;
// Generate JSON from payload.
try {
entity = new StringEntity(jsonMapper.writeValueAsString(payload));
} catch (UnsupportedEncodingException | JsonProcessingException e) {
req.PUT(HttpRequest.BodyPublishers.ofString(jsonMapper.writeValueAsString(payload), UTF_8));
} catch (JsonProcessingException e) {
throw new InvalidRequestException("Payload serialization failed", e);
}
/* Parse parameters */
put.setEntity(entity);
req.setHeader("Content-Type", "application/json; charset=utf-8");
/* Set X-Vault-Token header */
// Set X-Vault-Token header.
if (token != null) {
put.addHeader(HEADER_VAULT_TOKEN, token);
req.setHeader(HEADER_VAULT_TOKEN, token);
}
return request(put, retries);
return request(req, retries);
}
/**
@ -214,15 +214,15 @@ public final class RequestHelper implements Serializable {
* @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);
// Initialize DELETE.
HttpRequest.Builder req = HttpRequest.newBuilder(URI.create(baseURL + path)).DELETE();
/* Set X-Vault-Token header */
// Set X-Vault-Token header.
if (token != null) {
delete.addHeader(HEADER_VAULT_TOKEN, token);
req.setHeader(HEADER_VAULT_TOKEN, token);
}
return request(delete, retries);
return request(req, retries);
}
/**
@ -251,25 +251,31 @@ public final class RequestHelper implements Serializable {
*/
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);
// Add parameters to URI.
var uriBuilder = new StringBuilder(baseURL + path);
/* Initialize request */
get = new HttpGet(uriBuilder.build());
if (!payload.isEmpty()) {
uriBuilder.append("?").append(
payload.entrySet().stream().map(par ->
URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8)
).collect(Collectors.joining("&"))
);
}
// Initialize GET.
try {
var req = HttpRequest.newBuilder(new URI(uriBuilder.toString()));
// Set X-Vault-Token header.
if (token != null) {
req.setHeader(HEADER_VAULT_TOKEN, token);
}
return request(req, retries);
} 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);
}
/**
@ -297,34 +303,40 @@ public final class RequestHelper implements Serializable {
/**
* Execute prepared HTTP request and return result.
*
* @param base Prepares Request
* @param retries number of retries
* @param requestBuilder Prepared 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");
private String request(final HttpRequest.Builder requestBuilder, final int retries) throws VaultConnectorException {
// Set JSON Header.
requestBuilder.setHeader("accept", "application/json");
CloseableHttpResponse response = null;
var clientBuilder = HttpClient.newBuilder();
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());
}
// Set custom timeout, if defined.
if (this.timeout != null) {
clientBuilder.connectTimeout(Duration.ofMillis(timeout));
}
/* Execute request */
response = httpClient.execute(base);
// Set custom SSL context.
clientBuilder.sslContext(createSSLContext());
HttpClient client = clientBuilder.build();
// Execute request.
try {
HttpResponse<InputStream> response = client.sendAsync(
requestBuilder.build(),
HttpResponse.BodyHandlers.ofInputStream()
).join();
/* Check if response is valid */
if (response == null) {
throw new InvalidResponseException("Response unavailable");
}
switch (response.getStatusLine().getStatusCode()) {
switch (response.statusCode()) {
case 200:
return handleResult(response);
case 204:
@ -332,61 +344,52 @@ public final class RequestHelper implements Serializable {
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);
if (response.statusCode() >= 500 && response.statusCode() < 600 && retries > 0) {
// Retry on 5xx errors.
return request(requestBuilder, retries - 1);
} else {
/* Fail on different error code and/or no retries left */
// 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());
// Throw exception without details, if response entity is empty.
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode());
}
}
} 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.
}
}
} catch (CompletionException e) {
throw new ConnectionException(Error.CONNECTION, e.getCause());
}
}
/**
* Create a custom socket factory from trusted CA certificate.
* Create a custom SSL context from trusted CA certificate.
*
* @return The factory.
* @throws TlsException An error occured during initialization of the SSL context.
* @return The context.
* @throws TlsException An error occurred during initialization of the SSL context.
* @since 0.8.0
* @since 0.10 Generate {@link SSLContext} instead of Apache {@code SSLConnectionSocketFactory}
*/
private SSLConnectionSocketFactory createSSLSocketFactory() throws TlsException {
private SSLContext createSSLContext() throws TlsException {
try {
// Create Keystore with trusted certificate.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("trustedCert", trustedCaCert);
// Create context.
var sslContext = SSLContext.getInstance(tlsVersion);
// Initialize TrustManager.
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
if (trustedCaCert != null) {
// Create Keystore with trusted certificate.
var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("trustedCert", trustedCaCert);
// Create context usint this TrustManager.
SSLContext context = SSLContext.getInstance(tlsVersion);
context.init(null, tmf.getTrustManagers(), new SecureRandom());
// Initialize TrustManager.
var tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
sslContext.init(null, tmf.getTrustManagers(), null);
} else {
sslContext.init(null, null, null);
}
return new SSLConnectionSocketFactory(
context,
null,
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier()
);
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException | KeyManagementException e) {
return sslContext;
} catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException |
KeyManagementException e) {
throw new TlsException(Error.INIT_SSL_CONTEXT, e);
}
}
@ -398,10 +401,9 @@ public final class RequestHelper implements Serializable {
* @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"));
private String handleResult(final HttpResponse<InputStream> response) throws InvalidResponseException {
try (var reader = new BufferedReader(new InputStreamReader(response.body(), UTF_8))) {
return reader.lines().collect(Collectors.joining("\n"));
} catch (IOException ignored) {
throw new InvalidResponseException(Error.READ_RESPONSE, 200);
}
@ -413,18 +415,16 @@ public final class RequestHelper implements Serializable {
* @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"));
private void handleError(final HttpResponse<InputStream> response) throws VaultConnectorException {
if (response.body() != null) {
try (var reader = new BufferedReader(new InputStreamReader(response.body(), UTF_8))) {
var responseString = reader.lines().collect(Collectors.joining("\n"));
ErrorResponse er = jsonMapper.readValue(responseString, ErrorResponse.class);
/* Check for "permission denied" response */
if (!er.getErrors().isEmpty() && er.getErrors().get(0).equals("permission denied")) {
throw new PermissionDeniedException();
}
throw new InvalidResponseException(Error.RESPONSE_CODE,
response.getStatusLine().getStatusCode(), er.toString());
throw new InvalidResponseException(Error.RESPONSE_CODE, response.statusCode(), er.toString());
} catch (IOException ignored) {
// Exception ignored.
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,26 +18,21 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Vault AppRole role metamodel.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @since 1.1 implements {@link Serializable}
*/
@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);
}
public final class AppRole implements Serializable {
private static final long serialVersionUID = -6248529625864573990L;
@JsonProperty("role_name")
private String name;
@ -50,12 +45,8 @@ public final class AppRole {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean bindSecretId;
private List<String> boundCidrList;
private List<String> secretIdBoundCidrs;
private List<String> policies;
@JsonProperty("secret_id_num_uses")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer secretIdNumUses;
@ -64,6 +55,10 @@ public final class AppRole {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer secretIdTtl;
@JsonProperty("enable_local_secret_ids")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean enableLocalSecretIds;
@JsonProperty("token_ttl")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenTtl;
@ -72,77 +67,71 @@ public final class AppRole {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenMaxTtl;
@JsonProperty("period")
private List<String> tokenPolicies;
@JsonProperty("token_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer period;
private List<String> tokenBoundCidrs;
@JsonProperty("token_explicit_max_ttl")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenExplicitMaxTtl;
@JsonProperty("token_no_default_policy")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean tokenNoDefaultPolicy;
@JsonProperty("token_num_uses")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenNumUses;
@JsonProperty("token_period")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenPeriod;
@JsonProperty("token_type")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String tokenType;
/**
* Construct empty {@link AppRole} object.
*/
public AppRole() {
}
/**
* Construct complete {@link AppRole} object.
* Construct {@link AppRole} object from {@link AppRole.Builder}.
*
* @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)
* @param builder AppRole builder.
*/
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;
public AppRole(final Builder builder) {
this.name = builder.name;
this.id = builder.id;
this.bindSecretId = builder.bindSecretId;
this.secretIdBoundCidrs = builder.secretIdBoundCidrs;
this.secretIdNumUses = builder.secretIdNumUses;
this.secretIdTtl = builder.secretIdTtl;
this.enableLocalSecretIds = builder.enableLocalSecretIds;
this.tokenTtl = builder.tokenTtl;
this.tokenMaxTtl = builder.tokenMaxTtl;
this.tokenPolicies = builder.tokenPolicies;
this.tokenBoundCidrs = builder.tokenBoundCidrs;
this.tokenExplicitMaxTtl = builder.tokenExplicitMaxTtl;
this.tokenNoDefaultPolicy = builder.tokenNoDefaultPolicy;
this.tokenNumUses = builder.tokenNumUses;
this.tokenPeriod = builder.tokenPeriod;
this.tokenType = builder.tokenType != null ? builder.tokenType.value() : null;
}
/**
* Construct complete {@link AppRole} object.
* <p>
* This constructor is used for transition from {@code bound_cidr_list} to {@code secret_id_bound_cidrs} only.
* Get {@link Builder} instance.
*
* @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)
* @param name Role name.
* @return AppRole Builder.
* @since 0.8
*/
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;
this.tokenTtl = tokenTtl;
this.tokenMaxTtl = tokenMaxTtl;
this.period = period;
public static Builder builder(final String name) {
return new Builder(name);
}
/**
@ -167,41 +156,38 @@ public final class AppRole {
}
/**
* @return list of bound CIDR subnets
* @deprecated Use {@link #getSecretIdBoundCidrs()} instead, as this parameter is deprecated in Vault.
* @return list of bound CIDR subnets of associated tokens
* @since 0.9
*/
@Deprecated
public List<String> getBoundCidrList() {
return boundCidrList;
public List<String> getTokenBoundCidrs() {
return tokenBoundCidrs;
}
/**
* @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.
* @since 0.9
*/
@Deprecated
@JsonSetter("bound_cidr_list")
public void setBoundCidrList(final List<String> boundCidrList) {
this.boundCidrList = boundCidrList;
@JsonSetter("token_bound_cidrs")
public void setBoundCidrs(final List<String> boundCidrList) {
this.tokenBoundCidrs = boundCidrList;
}
/**
* @return list of subnets in CIDR notation as comma-separated {@link String}
* @deprecated Use {@link #getSecretIdBoundCidrsString()} instead, as this parameter is deprecated in Vault.
* @since 0.9
*/
@Deprecated
@JsonGetter("bound_cidr_list")
@JsonGetter("token_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getBoundCidrListString() {
if (boundCidrList == null || boundCidrList.isEmpty()) {
public String getTokenBoundCidrsString() {
if (tokenBoundCidrs == null || tokenBoundCidrs.isEmpty()) {
return "";
}
return String.join(",", boundCidrList);
return String.join(",", tokenBoundCidrs);
}
/**
* @return list of bound CIDR subnets
* @since 0.8 replaces {@link #getBoundCidrList()}
* @since 0.8 replaces {@code getBoundCidrList()}
*/
public List<String> getSecretIdBoundCidrs() {
return secretIdBoundCidrs;
@ -209,7 +195,7 @@ public final class AppRole {
/**
* @param secretIdBoundCidrs List of subnets in CIDR notation to bind secrets of this role to.
* @since 0.8 replaces {@link #setBoundCidrList(List)}
* @since 0.8 replaces {@code setBoundCidrList(List)}
*/
@JsonSetter("secret_id_bound_cidrs")
public void setSecretIdBoundCidrs(final List<String> secretIdBoundCidrs) {
@ -218,7 +204,7 @@ public final class AppRole {
/**
* @return List of subnets in CIDR notation as comma-separated {@link String}
* @since 0.8 replaces {@link #getBoundCidrListString()} ()}
* @since 0.8 replaces {@code getBoundCidrListString()} ()}
*/
@JsonGetter("secret_id_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ -230,30 +216,33 @@ public final class AppRole {
}
/**
* @return list of policies
* @return list of token policies
* @since 0.9
*/
public List<String> getPolicies() {
return policies;
public List<String> getTokenPolicies() {
return tokenPolicies;
}
/**
* @param policies list of policies
* @param tokenPolicies list of token policies
* @since 0.9
*/
@JsonSetter("policies")
public void setPolicies(final List<String> policies) {
this.policies = policies;
@JsonSetter("token_policies")
public void setTokenPolicies(final List<String> tokenPolicies) {
this.tokenPolicies = tokenPolicies;
}
/**
* @return list of policies as comma-separated {@link String}
* @since 0.9
*/
@JsonGetter("policies")
@JsonGetter("token_policies")
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public String getPoliciesString() {
if (policies == null || policies.isEmpty()) {
public String getTokenPoliciesString() {
if (tokenPolicies == null || tokenPolicies.isEmpty()) {
return "";
}
return String.join(",", policies);
return String.join(",", tokenPolicies);
}
/**
@ -270,6 +259,14 @@ public final class AppRole {
return secretIdTtl;
}
/**
* @return Enable local secret IDs?
* @since 0.9
*/
public Boolean getEnableLocalSecretIds() {
return enableLocalSecretIds;
}
/**
* @return token TTL in seconds
*/
@ -285,9 +282,378 @@ public final class AppRole {
}
/**
* @return duration in seconds, if specified
* @return explicit maximum token TTL in seconds, including renewals
* @since 0.9
*/
public Integer getPeriod() {
return period;
public Integer getTokenExplicitMaxTtl() {
return tokenExplicitMaxTtl;
}
/**
* @return enable default policy for token?
* @since 0.9
*/
public Boolean getTokenNoDefaultPolicy() {
return tokenNoDefaultPolicy;
}
/**
* @return number of uses for token
* @since 0.9
*/
public Integer getTokenNumUses() {
return tokenNumUses;
}
/**
* @return duration in seconds, if specified
* @since 0.9
*/
public Integer getTokenPeriod() {
return tokenPeriod;
}
/**
* @return duration in seconds, if specified
* @since 0.9
*/
public String getTokenType() {
return tokenType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
AppRole appRole = (AppRole) o;
return Objects.equals(name, appRole.name) &&
Objects.equals(id, appRole.id) &&
Objects.equals(bindSecretId, appRole.bindSecretId) &&
Objects.equals(secretIdBoundCidrs, appRole.secretIdBoundCidrs) &&
Objects.equals(secretIdNumUses, appRole.secretIdNumUses) &&
Objects.equals(secretIdTtl, appRole.secretIdTtl) &&
Objects.equals(enableLocalSecretIds, appRole.enableLocalSecretIds) &&
Objects.equals(tokenTtl, appRole.tokenTtl) &&
Objects.equals(tokenMaxTtl, appRole.tokenMaxTtl) &&
Objects.equals(tokenPolicies, appRole.tokenPolicies) &&
Objects.equals(tokenBoundCidrs, appRole.tokenBoundCidrs) &&
Objects.equals(tokenExplicitMaxTtl, appRole.tokenExplicitMaxTtl) &&
Objects.equals(tokenNoDefaultPolicy, appRole.tokenNoDefaultPolicy) &&
Objects.equals(tokenNumUses, appRole.tokenNumUses) &&
Objects.equals(tokenPeriod, appRole.tokenPeriod) &&
Objects.equals(tokenType, appRole.tokenType);
}
@Override
public int hashCode() {
return Objects.hash(name, id, bindSecretId, secretIdBoundCidrs, secretIdNumUses, secretIdTtl,
enableLocalSecretIds, tokenTtl, tokenMaxTtl, tokenPolicies, tokenBoundCidrs, tokenExplicitMaxTtl,
tokenNoDefaultPolicy, tokenNumUses, tokenPeriod, tokenType);
}
/**
* A builder for vault AppRole roles..
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @since 0.9 Moved into subclass of {@link AppRole}.
*/
public static final class Builder {
private String name;
private String id;
private Boolean bindSecretId;
private List<String> secretIdBoundCidrs;
private List<String> tokenPolicies;
private Integer secretIdNumUses;
private Integer secretIdTtl;
private Boolean enableLocalSecretIds;
private Integer tokenTtl;
private Integer tokenMaxTtl;
private List<String> tokenBoundCidrs;
private Integer tokenExplicitMaxTtl;
private Boolean tokenNoDefaultPolicy;
private Integer tokenNumUses;
private Integer tokenPeriod;
private Token.Type tokenType;
/**
* Construct {@link Builder} with only the role name set.
*
* @param name Role name
*/
public Builder(final String name) {
this.name = name;
}
/**
* Add role name.
*
* @param name Role name
* @return self
*/
public Builder withName(final String name) {
this.name = name;
return this;
}
/**
* Add custom role ID. (optional)
*
* @param id the ID
* @return self
*/
public Builder withId(final String id) {
this.id = id;
return this;
}
/**
* Set if role is bound to secret ID.
*
* @param bindSecretId the display name
* @return self
*/
public Builder withBindSecretID(final Boolean bindSecretId) {
this.bindSecretId = bindSecretId;
return this;
}
/**
* Bind role to secret ID.
* Convenience method for {@link #withBindSecretID(Boolean)}
*
* @return self
*/
public Builder withBindSecretID() {
return withBindSecretID(true);
}
/**
* Do not bind role to secret ID.
* Convenience method for {@link #withBindSecretID(Boolean)}
*
* @return self
*/
public Builder withoutBindSecretID() {
return withBindSecretID(false);
}
/**
* Set bound CIDR blocks.
*
* @param secretIdBoundCidrs List of CIDR blocks which can perform login
* @return self
* @since 0.8 replaces {@code withBoundCidrList(List)}
*/
public Builder withSecretIdBoundCidrs(final List<String> secretIdBoundCidrs) {
if (this.secretIdBoundCidrs == null) {
this.secretIdBoundCidrs = new ArrayList<>();
}
this.secretIdBoundCidrs.addAll(secretIdBoundCidrs);
return this;
}
/**
* Add a CIDR block to list of bound blocks for secret.
*
* @param secretBoundCidr the CIDR block
* @return self
* @since 0.9
*/
public Builder withSecretBoundCidr(final String secretBoundCidr) {
if (secretIdBoundCidrs == null) {
secretIdBoundCidrs = new ArrayList<>();
}
secretIdBoundCidrs.add(secretBoundCidr);
return this;
}
/**
* Add given policies.
*
* @param tokenPolicies the token policies
* @return self
* @since 0.9
*/
public Builder withTokenPolicies(final List<String> tokenPolicies) {
if (this.tokenPolicies == null) {
this.tokenPolicies = new ArrayList<>();
}
this.tokenPolicies.addAll(tokenPolicies);
return this;
}
/**
* Add a single policy.
*
* @param tokenPolicy the token policy
* @return self
* @since 0.9
*/
public Builder withTokenPolicy(final String tokenPolicy) {
if (this.tokenPolicies == null) {
this.tokenPolicies = new ArrayList<>();
}
tokenPolicies.add(tokenPolicy);
return this;
}
/**
* Set number of uses for sectet IDs.
*
* @param secretIdNumUses the number of uses
* @return self
*/
public Builder withSecretIdNumUses(final Integer secretIdNumUses) {
this.secretIdNumUses = secretIdNumUses;
return this;
}
/**
* Set default sectet ID TTL in seconds.
*
* @param secretIdTtl the TTL
* @return self
*/
public Builder withSecretIdTtl(final Integer secretIdTtl) {
this.secretIdTtl = secretIdTtl;
return this;
}
/**
* Enable or disable local secret IDs.
*
* @param enableLocalSecretIds Enable local secret IDs?
* @return self
* @since 0.9
*/
public Builder withEnableLocalSecretIds(final Boolean enableLocalSecretIds) {
this.enableLocalSecretIds = enableLocalSecretIds;
return this;
}
/**
* Set default token TTL in seconds.
*
* @param tokenTtl the TTL
* @return self
*/
public Builder withTokenTtl(final Integer tokenTtl) {
this.tokenTtl = tokenTtl;
return this;
}
/**
* Set maximum token TTL in seconds.
*
* @param tokenMaxTtl the TTL
* @return self
*/
public Builder withTokenMaxTtl(final Integer tokenMaxTtl) {
this.tokenMaxTtl = tokenMaxTtl;
return this;
}
/**
* Set bound CIDR blocks for associated tokens.
*
* @param tokenBoundCidrs List of CIDR blocks which can perform login
* @return self
* @since 0.9
*/
public Builder withTokenBoundCidrs(final List<String> tokenBoundCidrs) {
if (this.tokenBoundCidrs == null) {
this.tokenBoundCidrs = new ArrayList<>();
}
this.tokenBoundCidrs.addAll(tokenBoundCidrs);
return this;
}
/**
* Add a CIDR block to list of bound blocks for token.
*
* @param tokenBoundCidr the CIDR block
* @return self
* @since 0.9
*/
public Builder withTokenBoundCidr(final String tokenBoundCidr) {
if (tokenBoundCidrs == null) {
tokenBoundCidrs = new ArrayList<>();
}
tokenBoundCidrs.add(tokenBoundCidr);
return this;
}
/**
* Set explicit maximum token TTL in seconds.
*
* @param tokenExplicitMaxTtl the TTL
* @return self
*/
public Builder withTokenExplicitMaxTtl(final Integer tokenExplicitMaxTtl) {
this.tokenExplicitMaxTtl = tokenExplicitMaxTtl;
return this;
}
/**
* Enable or disable default policy for generated token.
*
* @param tokenNoDefaultPolicy Enable default policy for token?
* @return self
* @since 0.9
*/
public Builder withTokenNoDefaultPolicy(final Boolean tokenNoDefaultPolicy) {
this.tokenNoDefaultPolicy = tokenNoDefaultPolicy;
return this;
}
/**
* Set number of uses for generated tokens.
*
* @param tokenNumUses number of uses for tokens
* @return self
* @since 0.9
*/
public Builder withTokenNumUses(final Integer tokenNumUses) {
this.tokenNumUses = tokenNumUses;
return this;
}
/**
* Set renewal period for generated token in seconds.
*
* @param tokenPeriod period in seconds
* @return self
* @since 0.9
*/
public Builder withTokenPeriod(final Integer tokenPeriod) {
this.tokenPeriod = tokenPeriod;
return this;
}
/**
* Set type of generated token.
*
* @param tokenType token type
* @return self
* @since 0.9
*/
public Builder withTokenType(final Token.Type tokenType) {
this.tokenType = tokenType;
return this;
}
/**
* Build the AppRole role based on given parameters.
*
* @return the role
*/
public AppRole build() {
return new AppRole(this);
}
}
}

View File

@ -1,238 +0,0 @@
/*
* 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;
import java.util.ArrayList;
import java.util.List;
/**
* A builder for vault AppRole roles..
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public final class AppRoleBuilder {
private String name;
private String id;
private Boolean bindSecretId;
private List<String> boundCidrList;
private List<String> secretIdBoundCidrs;
private List<String> policies;
private Integer secretIdNumUses;
private Integer secretIdTtl;
private Integer tokenTtl;
private Integer tokenMaxTtl;
private Integer period;
/**
* Construct {@link AppRoleBuilder} with only the role name set.
*
* @param name Role name
*/
public AppRoleBuilder(final String name) {
this.name = name;
}
/**
* Add custom role ID. (optional)
*
* @param id the ID
* @return self
*/
public AppRoleBuilder withId(final String id) {
this.id = id;
return this;
}
/**
* Set if role is bound to secret ID.
*
* @param bindSecretId the display name
* @return self
*/
public AppRoleBuilder withBindSecretID(final Boolean bindSecretId) {
this.bindSecretId = bindSecretId;
return this;
}
/**
* Bind role to secret ID.
* Convenience method for {@link #withBindSecretID(Boolean)}
*
* @return self
*/
public AppRoleBuilder withBindSecretID() {
return withBindSecretID(true);
}
/**
* Do not bind role to secret ID.
* Convenience method for {@link #withBindSecretID(Boolean)}
*
* @return self
*/
public AppRoleBuilder withoutBindSecretID() {
return withBindSecretID(false);
}
/**
* Set bound CIDR blocks.
*
* @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.
*
* @param cidrBlock the CIDR block
* @return self
*/
public AppRoleBuilder withCidrBlock(final String cidrBlock) {
if (boundCidrList == null) {
boundCidrList = new ArrayList<>();
}
boundCidrList.add(cidrBlock);
if (secretIdBoundCidrs == null) {
secretIdBoundCidrs = new ArrayList<>();
}
secretIdBoundCidrs.add(cidrBlock);
return this;
}
/**
* Add given policies.
*
* @param policies the policies
* @return self
*/
public AppRoleBuilder withPolicies(final List<String> policies) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
this.policies.addAll(policies);
return this;
}
/**
* Add a single policy.
*
* @param policy the policy
* @return self
*/
public AppRoleBuilder withPolicy(final String policy) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.add(policy);
return this;
}
/**
* Set number of uses for sectet IDs.
*
* @param secredIdNumUses the number of uses
* @return self
*/
public AppRoleBuilder withSecretIdNumUses(final Integer secredIdNumUses) {
this.secretIdNumUses = secredIdNumUses;
return this;
}
/**
* Set default sectet ID TTL in seconds.
*
* @param secredIdTtl the TTL
* @return self
*/
public AppRoleBuilder withSecretIdTtl(final Integer secredIdTtl) {
this.secretIdTtl = secredIdTtl;
return this;
}
/**
* Set default token TTL in seconds.
*
* @param tokenTtl the TTL
* @return self
*/
public AppRoleBuilder withTokenTtl(final Integer tokenTtl) {
this.tokenTtl = tokenTtl;
return this;
}
/**
* Set maximum token TTL in seconds.
*
* @param tokenMaxTtl the TTL
* @return self
*/
public AppRoleBuilder withTokenMaxTtl(final Integer tokenMaxTtl) {
this.tokenMaxTtl = tokenMaxTtl;
return this;
}
/**
* Set renewal period for generated token in seconds.
*
* @param period period in seconds
* @return self
*/
public AppRoleBuilder withPeriod(final Integer period) {
this.period = period;
return this;
}
/**
* Build the AppRole role based on given parameters.
*
* @return the role
*/
public AppRole build() {
return new AppRole(name,
id,
bindSecretId,
boundCidrList,
secretIdBoundCidrs,
policies,
secretIdNumUses,
secretIdTtl,
tokenTtl,
tokenMaxTtl,
period);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,17 +18,22 @@ package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.*;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Vault AppRole role metamodel.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleSecret {
public final class AppRoleSecret implements Serializable {
private static final long serialVersionUID = -3401074170145792641L;
@JsonProperty("secret_id")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String id;
@ -166,4 +171,29 @@ public final class AppRoleSecret {
public Integer getTtl() {
return ttl;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
AppRoleSecret that = (AppRoleSecret) o;
return Objects.equals(id, that.id) &&
Objects.equals(accessor, that.accessor) &&
Objects.equals(metadata, that.metadata) &&
Objects.equals(cidrList, that.cidrList) &&
Objects.equals(creationTime, that.creationTime) &&
Objects.equals(expirationTime, that.expirationTime) &&
Objects.equals(lastUpdatedTime, that.lastUpdatedTime) &&
Objects.equals(numUses, that.numUses) &&
Objects.equals(ttl, that.ttl);
}
@Override
public int hashCode() {
return Objects.hash(id, accessor, metadata, cidrList, creationTime, expirationTime, lastUpdatedTime, numUses,
ttl);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -24,6 +24,7 @@ package de.stklcode.jvault.connector.model;
*/
public enum AuthBackend {
TOKEN("token"),
@Deprecated(since = "1.1.3", forRemoval = true)
APPID("app-id"),
APPROLE("approle"),
USERPASS("userpass"),

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,31 +20,28 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Map;
import java.io.Serializable;
import java.util.*;
/**
* Vault Token metamodel.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Token {
/**
* Get {@link TokenBuilder} instance.
*
* @return Token Builder.
* @since 0.8
*/
public static TokenBuilder builder() {
return new TokenBuilder();
}
public final class Token implements Serializable {
private static final long serialVersionUID = 5208508683665365287L;
@JsonProperty("id")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String id;
@JsonProperty("type")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String type;
@JsonProperty("display_name")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String displayName;
@ -61,6 +58,10 @@ public final class Token {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer ttl;
@JsonProperty("explicit_max_ttl")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer explicitMaxTtl;
@JsonProperty("num_uses")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer numUses;
@ -77,37 +78,49 @@ public final class Token {
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean renewable;
@JsonProperty("period")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer period;
@JsonProperty("entity_alias")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String entityAlias;
/**
* Construct complete {@link Token} object.
*
* @param id Token ID (optional)
* @param displayName Token display name (optional)
* @param noParent Token has no parent (optional)
* @param noDefaultPolicy Do not add default policy (optional)
* @param ttl Token TTL in seconds (optional)
* @param numUses Number of uses (optional)
* @param policies List of policies (optional)
* @param meta Metadata (optional)
* @param renewable Is the token renewable (optional)
* Construct empty {@link Token} object.
*/
public Token(final String id,
final String displayName,
final Boolean noParent,
final Boolean noDefaultPolicy,
final Integer ttl,
final Integer numUses,
final List<String> policies,
final Map<String, String> meta,
final Boolean renewable) {
this.id = id;
this.displayName = displayName;
this.ttl = ttl;
this.numUses = numUses;
this.noParent = noParent;
this.noDefaultPolicy = noDefaultPolicy;
this.policies = policies;
this.meta = meta;
this.renewable = renewable;
public Token() {
}
/**
* Construct {@link Token} object from {@link Builder}.
*
* @param builder Token builder.
*/
public Token(final Builder builder) {
this.id = builder.id;
this.type = builder.type != null ? builder.type.value() : null;
this.displayName = builder.displayName;
this.noParent = builder.noParent;
this.noDefaultPolicy = builder.noDefaultPolicy;
this.ttl = builder.ttl;
this.explicitMaxTtl = builder.explicitMaxTtl;
this.numUses = builder.numUses;
this.policies = builder.policies;
this.meta = builder.meta;
this.renewable = builder.renewable;
this.period = builder.period;
this.entityAlias = builder.entityAlias;
}
/**
* Get {@link Builder} instance.
*
* @return Token Builder.
* @since 0.8
*/
public static Builder builder() {
return new Builder();
}
/**
@ -117,6 +130,14 @@ public final class Token {
return id;
}
/**
* @return Token type
* @since 0.9
*/
public String getType() {
return type;
}
/**
* @return Token display name
*/
@ -145,6 +166,14 @@ public final class Token {
return ttl;
}
/**
* @return Explicit maximum time-to-live in seconds
* @since 0.9
*/
public Integer getExplicitMaxTtl() {
return explicitMaxTtl;
}
/**
* @return Number of uses
*/
@ -172,4 +201,354 @@ public final class Token {
public Boolean isRenewable() {
return renewable;
}
/**
* @return Token period.
* @since 0.9
*/
public Integer getPeriod() {
return period;
}
/**
* @return Token entity alias.
* @since 0.9
*/
public String getEntityAlias() {
return entityAlias;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
Token token = (Token) o;
return Objects.equals(id, token.id) &&
Objects.equals(type, token.type) &&
Objects.equals(displayName, token.displayName) &&
Objects.equals(noParent, token.noParent) &&
Objects.equals(noDefaultPolicy, token.noDefaultPolicy) &&
Objects.equals(ttl, token.ttl) &&
Objects.equals(explicitMaxTtl, token.explicitMaxTtl) &&
Objects.equals(numUses, token.numUses) &&
Objects.equals(policies, token.policies) &&
Objects.equals(meta, token.meta) &&
Objects.equals(renewable, token.renewable) &&
Objects.equals(period, token.period) &&
Objects.equals(entityAlias, token.entityAlias);
}
@Override
public int hashCode() {
return Objects.hash(id, type, displayName, noParent, noDefaultPolicy, ttl, explicitMaxTtl, numUses, policies,
meta, renewable, period, entityAlias);
}
/**
* Constants for token types.
*/
public enum Type {
DEFAULT("default"),
BATCH("batch"),
SERVICE("service"),
DEFAULT_SERVICE("default-service"),
DEFAULT_BATCH("default-batch");
private final String value;
Type(String value) {
this.value = value;
}
public String value() {
return value;
}
}
/**
* A builder for vault tokens.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @since 0.9 Moved into subclass of {@link Token}.
*/
public static final class Builder {
private String id;
private Type type;
private String displayName;
private Boolean noParent;
private Boolean noDefaultPolicy;
private Integer ttl;
private Integer explicitMaxTtl;
private Integer numUses;
private List<String> policies;
private Map<String, String> meta;
private Boolean renewable;
private Integer period;
private String entityAlias;
/**
* Add token ID. (optional)
*
* @param id the ID
* @return self
*/
public Builder withId(final String id) {
this.id = id;
return this;
}
/**
* Specify token type.
*
* @param type the type
* @return self
* @since 0.9
*/
public Builder withType(final Token.Type type) {
this.type = type;
return this;
}
/**
* Add display name.
*
* @param displayName the display name
* @return self
*/
public Builder withDisplayName(final String displayName) {
this.displayName = displayName;
return this;
}
/**
* Set desired time to live.
*
* @param ttl the ttl
* @return self
*/
public Builder withTtl(final Integer ttl) {
this.ttl = ttl;
return this;
}
/**
* Set desired explicit maximum time to live.
*
* @param explicitMaxTtl the explicit max. TTL
* @return self
*/
public Builder withExplicitMaxTtl(final Integer explicitMaxTtl) {
this.explicitMaxTtl = explicitMaxTtl;
return this;
}
/**
* Set desired number of uses.
*
* @param numUses the number of uses
* @return self
*/
public Builder withNumUses(final Integer numUses) {
this.numUses = numUses;
return this;
}
/**
* Set TRUE if the token should be created without parent.
*
* @param noParent if TRUE, token is created as orphan
* @return self
*/
public Builder withNoParent(final boolean noParent) {
this.noParent = noParent;
return this;
}
/**
* Create token without parent.
* Convenience method for withNoParent()
*
* @return self
*/
public Builder asOrphan() {
return withNoParent(true);
}
/**
* Create token with parent.
* Convenience method for withNoParent()
*
* @return self
*/
public Builder withParent() {
return withNoParent(false);
}
/**
* Set TRUE if the default policy should not be part of this token.
*
* @param noDefaultPolicy if TRUE, default policy is not attached
* @return self
*/
public Builder withNoDefaultPolicy(final boolean noDefaultPolicy) {
this.noDefaultPolicy = noDefaultPolicy;
return this;
}
/**
* Attach default policy to token.
* Convenience method for withNoDefaultPolicy()
*
* @return self
*/
public Builder withDefaultPolicy() {
return withNoDefaultPolicy(false);
}
/**
* Do not attach default policy to token.
* Convenience method for withNoDefaultPolicy()
*
* @return self
*/
public Builder withoutDefaultPolicy() {
return withNoDefaultPolicy(true);
}
/**
* Add given policies.
*
* @param policies the policies
* @return self
* @since 0.5.0
*/
public Builder withPolicies(final String... policies) {
return withPolicies(Arrays.asList(policies));
}
/**
* Add given policies.
*
* @param policies the policies
* @return self
*/
public Builder withPolicies(final List<String> policies) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
this.policies.addAll(policies);
return this;
}
/**
* Add a single policy.
*
* @param policy the policy
* @return self
*/
public Builder withPolicy(final String policy) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.add(policy);
return this;
}
/**
* Add meta data.
*
* @param meta the metadata
* @return self
*/
public Builder withMeta(final Map<String, String> meta) {
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.putAll(meta);
return this;
}
/**
* Add meta data.
*
* @param key the key
* @param value the value
* @return self
*/
public Builder withMeta(final String key, final String value) {
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.put(key, value);
return this;
}
/**
* Set if token is renewable.
*
* @param renewable TRUE, if renewable
* @return self
*/
public Builder withRenewable(final Boolean renewable) {
this.renewable = renewable;
return this;
}
/**
* Set token to be renewable.
* Convenience method for withRenewable()
*
* @return self
*/
public Builder renewable() {
return withRenewable(true);
}
/**
* Set token to be not renewable.
* Convenience method for withRenewable()
*
* @return self
*/
public Builder notRenewable() {
return withRenewable(false);
}
/**
* Set token period (former lease time).
*
* @param period Period in seconds.
* @return self
*/
public Builder withPeriod(final Integer period) {
this.period = period;
return this;
}
/**
* Set entity alias for token.
* Only works in combination with an associated token role.
*
* @param entityAlias Entity alias.
* @return self
*/
public Builder withEntityAlias(final String entityAlias) {
this.entityAlias = entityAlias;
return this;
}
/**
* Build the token based on given parameters.
*
* @return the token
*/
public Token build() {
return new Token(this);
}
}
}

View File

@ -1,259 +0,0 @@
/*
* 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;
import java.util.*;
/**
* A builder for vault tokens.
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public final class TokenBuilder {
private String id;
private String displayName;
private Boolean noParent;
private Boolean noDefaultPolicy;
private Integer ttl;
private Integer numUses;
private List<String> policies;
private Map<String, String> meta;
private Boolean renewable;
/**
* Add token ID. (optional)
*
* @param id the ID
* @return self
*/
public TokenBuilder withId(final String id) {
this.id = id;
return this;
}
/**
* Add display name.
*
* @param displayName the display name
* @return self
*/
public TokenBuilder withDisplayName(final String displayName) {
this.displayName = displayName;
return this;
}
/**
* Set desired time to live.
*
* @param ttl the ttl
* @return self
*/
public TokenBuilder withTtl(final Integer ttl) {
this.ttl = ttl;
return this;
}
/**
* Set desired number of uses.
*
* @param numUses the number of uses
* @return self
*/
public TokenBuilder withNumUses(final Integer numUses) {
this.numUses = numUses;
return this;
}
/**
* Set TRUE if the token should be created without parent.
*
* @param noParent if TRUE, token is created as orphan
* @return self
*/
public TokenBuilder withNoParent(final boolean noParent) {
this.noParent = noParent;
return this;
}
/**
* Create token without parent.
* Convenience method for withNoParent()
*
* @return self
*/
public TokenBuilder asOrphan() {
return withNoParent(true);
}
/**
* Create token with parent.
* Convenience method for withNoParent()
*
* @return self
*/
public TokenBuilder withParent() {
return withNoParent(false);
}
/**
* Set TRUE if the default policy should not be part of this token.
*
* @param noDefaultPolicy if TRUE, default policy is not attached
* @return self
*/
public TokenBuilder withNoDefaultPolicy(final boolean noDefaultPolicy) {
this.noDefaultPolicy = noDefaultPolicy;
return this;
}
/**
* Attach default policy to token.
* Convenience method for withNoDefaultPolicy()
*
* @return self
*/
public TokenBuilder withDefaultPolicy() {
return withNoDefaultPolicy(false);
}
/**
* Do not attach default policy to token.
* Convenience method for withNoDefaultPolicy()
*
* @return self
*/
public TokenBuilder withoutDefaultPolicy() {
return withNoDefaultPolicy(true);
}
/**
* Add given policies.
*
* @param policies the policies
* @return self
* @since 0.5.0
*/
public TokenBuilder withPolicies(final String... policies) {
return withPolicies(Arrays.asList(policies));
}
/**
* Add given policies.
*
* @param policies the policies
* @return self
*/
public TokenBuilder withPolicies(final List<String> policies) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
this.policies.addAll(policies);
return this;
}
/**
* Add a single policy.
*
* @param policy the policy
* @return self
*/
public TokenBuilder withPolicy(final String policy) {
if (this.policies == null) {
this.policies = new ArrayList<>();
}
policies.add(policy);
return this;
}
/**
* Add meta data.
*
* @param meta the metadata
* @return self
*/
public TokenBuilder withMeta(final Map<String, String> meta) {
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.putAll(meta);
return this;
}
/**
* Add meta data.
*
* @param key the key
* @param value the value
* @return self
*/
public TokenBuilder withMeta(final String key, final String value) {
if (this.meta == null) {
this.meta = new HashMap<>();
}
this.meta.put(key, value);
return this;
}
/**
* Set if token is renewable.
*
* @param renewable TRUE, if renewable
* @return self
*/
public TokenBuilder withRenewable(final Boolean renewable) {
this.renewable = renewable;
return this;
}
/**
* Set token to be renewable.
* Convenience method for withRenewable()
*
* @return self
*/
public TokenBuilder renewable() {
return withRenewable(true);
}
/**
* Set token to be not renewable.
* Convenience method for withRenewable()
*
* @return self
*/
public TokenBuilder notRenewable() {
return withRenewable(false);
}
/**
* Build the token based on given parameters.
*
* @return the token
*/
public Token build() {
return new Token(id,
displayName,
noParent,
noDefaultPolicy,
ttl,
numUses,
policies,
meta,
renewable);
}
}

View File

@ -0,0 +1,598 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Vault Token Role metamodel.
*
* @author Stefan Kalscheuer
* @since 0.9
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenRole implements Serializable {
private static final long serialVersionUID = -3505215215838576321L;
@JsonProperty("name")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String name;
@JsonProperty("allowed_policies")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> allowedPolicies;
@JsonProperty("allowed_policies_glob")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> allowedPoliciesGlob;
@JsonProperty("disallowed_policies")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> disallowedPolicies;
@JsonProperty("disallowed_policies_glob")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> disallowedPoliciesGlob;
@JsonProperty("orphan")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean orphan;
@JsonProperty("renewable")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean renewable;
@JsonProperty("path_suffix")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String pathSuffix;
@JsonProperty("allowed_entity_aliases")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> allowedEntityAliases;
@JsonProperty("token_bound_cidrs")
@JsonInclude(JsonInclude.Include.NON_NULL)
private List<String> tokenBoundCidrs;
@JsonProperty("token_explicit_max_ttl")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenExplicitMaxTtl;
@JsonProperty("token_no_default_policy")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Boolean tokenNoDefaultPolicy;
@JsonProperty("token_num_uses")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenNumUses;
@JsonProperty("token_period")
@JsonInclude(JsonInclude.Include.NON_NULL)
private Integer tokenPeriod;
@JsonProperty("token_type")
@JsonInclude(JsonInclude.Include.NON_NULL)
private String tokenType;
/**
* Construct empty {@link TokenRole} object.
*/
public TokenRole() {
}
public TokenRole(final Builder builder) {
this.name = builder.name;
this.allowedPolicies = builder.allowedPolicies;
this.allowedPoliciesGlob = builder.allowedPoliciesGlob;
this.disallowedPolicies = builder.disallowedPolicies;
this.disallowedPoliciesGlob = builder.disallowedPoliciesGlob;
this.orphan = builder.orphan;
this.renewable = builder.renewable;
this.pathSuffix = builder.pathSuffix;
this.allowedEntityAliases = builder.allowedEntityAliases;
this.tokenBoundCidrs = builder.tokenBoundCidrs;
this.tokenExplicitMaxTtl = builder.tokenExplicitMaxTtl;
this.tokenNoDefaultPolicy = builder.tokenNoDefaultPolicy;
this.tokenNumUses = builder.tokenNumUses;
this.tokenPeriod = builder.tokenPeriod;
this.tokenType = builder.tokenType != null ? builder.tokenType.value() : null;
}
/**
* Get {@link Builder} instance.
*
* @return Token Role Builder.
*/
public static Builder builder() {
return new Builder();
}
/**
* @return Token Role name
*/
public String getName() {
return name;
}
/**
* @return List of allowed policies
*/
public List<String> getAllowedPolicies() {
return allowedPolicies;
}
/**
* @return List of allowed policy glob patterns
* @since 1.1
*/
public List<String> getAllowedPoliciesGlob() {
return allowedPoliciesGlob;
}
/**
* @return List of disallowed policies
*/
public List<String> getDisallowedPolicies() {
return disallowedPolicies;
}
/**
* @return List of disallowed policy glob patterns
* @since 1.1
*/
public List<String> getDisallowedPoliciesGlob() {
return disallowedPoliciesGlob;
}
/**
* @return Is Token Role orphan?
*/
public Boolean getOrphan() {
return orphan;
}
/**
* @return Is Token Role renewable?
*/
public Boolean getRenewable() {
return renewable;
}
/**
* @return Path suffix
*/
public String getPathSuffix() {
return pathSuffix;
}
/**
* @return List of allowed entity aliases
*/
public List<String> getAllowedEntityAliases() {
return allowedEntityAliases;
}
/**
* @return Token bound CIDR blocks
*/
public List<String> getTokenBoundCidrs() {
return tokenBoundCidrs;
}
/**
* @return Token explicit maximum TTL
*/
public Integer getTokenExplicitMaxTtl() {
return tokenExplicitMaxTtl;
}
/**
* @return Token without default policy?
*/
public Boolean getTokenNoDefaultPolicy() {
return tokenNoDefaultPolicy;
}
/**
* @return Token number of uses
*/
public Integer getTokenNumUses() {
return tokenNumUses;
}
/**
* @return Token period
*/
public Integer getTokenPeriod() {
return tokenPeriod;
}
/**
* @return Token type
*/
public String getTokenType() {
return tokenType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
TokenRole tokenRole = (TokenRole) o;
return Objects.equals(name, tokenRole.name) &&
Objects.equals(allowedPolicies, tokenRole.allowedPolicies) &&
Objects.equals(allowedPoliciesGlob, tokenRole.allowedPoliciesGlob) &&
Objects.equals(disallowedPolicies, tokenRole.disallowedPolicies) &&
Objects.equals(disallowedPoliciesGlob, tokenRole.disallowedPoliciesGlob) &&
Objects.equals(orphan, tokenRole.orphan) &&
Objects.equals(renewable, tokenRole.renewable) &&
Objects.equals(pathSuffix, tokenRole.pathSuffix) &&
Objects.equals(allowedEntityAliases, tokenRole.allowedEntityAliases) &&
Objects.equals(tokenBoundCidrs, tokenRole.tokenBoundCidrs) &&
Objects.equals(tokenExplicitMaxTtl, tokenRole.tokenExplicitMaxTtl) &&
Objects.equals(tokenNoDefaultPolicy, tokenRole.tokenNoDefaultPolicy) &&
Objects.equals(tokenNumUses, tokenRole.tokenNumUses) &&
Objects.equals(tokenPeriod, tokenRole.tokenPeriod) &&
Objects.equals(tokenType, tokenRole.tokenType);
}
@Override
public int hashCode() {
return Objects.hash(name, allowedPolicies, allowedPoliciesGlob, disallowedPolicies, disallowedPoliciesGlob,
orphan, renewable, pathSuffix, allowedEntityAliases, tokenBoundCidrs, tokenExplicitMaxTtl,
tokenNoDefaultPolicy, tokenNumUses, tokenPeriod, tokenType);
}
/**
* A builder for vault token roles.
*
* @author Stefan Kalscheuer
* @since 0.9
*/
public static final class Builder {
private String name;
private List<String> allowedPolicies;
private List<String> allowedPoliciesGlob;
private List<String> disallowedPolicies;
private List<String> disallowedPoliciesGlob;
private Boolean orphan;
private Boolean renewable;
private String pathSuffix;
private List<String> allowedEntityAliases;
private List<String> tokenBoundCidrs;
private Integer tokenExplicitMaxTtl;
private Boolean tokenNoDefaultPolicy;
private Integer tokenNumUses;
private Integer tokenPeriod;
private Token.Type tokenType;
/**
* Add token role name.
*
* @param name role name
* @return self
*/
public Builder forName(final String name) {
this.name = name;
return this;
}
/**
* Add an allowed policy.
*
* @param allowedPolicy allowed policy to add
* @return self
*/
public Builder withAllowedPolicy(final String allowedPolicy) {
if (allowedPolicy != null) {
if (this.allowedPolicies == null) {
this.allowedPolicies = new ArrayList<>();
}
this.allowedPolicies.add(allowedPolicy);
}
return this;
}
/**
* Add allowed policies.
*
* @param allowedPolicies list of allowed policies
* @return self
*/
public Builder withAllowedPolicies(final List<String> allowedPolicies) {
if (allowedPolicies != null) {
if (this.allowedPolicies == null) {
this.allowedPolicies = new ArrayList<>();
}
this.allowedPolicies.addAll(allowedPolicies);
}
return this;
}
/**
* Add an allowed policy glob pattern.
*
* @param allowedPolicyGlob allowed policy glob pattern to add
* @return self
* @since 1.1
*/
public Builder withAllowedPolicyGlob(final String allowedPolicyGlob) {
if (allowedPolicyGlob != null) {
if (this.allowedPoliciesGlob == null) {
this.allowedPoliciesGlob = new ArrayList<>();
}
this.allowedPoliciesGlob.add(allowedPolicyGlob);
}
return this;
}
/**
* Add allowed policy glob patterns.
*
* @param allowedPoliciesGlob list of allowed policy glob patterns
* @return self
* @since 1.1
*/
public Builder withAllowedPoliciesGlob(final List<String> allowedPoliciesGlob) {
if (allowedPoliciesGlob != null) {
if (this.allowedPoliciesGlob == null) {
this.allowedPoliciesGlob = new ArrayList<>();
}
this.allowedPoliciesGlob.addAll(allowedPoliciesGlob);
}
return this;
}
/**
* Add a disallowed policy.
*
* @param disallowedPolicy disallowed policy to add
* @return self
*/
public Builder withDisallowedPolicy(final String disallowedPolicy) {
if (disallowedPolicy != null) {
if (this.disallowedPolicies == null) {
this.disallowedPolicies = new ArrayList<>();
}
this.disallowedPolicies.add(disallowedPolicy);
}
return this;
}
/**
* Add disallowed policies.
*
* @param disallowedPolicies list of disallowed policies
* @return self
*/
public Builder withDisallowedPolicies(final List<String> disallowedPolicies) {
if (disallowedPolicies != null) {
if (this.disallowedPolicies == null) {
this.disallowedPolicies = new ArrayList<>();
}
this.disallowedPolicies.addAll(disallowedPolicies);
}
return this;
}
/**
* Add an allowed policy glob pattern.
*
* @param disallowedPolicyGlob disallowed policy glob pattern to add
* @return self
* @since 1.1
*/
public Builder withDisallowedPolicyGlob(final String disallowedPolicyGlob) {
if (disallowedPolicyGlob != null) {
if (this.disallowedPoliciesGlob == null) {
this.disallowedPoliciesGlob = new ArrayList<>();
}
this.disallowedPoliciesGlob.add(disallowedPolicyGlob);
}
return this;
}
/**
* Add disallowed policy glob patterns.
*
* @param disallowedPoliciesGlob list of disallowed policy glob patterns
* @return self
* @since 1.1
*/
public Builder withDisallowedPoliciesGlob(final List<String> disallowedPoliciesGlob) {
if (disallowedPoliciesGlob != null) {
if (this.disallowedPoliciesGlob == null) {
this.disallowedPoliciesGlob = new ArrayList<>();
}
this.disallowedPoliciesGlob.addAll(disallowedPoliciesGlob);
}
return this;
}
/**
* Set TRUE if the token role should be created orphan.
*
* @param orphan if TRUE, token role is created as orphan
* @return self
*/
public Builder orphan(final Boolean orphan) {
this.orphan = orphan;
return this;
}
/**
* Set TRUE if the token role should be created renewable.
*
* @param renewable if TRUE, token role is created renewable
* @return self
*/
public Builder renewable(final Boolean renewable) {
this.renewable = renewable;
return this;
}
/**
* Set token role path suffix.
*
* @param pathSuffix path suffix to use
* @return self
*/
public Builder withPathSuffix(final String pathSuffix) {
this.pathSuffix = pathSuffix;
return this;
}
/**
* Add an allowed entity alias.
*
* @param allowedEntityAlias allowed entity alias to add
* @return self
*/
public Builder withAllowedEntityAlias(final String allowedEntityAlias) {
if (allowedEntityAlias != null) {
if (this.allowedEntityAliases == null) {
this.allowedEntityAliases = new ArrayList<>();
}
this.allowedEntityAliases.add(allowedEntityAlias);
}
return this;
}
/**
* Add allowed entity aliases.
*
* @param allowedEntityAliases list of allowed entity aliases to add
* @return self
*/
public Builder withAllowedEntityAliases(final List<String> allowedEntityAliases) {
if (allowedEntityAliases != null) {
if (this.allowedEntityAliases == null) {
this.allowedEntityAliases = new ArrayList<>();
}
this.allowedEntityAliases.addAll(allowedEntityAliases);
}
return this;
}
/**
* Add a single bound CIDR.
*
* @param tokenBoundCidr bound CIDR to add
* @return self
*/
public Builder withTokenBoundCidr(final String tokenBoundCidr) {
if (tokenBoundCidr != null) {
if (this.tokenBoundCidrs == null) {
this.tokenBoundCidrs = new ArrayList<>();
}
this.tokenBoundCidrs.add(tokenBoundCidr);
}
return this;
}
/**
* Add a list of bound CIDRs.
*
* @param tokenBoundCidrs list of bound CIDRs to add
* @return self
*/
public Builder withTokenBoundCidrs(final List<String> tokenBoundCidrs) {
if (tokenBoundCidrs != null) {
if (this.tokenBoundCidrs == null) {
this.tokenBoundCidrs = new ArrayList<>();
}
this.tokenBoundCidrs.addAll(tokenBoundCidrs);
}
return this;
}
/**
* Set explicit max. TTL for token.
*
* @param tokenExplicitMaxTtl explicit maximum TTL
* @return self
*/
public Builder withTokenExplicitMaxTtl(final Integer tokenExplicitMaxTtl) {
this.tokenExplicitMaxTtl = tokenExplicitMaxTtl;
return this;
}
/**
* Set TRUE if the token role should be created renewable.
*
* @param tokenNoDefaultPolicy if TRUE, token is created without default policy.
* @return self
*/
public Builder withTokenNoDefaultPolicy(final Boolean tokenNoDefaultPolicy) {
this.tokenNoDefaultPolicy = tokenNoDefaultPolicy;
return this;
}
/**
* Set number of uses for tokens.
*
* @param tokenNumUses number of uses for associated tokens.
* @return self
*/
public Builder withTokenNumUses(final Integer tokenNumUses) {
this.tokenNumUses = tokenNumUses;
return this;
}
/**
* Set token period.
*
* @param tokenPeriod token period
* @return self
*/
public Builder withTokenPeriod(final Integer tokenPeriod) {
this.tokenPeriod = tokenPeriod;
return this;
}
/**
* Set token type.
*
* @param tokenType token type
* @return self
*/
public Builder withTokenType(final Token.Type tokenType) {
this.tokenType = tokenType;
return this;
}
/**
* Build the token based on given parameters.
*
* @return the token
*/
public TokenRole build() {
return new TokenRole(this);
}
}
}

View File

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

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,10 @@
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 com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.AppRole;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for AppRole lookup.
@ -33,24 +30,10 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleResponse extends VaultDataResponse {
private AppRole role;
private static final long serialVersionUID = -6536422219633829177L;
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
/* 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);
}
});
this.role = mapper.readValue(mapper.writeValueAsString(filteredData), AppRole.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
@JsonProperty("data")
private AppRole role;
/**
* @return The role
@ -58,4 +41,20 @@ public final class AppRoleResponse extends VaultDataResponse {
public AppRole getRole() {
return role;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
AppRoleResponse that = (AppRoleResponse) o;
return Objects.equals(role, that.role);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), role);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,13 +17,10 @@
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 com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.AppRoleSecret;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for AppRole lookup.
@ -33,24 +30,10 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AppRoleSecretResponse extends VaultDataResponse {
private AppRoleSecret secret;
private static final long serialVersionUID = -2484103304072370585L;
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
/* 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);
}
});
this.secret = mapper.readValue(mapper.writeValueAsString(filteredData), AppRoleSecret.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
@JsonProperty("data")
private AppRoleSecret secret;
/**
* @return The secret
@ -58,4 +41,20 @@ public final class AppRoleSecretResponse extends VaultDataResponse {
public AppRoleSecret getSecret() {
return secret;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
AppRoleSecretResponse that = (AppRoleSecretResponse) o;
return Objects.equals(secret, that.secret);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), secret);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,22 +17,24 @@
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 com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Authentication method response.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethodsResponse extends VaultDataResponse {
private static final long serialVersionUID = -1802724129533405375L;
@JsonProperty("data")
private Map<String, AuthMethod> supportedMethods;
/**
@ -42,23 +44,26 @@ public final class AuthMethodsResponse extends VaultDataResponse {
this.supportedMethods = new HashMap<>();
}
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
for (Map.Entry<String, Object> entry : data.entrySet()) {
try {
this.supportedMethods.put(entry.getKey(),
mapper.readValue(mapper.writeValueAsString(entry.getValue()), AuthMethod.class));
} catch (IOException e) {
throw new InvalidResponseException();
}
}
}
/**
* @return Supported authentication methods
*/
public Map<String, AuthMethod> getSupportedMethods() {
return supportedMethods;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
AuthMethodsResponse that = (AuthMethodsResponse) o;
return Objects.equals(supportedMethods, that.supportedMethods);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), supportedMethods);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,9 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for authentication providing auth info in {@link AuthData} field.
@ -33,37 +30,10 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthResponse extends VaultDataResponse {
private Map<String, Object> data;
private static final long serialVersionUID = 1628851361067456715L;
private AuthData auth;
/**
* Set authentication data. The input will be mapped to the {@link AuthData} model.
*
* @param auth Raw authentication data
* @throws InvalidResponseException on mapping errors
*/
@JsonProperty("auth")
public void setAuth(final Map<String, Object> auth) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
this.auth = mapper.readValue(mapper.writeValueAsString(auth), AuthData.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
@Override
public void setData(final Map<String, Object> data) {
this.data = data;
}
/**
* @return Raw data
*/
public Map<String, Object> getData() {
return data;
}
private AuthData auth;
/**
* @return Authentication data
@ -71,4 +41,20 @@ public final class AuthResponse extends VaultDataResponse {
public AuthData getAuth() {
return auth;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
AuthResponse that = (AuthResponse) o;
return Objects.equals(auth, that.auth);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), auth);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,11 +21,12 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/**
* Vault response from credentials lookup. Simple wrapper for data objects containing username and password fields.
*
* @author Stefan Kalscheuer
* @since 0.5.0
* @author Stefan Kalscheuer
* @since 0.5.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class CredentialsResponse extends SecretResponse {
public final class CredentialsResponse extends PlainSecretResponse {
private static final long serialVersionUID = -1439692963299045425L;
/**
* @return Username

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,15 +20,18 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
import java.util.Objects;
/**
* Vault response in case of errors.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class ErrorResponse implements VaultResponse {
private static final long serialVersionUID = -6227368087842549149L;
@JsonProperty("errors")
private List<String> errors;
@ -47,4 +50,20 @@ public final class ErrorResponse implements VaultResponse {
return errors.get(0);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
ErrorResponse that = (ErrorResponse) o;
return Objects.equals(errors, that.errors);
}
@Override
public int hashCode() {
return Objects.hash(errors);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,18 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
/**
* Vault response for health query.
*
* @author Stefan Kalscheuer
* @since 0.7.0
* @author Stefan Kalscheuer
* @since 0.7.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class HealthResponse implements VaultResponse {
private static final long serialVersionUID = 6483840078694294401L;
@JsonProperty("cluster_id")
private String clusterID;
@ -48,7 +52,7 @@ public final class HealthResponse implements VaultResponse {
@JsonProperty("initialized")
private Boolean initialized;
@JsonProperty("replication_perf_mode")
@JsonProperty("replication_performance_mode")
private String replicationPerfMode;
@JsonProperty("replication_dr_mode")
@ -129,4 +133,30 @@ public final class HealthResponse implements VaultResponse {
public Boolean isPerformanceStandby() {
return performanceStandby;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
HealthResponse that = (HealthResponse) o;
return Objects.equals(clusterID, that.clusterID) &&
Objects.equals(clusterName, that.clusterName) &&
Objects.equals(version, that.version) &&
Objects.equals(serverTimeUTC, that.serverTimeUTC) &&
Objects.equals(standby, that.standby) &&
Objects.equals(sealed, that.sealed) &&
Objects.equals(initialized, that.initialized) &&
Objects.equals(replicationPerfMode, that.replicationPerfMode) &&
Objects.equals(replicationDrMode, that.replicationDrMode) &&
Objects.equals(performanceStandby, that.performanceStandby);
}
@Override
public int hashCode() {
return Objects.hash(clusterID, clusterName, version, serverTimeUTC, standby, sealed, initialized,
replicationPerfMode, replicationDrMode, performanceStandby);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,18 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
/**
* Vault response for help request.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class HelpResponse implements VaultResponse {
private static final long serialVersionUID = -1152070966642848490L;
@JsonProperty("help")
private String help;
@ -36,4 +40,20 @@ public final class HelpResponse implements VaultResponse {
public String getHelp() {
return help;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
HelpResponse that = (HelpResponse) o;
return Objects.equals(help, that.help);
}
@Override
public int hashCode() {
return Objects.hash(help);
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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;
import de.stklcode.jvault.connector.model.response.embedded.SecretWrapper;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for secret responses with metadata.
*
* @author Stefan Kalscheuer
* @since 1.1 abstract
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MetaSecretResponse extends SecretResponse {
private static final long serialVersionUID = -1076542846391240162L;
@JsonProperty("data")
private SecretWrapper secret;
@Override
public final Map<String, Serializable> getData() {
if (secret != null) {
return secret.getData();
} else {
return Collections.emptyMap();
}
}
@Override
public final VersionMetadata getMetadata() {
if (secret != null) {
return secret.getMetadata();
} else {
return null;
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
MetaSecretResponse that = (MetaSecretResponse) o;
return Objects.equals(secret, that.secret);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), secret);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,12 +17,11 @@
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 com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.SecretMetadata;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for secret metadata (KV v2).
@ -32,19 +31,11 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class MetadataResponse extends VaultDataResponse {
private static final long serialVersionUID = -3679762333630984679L;
@JsonProperty("data")
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.
*
@ -53,4 +44,20 @@ public class MetadataResponse extends VaultDataResponse {
public SecretMetadata getMetadata() {
return metadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
MetadataResponse that = (MetadataResponse) o;
return Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), metadata);
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for plain secret responses.
*
* @author Stefan Kalscheuer
* @since 1.1 abstract
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class PlainSecretResponse extends SecretResponse {
private static final long serialVersionUID = 3010138542437913023L;
@JsonProperty("data")
private Map<String, Serializable> data;
@Override
public final Map<String, Serializable> getData() {
return Objects.requireNonNullElseGet(data, Collections::emptyMap);
}
@Override
public final VersionMetadata getMetadata() {
return null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
PlainSecretResponse that = (PlainSecretResponse) o;
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,28 +17,45 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* Simple Vault data response.
*
* @author Stefan Kalscheuer
* @since 0.4.0
* @author Stefan Kalscheuer
* @since 0.4.0
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class RawDataResponse extends VaultDataResponse {
private Map<String, Object> data;
private static final long serialVersionUID = -319727427792124071L;
@Override
public void setData(final Map<String, Object> data) {
this.data = data;
}
@JsonProperty("data")
private Map<String, Serializable> data;
/**
* @return Raw data {@link Map}
*/
public Map<String, Object> getData() {
public Map<String, Serializable> getData() {
return data;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
RawDataResponse that = (RawDataResponse) o;
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,6 +19,8 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Objects;
/**
* Vault response for seal status or unseal request.
*
@ -27,6 +29,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SealResponse implements VaultResponse {
private static final long serialVersionUID = -3661916639367542617L;
@JsonProperty("type")
private String type;
@ -57,6 +61,15 @@ public final class SealResponse implements VaultResponse {
@JsonProperty("cluster_id")
private String clusterId;
@JsonProperty("migration")
private Boolean migration;
@JsonProperty("recovery_seal")
private Boolean recoverySeal;
@JsonProperty("storage_type")
private String storageType;
/**
* @return Seal type.
* @since 0.8
@ -132,4 +145,57 @@ public final class SealResponse implements VaultResponse {
public String getClusterId() {
return clusterId;
}
/**
* @return Migration status (since Vault 1.4)
* @since 1.1
*/
public Boolean getMigration() {
return migration;
}
/**
* @return Recovery seal status.
* @since 1.1
*/
public Boolean getRecoverySeal() {
return recoverySeal;
}
/**
* @return Storage type (since Vault 1.3).
* @since 1.1
*/
public String getStorageType() {
return storageType;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SealResponse that = (SealResponse) o;
return sealed == that.sealed &&
initialized == that.initialized &&
Objects.equals(type, that.type) &&
Objects.equals(threshold, that.threshold) &&
Objects.equals(numberOfShares, that.numberOfShares) &&
Objects.equals(progress, that.progress) &&
Objects.equals(version, that.version) &&
Objects.equals(nonce, that.nonce) &&
Objects.equals(clusterName, that.clusterName) &&
Objects.equals(clusterId, that.clusterId) &&
Objects.equals(migration, that.migration) &&
Objects.equals(recoverySeal, that.recoverySeal) &&
Objects.equals(storageType, that.storageType);
}
@Override
public int hashCode() {
return Objects.hash(type, sealed, initialized, threshold, numberOfShares, progress, version, nonce,
clusterName, clusterId, migration, recoverySeal, storageType);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,10 +18,11 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.SecretListWrapper;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for secret list request.
@ -31,27 +32,34 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretListResponse extends VaultDataResponse {
private List<String> keys;
/**
* Set data. Extracts list of keys from raw response data.
*
* @param data Raw data
* @throws InvalidResponseException on parsing errors
*/
private static final long serialVersionUID = 8597121175002967213L;
@JsonProperty("data")
public void setData(final Map<String, Object> data) throws InvalidResponseException {
try {
this.keys = (List<String>) data.get("keys");
} catch (ClassCastException e) {
throw new InvalidResponseException("Keys could not be parsed from data.", e);
}
}
private SecretListWrapper data;
/**
* @return List of secret keys
*/
public List<String> getKeys() {
return keys;
if (data == null) {
return Collections.emptyList();
}
return Objects.requireNonNullElseGet(data.getKeys(), Collections::emptyList);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
SecretListResponse that = (SecretListResponse) o;
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,7 +22,7 @@ import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException;
import java.util.HashMap;
import java.io.Serializable;
import java.util.Map;
/**
@ -30,46 +30,20 @@ import java.util.Map;
*
* @author Stefan Kalscheuer
* @since 0.1
* @since 1.1 abstract
*/
@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 {
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;
}
}
public abstract class SecretResponse extends VaultDataResponse {
private static final long serialVersionUID = 5198088815871692951L;
/**
* Get complete data object.
*
* @return data map
* @since 0.4.0
* @since 1.1 Serializable map value.
*/
public final Map<String, Object> getData() {
if (data == null) {
return new HashMap<>();
}
return data;
}
public abstract Map<String, Serializable> getData();
/**
* Get secret metadata. This is only available for KV v2 secrets.
@ -77,9 +51,7 @@ public class SecretResponse extends VaultDataResponse {
* @return Metadata of the secret.
* @since 0.8
*/
public final VersionMetadata getMetadata() {
return metadata;
}
public abstract VersionMetadata getMetadata();
/**
* Get a single value for given key.
@ -89,60 +61,34 @@ public class SecretResponse extends VaultDataResponse {
* @since 0.4.0
*/
public final Object get(final String key) {
if (data == null) {
return null;
}
return getData().get(key);
}
/**
* Get data element for key "value".
* Method for backwards compatibility in case of simple secrets.
*
* @return the value
* @deprecated Deprecated artifact, will be removed at latest at v1.0.0
*/
@Deprecated
public final String getValue() {
Object value = get("value");
if (value == null) {
return null;
}
return value.toString();
}
/**
* Get response parsed as JSON.
*
* @param type Class to parse response
* @param <T> Class to parse response
* @return Parsed object
* @throws InvalidResponseException on parsing error
* @since 0.3
* @deprecated Deprecated artifact, will be removed at latest at v1.0.0
*/
@Deprecated
public final <T> T getValue(final Class<T> type) throws InvalidResponseException {
return get("value", type);
}
/**
* Get response parsed as JSON.
*
* @param key the key
* @param type Class to parse response
* @param <T> Class to parse response
* @param <C> Class to parse response
* @return Parsed object or {@code null} if absent
* @throws InvalidResponseException on parsing error
* @since 0.4.0
*/
public final <T> T get(final String key, final Class<T> type) throws InvalidResponseException {
public final <C> C get(final String key, final Class<C> type) throws InvalidResponseException {
try {
Object rawValue = get(key);
if (rawValue == null) {
return null;
} else if (type.isInstance(rawValue)) {
return type.cast(rawValue);
} else {
var om = new ObjectMapper();
if (rawValue instanceof String) {
return om.readValue((String) rawValue, type);
} else {
return om.readValue(om.writeValueAsString(rawValue), type);
}
}
return new ObjectMapper().readValue(rawValue.toString(), type);
} catch (IOException e) {
throw new InvalidResponseException("Unable to parse response payload: " + e.getMessage());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,34 +17,24 @@
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 com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.response.embedded.VersionMetadata;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
/**
* Vault response for a single secret version metatada, i.e. after update (KV v2).
* Vault response for a single secret version metadata, i.e. after update (KV v2).
*
* @author Stefan Kalscheuer
* @since 0.8
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretVersionResponse extends VaultDataResponse {
private static final long serialVersionUID = 2748635005258576174L;
@JsonProperty("data")
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.
*
@ -53,4 +43,20 @@ public class SecretVersionResponse extends VaultDataResponse {
public VersionMetadata getMetadata() {
return metadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
SecretVersionResponse that = (SecretVersionResponse) o;
return Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), metadata);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,12 +18,9 @@ package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;
/**
* Vault response from token lookup providing Token information in {@link TokenData} field.
@ -33,31 +30,41 @@ import java.util.Map;
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenResponse extends VaultDataResponse {
private static final long serialVersionUID = -4053126653764241197L;
@JsonProperty("data")
private TokenData data;
@JsonProperty("auth")
private Boolean auth;
/**
* Set data. Parses response data map to {@link TokenData}.
*
* @param data Raw response data
* @throws InvalidResponseException on parsing errors
*/
@Override
public void setData(final Map<String, Object> data) throws InvalidResponseException {
ObjectMapper mapper = new ObjectMapper();
try {
this.data = mapper.readValue(mapper.writeValueAsString(data), TokenData.class);
} catch (IOException e) {
throw new InvalidResponseException("Failed deserializing response", e);
}
}
/**
* @return Token data
*/
public TokenData getData() {
return data;
}
/**
* @return Auth data
*/
public Boolean getAuth() {
return auth;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
TokenResponse that = (TokenResponse) o;
return Objects.equals(data, that.data) && Objects.equals(auth, that.auth);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data, auth);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.model.TokenRole;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import java.util.Objects;
/**
* Vault response from token role lookup providing Token information in {@link TokenData} field.
*
* @author Stefan Kalscheuer
* @since 0.9
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenRoleResponse extends VaultDataResponse {
private static final long serialVersionUID = 5265363857731948626L;
@JsonProperty("data")
private TokenRole data;
/**
* @return TokenRole data
*/
public TokenRole getData() {
return data;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass() || !super.equals(o)) {
return false;
}
TokenRoleResponse that = (TokenRoleResponse) o;
return Objects.equals(data, that.data);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), data);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,10 +17,10 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.annotation.JsonProperty;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.response.embedded.WrapInfo;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Abstract Vault response with default payload fields.
@ -29,6 +29,11 @@ import java.util.Map;
* @since 0.1
*/
public abstract class VaultDataResponse implements VaultResponse {
private static final long serialVersionUID = 7486270767477652184L;
@JsonProperty("request_id")
private String requestId;
@JsonProperty("lease_id")
private String leaseId;
@ -41,14 +46,16 @@ public abstract class VaultDataResponse implements VaultResponse {
@JsonProperty("warnings")
private List<String> warnings;
@JsonProperty("wrap_info")
private WrapInfo wrapInfo;
/**
* Set data. To be implemented in the specific subclasses, as data can be of arbitrary structure.
*
* @param data Raw response data
* @throws InvalidResponseException on parsing errors
* @return Request ID
* @since 1.1
*/
@JsonProperty("data")
public abstract void setData(final Map<String, Object> data) throws InvalidResponseException;
public final String getRequestId() {
return requestId;
}
/**
* @return Lease ID
@ -77,4 +84,33 @@ public abstract class VaultDataResponse implements VaultResponse {
public final List<String> getWarnings() {
return warnings;
}
/**
* @return Wrapping information
* @since 1.1
*/
public final WrapInfo getWrapInfo() {
return wrapInfo;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
VaultDataResponse that = (VaultDataResponse) o;
return renewable == that.renewable &&
Objects.equals(requestId, that.requestId) &&
Objects.equals(leaseId, that.leaseId) &&
Objects.equals(leaseDuration, that.leaseDuration) &&
Objects.equals(warnings, that.warnings) &&
Objects.equals(wrapInfo, that.wrapInfo);
}
@Override
public int hashCode() {
return Objects.hash(requestId, leaseId, renewable, leaseDuration, warnings, wrapInfo);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,11 +16,14 @@
package de.stklcode.jvault.connector.model.response;
import java.io.Serializable;
/**
* Marker interface for responses from Vault backend.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
* @since 1.1 extends {@link Serializable}
*/
public interface VaultResponse {
public interface VaultResponse extends Serializable {
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,17 +19,22 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Embedded authorization information inside Vault response.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthData {
public final class AuthData implements Serializable {
private static final long serialVersionUID = -6962244199229885869L;
@JsonProperty("client_token")
private String clientToken;
@ -39,6 +44,9 @@ public final class AuthData {
@JsonProperty("policies")
private List<String> policies;
@JsonProperty("token_policies")
private List<String> tokenPolicies;
@JsonProperty("metadata")
private Map<String, Object> metadata;
@ -48,6 +56,15 @@ public final class AuthData {
@JsonProperty("renewable")
private boolean renewable;
@JsonProperty("entity_id")
private String entityId;
@JsonProperty("token_type")
private String tokenType;
@JsonProperty("orphan")
private boolean orphan;
/**
* @return Client token
*/
@ -56,10 +73,11 @@ public final class AuthData {
}
/**
* @return Token accessor
* @return Token type
* @since 0.9
*/
public String getAccessor() {
return accessor;
public String getTokenType() {
return tokenType;
}
/**
@ -69,6 +87,14 @@ public final class AuthData {
return policies;
}
/**
* @return List of policies associated with the token
* @since 0.9
*/
public List<String> getTokenPolicies() {
return tokenPolicies;
}
/**
* @return Metadata
*/
@ -89,4 +115,54 @@ public final class AuthData {
public boolean isRenewable() {
return renewable;
}
/**
* @return Entity ID
* @since 0.9
*/
public String getEntityId() {
return entityId;
}
/**
* @return Token accessor
*/
public String getAccessor() {
return accessor;
}
/**
* @return Token is orphan
* @since 0.9
*/
public boolean isOrphan() {
return orphan;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
AuthData authData = (AuthData) o;
return renewable == authData.renewable &&
orphan == authData.orphan &&
Objects.equals(clientToken, authData.clientToken) &&
Objects.equals(accessor, authData.accessor) &&
Objects.equals(policies, authData.policies) &&
Objects.equals(tokenPolicies, authData.tokenPolicies) &&
Objects.equals(metadata, authData.metadata) &&
Objects.equals(leaseDuration, authData.leaseDuration) &&
Objects.equals(entityId, authData.entityId) &&
Objects.equals(tokenType, authData.tokenType);
}
@Override
public int hashCode() {
return Objects.hash(clientToken, accessor, policies, tokenPolicies, metadata, leaseDuration, renewable,
entityId, tokenType, orphan);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,28 +21,45 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import de.stklcode.jvault.connector.model.AuthBackend;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* Embedded authentication method response.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class AuthMethod {
public final class AuthMethod implements Serializable {
private static final long serialVersionUID = -2718660627880077335L;
private AuthBackend type;
private String rawType;
@JsonProperty("accessor")
private String accessor;
@JsonProperty("description")
private String description;
@JsonProperty("config")
private Map<String, String> config;
@JsonProperty("external_entropy_access")
private boolean externalEntropyAccess;
@JsonProperty("local")
private boolean local;
@JsonProperty("seal_wrap")
private boolean sealWrap;
@JsonProperty("uuid")
private String uuid;
/**
* @param type Backend type, passed to {@link AuthBackend#forType(String)}
*/
@ -66,6 +83,14 @@ public final class AuthMethod {
return rawType;
}
/**
* @return Accessor
* @since 1.1
*/
public String getAccessor() {
return accessor;
}
/**
* @return Description
*/
@ -80,10 +105,58 @@ public final class AuthMethod {
return config;
}
/**
* @return Backend has access to external entropy source
* @since 1.1
*/
public boolean isExternalEntropyAccess() {
return externalEntropyAccess;
}
/**
* @return Is local backend
*/
public boolean isLocal() {
return local;
}
/**
* @return Seal wrapping enabled
* @since 1.1
*/
public boolean isSealWrap() {
return sealWrap;
}
/**
* @return Backend UUID
* @since 1.1
*/
public String getUuid() {
return uuid;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
AuthMethod that = (AuthMethod) o;
return local == that.local &&
type == that.type &&
externalEntropyAccess == that.externalEntropyAccess &&
sealWrap == that.sealWrap &&
Objects.equals(rawType, that.rawType) &&
Objects.equals(accessor, that.accessor) &&
Objects.equals(description, that.description) &&
Objects.equals(config, that.config) &&
Objects.equals(uuid, that.uuid);
}
@Override
public int hashCode() {
return Objects.hash(type, rawType, accessor, description, config, externalEntropyAccess, local, sealWrap, uuid);
}
}

View File

@ -0,0 +1,42 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* Wrapper object for secret key lists.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretListWrapper implements Serializable {
private static final long serialVersionUID = -8777605197063766125L;
@JsonProperty("keys")
private List<String> keys;
public List<String> getKeys() {
return keys;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SecretListWrapper that = (SecretListWrapper) o;
return Objects.equals(keys, that.keys);
}
@Override
public int hashCode() {
return Objects.hash(keys);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,20 +19,26 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Map;
import java.util.Objects;
/**
* Embedded metadata for Key-Value v2 secrets.
*
* @author Stefan Kalscheuer
* @since 0.8
* @author Stefan Kalscheuer
* @since 0.8
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class SecretMetadata {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
public final class SecretMetadata implements Serializable {
private static final long serialVersionUID = 1684891108903409038L;
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
@JsonProperty("created_time")
private String createdTimeString;
@ -124,4 +130,24 @@ public final class SecretMetadata {
return versions;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SecretMetadata that = (SecretMetadata) o;
return Objects.equals(createdTimeString, that.createdTimeString) &&
Objects.equals(currentVersion, that.currentVersion) &&
Objects.equals(maxVersions, that.maxVersions) &&
Objects.equals(oldestVersion, that.oldestVersion) &&
Objects.equals(updatedTime, that.updatedTime) &&
Objects.equals(versions, that.versions);
}
@Override
public int hashCode() {
return Objects.hash(createdTimeString, currentVersion, maxVersions, oldestVersion, updatedTime, versions);
}
}

View File

@ -0,0 +1,49 @@
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Map;
import java.util.Objects;
/**
* Wrapper object for secret data and metadata.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class SecretWrapper implements Serializable {
private static final long serialVersionUID = 8600413181758893378L;
@JsonProperty("data")
private Map<String, Serializable> data;
@JsonProperty("metadata")
private VersionMetadata metadata;
public Map<String, Serializable> getData() {
return data;
}
public VersionMetadata getMetadata() {
return metadata;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
SecretWrapper that = (SecretWrapper) o;
return Objects.equals(data, that.data) && Objects.equals(metadata, that.metadata);
}
@Override
public int hashCode() {
return Objects.hash(data, metadata);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,14 +19,23 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Embedded token information inside Vault response.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class TokenData {
public final class TokenData implements Serializable {
private static final long serialVersionUID = 2915180734313753649L;
@JsonProperty("accessor")
private String accessor;
@ -39,11 +48,23 @@ public final class TokenData {
@JsonProperty("display_name")
private String name;
@JsonProperty("entity_id")
private String entityId;
@JsonProperty("expire_time")
private String expireTime;
@JsonProperty("explicit_max_ttl")
private Integer explicitMaxTtl;
@JsonProperty("id")
private String id;
@JsonProperty("issue_time")
private String issueTime;
@JsonProperty("meta")
private String meta;
private Map<String, Object> meta;
@JsonProperty("num_uses")
private Integer numUses;
@ -54,12 +75,18 @@ public final class TokenData {
@JsonProperty("path")
private String path;
@JsonProperty("role")
private String role;
@JsonProperty("policies")
private List<String> policies;
@JsonProperty("renewable")
private boolean renewable;
@JsonProperty("ttl")
private Integer ttl;
@JsonProperty("type")
private String type;
/**
* @return Token accessor
*/
@ -88,6 +115,42 @@ public final class TokenData {
return name;
}
/**
* @return Entity ID
* @since 0.9
*/
public String getEntityId() {
return entityId;
}
/**
* @return Expire time as raw string value
* @since 0.9
*/
public String getExpireTimeString() {
return expireTime;
}
/**
* @return Expire time (parsed)
* @since 0.9
*/
public ZonedDateTime getExpireTime() {
if (expireTime == null) {
return null;
} else {
return ZonedDateTime.parse(expireTime);
}
}
/**
* @return Explicit maximum TTL
* @since 0.9
*/
public Integer getExplicitMaxTtl() {
return explicitMaxTtl;
}
/**
* @return Token ID
*/
@ -95,6 +158,34 @@ public final class TokenData {
return id;
}
/**
* @return Issue time as raw string value
* @since 0.9
*/
public String getIssueTimeString() {
return issueTime;
}
/**
* @return Expire time (parsed)
* @since 0.9
*/
public ZonedDateTime getIssueTime() {
if (issueTime == null) {
return null;
} else {
return ZonedDateTime.parse(issueTime);
}
}
/**
* @return Token type
* @since 0.9
*/
public String getType() {
return type;
}
/**
* @return Number of uses
*/
@ -117,10 +208,19 @@ public final class TokenData {
}
/**
* @return Token role
* @return Token policies
* @since 0.9
*/
public String getRole() {
return role;
public List<String> getPolicies() {
return policies;
}
/**
* @return Token is renewable
* @since 0.9
*/
public boolean isRenewable() {
return renewable;
}
/**
@ -133,7 +233,40 @@ public final class TokenData {
/**
* @return Metadata
*/
public String getMeta() {
public Map<String, Object> getMeta() {
return meta;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
TokenData tokenData = (TokenData) o;
return orphan == tokenData.orphan &&
renewable == tokenData.renewable &&
Objects.equals(accessor, tokenData.accessor) &&
Objects.equals(creationTime, tokenData.creationTime) &&
Objects.equals(creationTtl, tokenData.creationTtl) &&
Objects.equals(name, tokenData.name) &&
Objects.equals(entityId, tokenData.entityId) &&
Objects.equals(expireTime, tokenData.expireTime) &&
Objects.equals(explicitMaxTtl, tokenData.explicitMaxTtl) &&
Objects.equals(id, tokenData.id) &&
Objects.equals(issueTime, tokenData.issueTime) &&
Objects.equals(meta, tokenData.meta) &&
Objects.equals(numUses, tokenData.numUses) &&
Objects.equals(path, tokenData.path) &&
Objects.equals(policies, tokenData.policies) &&
Objects.equals(ttl, tokenData.ttl) &&
Objects.equals(type, tokenData.type);
}
@Override
public int hashCode() {
return Objects.hash(accessor, creationTime, creationTtl, name, entityId, expireTime, explicitMaxTtl, id,
issueTime, meta, numUses, orphan, path, policies, renewable, ttl, type);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,19 +19,25 @@ package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Objects;
/**
* Embedded metadata for a single Key-Value v2 version.
*
* @author Stefan Kalscheuer
* @since 0.8
* @author Stefan Kalscheuer
* @since 0.8
* @since 1.1 implements {@link Serializable}
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public final class VersionMetadata {
private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSX");
public final class VersionMetadata implements Serializable {
private static final long serialVersionUID = -5286693953873839611L;
private static final DateTimeFormatter TIME_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSXXX");
@JsonProperty("created_time")
private String createdTimeString;
@ -103,4 +109,22 @@ public final class VersionMetadata {
return version;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
VersionMetadata that = (VersionMetadata) o;
return destroyed == that.destroyed &&
Objects.equals(createdTimeString, that.createdTimeString) &&
Objects.equals(deletionTimeString, that.deletionTimeString) &&
Objects.equals(version, that.version);
}
@Override
public int hashCode() {
return Objects.hash(createdTimeString, deletionTimeString, destroyed, version);
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response.embedded;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Objects;
/**
* Wrapping information object.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
public class WrapInfo implements Serializable {
private static final long serialVersionUID = -7764500642913116581L;
@JsonProperty("token")
private String token;
@JsonProperty("ttl")
private Integer ttl;
@JsonProperty("creation_time")
private String creationTime;
@JsonProperty("creation_path")
private String creationPath;
/**
* @return Token
*/
public String getToken() {
return token;
}
/**
* @return TTL (in seconds)
*/
public Integer getTtl() {
return ttl;
}
/**
* @return Creation time
*/
public String getCreationTime() {
return creationTime;
}
/**
* @return Creation path
*/
public String getCreationPath() {
return creationPath;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
WrapInfo that = (WrapInfo) o;
return Objects.equals(token, that.token) &&
Objects.equals(ttl, that.ttl) &&
Objects.equals(creationTime, that.creationTime) &&
Objects.equals(creationPath, that.creationPath);
}
@Override
public int hashCode() {
return Objects.hash(token, ttl, creationTime, creationPath);
}
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,36 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* JVaultConnector module.
*
* @author Stefan Kalscheuer
*/
module de.stklcode.jvault.connector {
exports de.stklcode.jvault.connector;
exports de.stklcode.jvault.connector.exception;
exports de.stklcode.jvault.connector.model;
exports de.stklcode.jvault.connector.model.response;
exports de.stklcode.jvault.connector.model.response.embedded;
opens de.stklcode.jvault.connector.model to com.fasterxml.jackson.databind;
opens de.stklcode.jvault.connector.model.response to com.fasterxml.jackson.databind;
opens de.stklcode.jvault.connector.model.response.embedded to com.fasterxml.jackson.databind;
requires java.base;
requires java.net.http;
requires com.fasterxml.jackson.databind;
}

View File

@ -0,0 +1,189 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector;
import com.github.stefanbirkner.systemlambda.SystemLambda;
import de.stklcode.jvault.connector.exception.ConnectionException;
import de.stklcode.jvault.connector.exception.TlsException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.nio.file.NoSuchFileException;
import static com.github.stefanbirkner.systemlambda.SystemLambda.withEnvironmentVariable;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit test for HTTP Vault connector factory
*
* @author Stefan Kalscheuer
* @since 0.8.0
*/
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;
/**
* Test the builder.
*/
@Test
void builderTest() throws Exception {
// Minimal configuration.
HTTPVaultConnector connector = HTTPVaultConnector.builder().withHost("vault.example.com").build();
assertEquals("https://vault.example.com:8200/v1/", getRequestHelperPrivate(connector, "baseURL"), "URL not set correctly");
assertNull(getRequestHelperPrivate(connector, "trustedCaCert"), "Trusted CA cert set when no cert provided");
assertEquals(0, getRequestHelperPrivate(connector, "retries"), "Number of retries unexpectedly set");
// Specify all options.
HTTPVaultConnectorBuilder builder = HTTPVaultConnector.builder()
.withHost("vault2.example.com")
.withoutTLS()
.withPort(1234)
.withPrefix("/foo/")
.withTimeout(5678)
.withNumberOfRetries(9);
connector = builder.build();
assertEquals("http://vault2.example.com:1234/foo/", getRequestHelperPrivate(connector, "baseURL"), "URL not set correctly");
assertNull(getRequestHelperPrivate(connector, "trustedCaCert"), "Trusted CA cert set when no cert provided");
assertEquals(9, getRequestHelperPrivate(connector, "retries"), "Unexpected number of retries");
assertEquals(5678, getRequestHelperPrivate(connector, "timeout"), "Number timeout value");
assertThrows(ConnectionException.class, builder::buildAndAuth, "Immediate authentication should throw exception without token");
// Initialization from URL.
assertThrows(
URISyntaxException.class,
() -> HTTPVaultConnector.builder().withBaseURL("foo:/\\1nv4l1d_UrL"),
"Initialization from invalid URL should fail"
);
connector = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().withBaseURL("https://vault3.example.com:5678/bar/").build(),
"Initialization from valid URL should not fail"
);
assertEquals("https://vault3.example.com:5678/bar/", getRequestHelperPrivate(connector, "baseURL"), "URL not set correctly");
// Port numbers.
assertThrows(IllegalArgumentException.class, () -> HTTPVaultConnector.builder().withPort(65536), "Too large port number should throw an exception");
assertThrows(IllegalArgumentException.class, () -> HTTPVaultConnector.builder().withPort(0), "Port number 0 should throw an exception");
builder = assertDoesNotThrow(() -> HTTPVaultConnector.builder().withPort(-1), "Port number -1 should not throw an exception");
assertNull(builder.getPort(), "Port number -1 should be omitted");
builder = assertDoesNotThrow(() -> HTTPVaultConnector.builder().withPort(null), "Port number NULL should not throw an exception");
assertNull(builder.getPort(), "Port number NULL should be passed through");
}
/**
* Test building from environment variables
*/
@Test
void testFromEnv() throws Exception {
// Provide address only should be enough.
withVaultEnv(VAULT_ADDR, null, null, null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Factory creation from minimal environment failed"
);
HTTPVaultConnector connector = builder.build();
assertEquals(VAULT_ADDR + "/v1/", getRequestHelperPrivate(connector, "baseURL"), "URL not set correctly");
assertNull(getRequestHelperPrivate(connector, "trustedCaCert"), "Trusted CA cert set when no cert provided");
assertEquals(0, getRequestHelperPrivate(connector, "retries"), "Non-default number of retries, when none set");
return null;
});
// Provide address and number of retries.
withVaultEnv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Factory creation from environment failed"
);
HTTPVaultConnector connector = builder.build();
assertEquals(VAULT_ADDR + "/v1/", getRequestHelperPrivate(connector, "baseURL"), "URL not set correctly");
assertNull(getRequestHelperPrivate(connector, "trustedCaCert"), "Trusted CA cert set when no cert provided");
assertEquals(VAULT_MAX_RETRIES, getRequestHelperPrivate(connector, "retries"), "Number of retries not set correctly");
return null;
});
// Provide CA certificate.
String VAULT_CACERT = tempDir.toString() + "/doesnotexist";
withVaultEnv(VAULT_ADDR, VAULT_CACERT, VAULT_MAX_RETRIES.toString(), null).execute(() -> {
TlsException e = assertThrows(
TlsException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Creation with unknown cert path failed"
);
assertTrue(e.getCause() instanceof NoSuchFileException);
assertEquals(VAULT_CACERT, ((NoSuchFileException) e.getCause()).getFile());
return null;
});
// Automatic authentication.
withVaultEnv(VAULT_ADDR, null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN).execute(() -> {
HTTPVaultConnectorBuilder builder = assertDoesNotThrow(
() -> HTTPVaultConnector.builder().fromEnv(),
"Factory creation from minimal environment failed"
);
assertEquals(VAULT_TOKEN, getPrivate(builder, "token"), "Token not set correctly");
return null;
});
// Invalid URL.
withVaultEnv("This is not a valid URL!", null, VAULT_MAX_RETRIES.toString(), VAULT_TOKEN).execute(() -> {
assertThrows(
ConnectionException.class,
() -> HTTPVaultConnector.builder().fromEnv(),
"Invalid URL from environment should raise an exception"
);
return null;
});
}
private SystemLambda.WithEnvironmentVariables withVaultEnv(String vault_addr, String vault_cacert, String vault_max_retries, String vault_token) {
return withEnvironmentVariable("VAULT_ADDR", vault_addr)
.and("VAULT_CACERT", vault_cacert)
.and("VAULT_MAX_RETRIES", vault_max_retries)
.and("VAULT_TOKEN", vault_token);
}
private Object getRequestHelperPrivate(HTTPVaultConnector connector, String fieldName) throws NoSuchFieldException, IllegalAccessException {
return getPrivate(getPrivate(connector, "request"), fieldName);
}
private Object getPrivate(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Field field = target.getClass().getDeclaredField(fieldName);
if (field.canAccess(target)) {
return field.get(target);
}
field.setAccessible(true);
Object value = field.get(target);
field.setAccessible(false);
return value;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,504 +0,0 @@
/*
* 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

@ -1,135 +0,0 @@
/*
* 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 String VAULT_ADDR = "https://localhost:8201";
private static Integer VAULT_MAX_RETRIES = 13;
private static String VAULT_TOKEN = "00001111-2222-3333-4444-555566667777";
@TempDir
File tempDir;
@Rule
public final EnvironmentVariables environment = new EnvironmentVariables();
/**
* Test building from environment variables
*/
@Test
public 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-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,10 +18,8 @@ package de.stklcode.jvault.connector.exception;
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.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
* Common JUnit test for Exceptions extending {@link VaultConnectorException}.
@ -29,19 +27,19 @@ import static org.hamcrest.core.Is.is;
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class VaultConnectorExceptionTest {
class VaultConnectorExceptionTest {
private static final String MSG = "This is a test exception!";
private static final Throwable CAUSE = new Exception("Test-Cause");
private static final Integer STATUS_CODE = 1337;
private static final String RESPONSE = "Dummy response";
@Test
public void authorizationRequiredExceptionTest() {
void authorizationRequiredExceptionTest() {
assertEmptyConstructor(new AuthorizationRequiredException());
}
@Test
public void connectionExceptionTest() {
void connectionExceptionTest() {
assertEmptyConstructor(new ConnectionException());
assertMsgConstructor(new ConnectionException(MSG));
assertCauseConstructor(new ConnectionException(CAUSE));
@ -49,7 +47,7 @@ public class VaultConnectorExceptionTest {
}
@Test
public void invalidRequestExceptionTest() {
void invalidRequestExceptionTest() {
assertEmptyConstructor(new InvalidRequestException());
assertMsgConstructor(new InvalidRequestException(MSG));
assertCauseConstructor(new InvalidRequestException(CAUSE));
@ -57,7 +55,7 @@ public class VaultConnectorExceptionTest {
}
@Test
public void invalidResponseExceptionTest() {
void invalidResponseExceptionTest() {
assertEmptyConstructor(new InvalidResponseException());
assertMsgConstructor(new InvalidResponseException(MSG));
assertCauseConstructor(new InvalidResponseException(CAUSE));
@ -65,42 +63,39 @@ public class VaultConnectorExceptionTest {
// Constructor with message and status code.
InvalidResponseException e = new InvalidResponseException(MSG, STATUS_CODE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(nullValue()));
assertEquals(MSG, e.getMessage());
assertNull(e.getCause());
assertEquals(STATUS_CODE, e.getStatusCode());
assertNull(e.getResponse());
// Constructor with message, status code and cause.
e = new InvalidResponseException(MSG, STATUS_CODE, CAUSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(nullValue()));
assertEquals(MSG, e.getMessage());
assertEquals(CAUSE, e.getCause());
assertEquals(STATUS_CODE, e.getStatusCode());
assertNull(e.getResponse());
// Constructor with message, status code and response.
e = new InvalidResponseException(MSG, STATUS_CODE, RESPONSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(RESPONSE));
assertEquals(MSG, e.getMessage());
assertNull(e.getCause());
assertEquals(STATUS_CODE, e.getStatusCode());
assertEquals(RESPONSE, e.getResponse());
// Constructor with message, status code, response and cause.
e = new InvalidResponseException(MSG, STATUS_CODE, RESPONSE, CAUSE);
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
assertThat(e.getStatusCode(), is(STATUS_CODE));
assertThat(e.getResponse(), is(RESPONSE));
assertEquals(MSG, e.getMessage());
assertEquals(CAUSE, e.getCause());
assertEquals(STATUS_CODE, e.getStatusCode());
assertEquals(RESPONSE, e.getResponse());
}
@Test
public void permissionDeniedExceptionTest() {
void permissionDeniedExceptionTest() {
// Default message overwritten.
PermissionDeniedException e = new PermissionDeniedException();
assertThat(e, is(instanceOf(VaultConnectorException.class)));
assertThat(e, is(instanceOf(Exception.class)));
assertThat(e, is(instanceOf(Throwable.class)));
assertThat(e.getMessage(), is("Permission denied"));
assertThat(e.getCause(), is(nullValue()));
assertEquals("Permission denied", e.getMessage());
assertNull(e.getCause());
assertMsgConstructor(new PermissionDeniedException(MSG));
assertCauseConstructor(new PermissionDeniedException(CAUSE));
@ -108,7 +103,7 @@ public class VaultConnectorExceptionTest {
}
@Test
public void tlsExceptionTest() {
void tlsExceptionTest() {
assertEmptyConstructor(new TlsException());
assertMsgConstructor(new TlsException(MSG));
assertCauseConstructor(new TlsException(CAUSE));
@ -121,11 +116,8 @@ public class VaultConnectorExceptionTest {
* @param e the exception
*/
private void assertEmptyConstructor(VaultConnectorException e) {
assertThat(e, is(instanceOf(VaultConnectorException.class)));
assertThat(e, is(instanceOf(Exception.class)));
assertThat(e, is(instanceOf(Throwable.class)));
assertThat(e.getMessage(), is(nullValue()));
assertThat(e.getCause(), is(nullValue()));
assertNull(e.getMessage());
assertNull(e.getCause());
}
/**
@ -134,8 +126,8 @@ public class VaultConnectorExceptionTest {
* @param e the exception
*/
private void assertMsgConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(nullValue()));
assertEquals(MSG, e.getMessage());
assertNull(e.getCause());
}
/**
@ -144,8 +136,8 @@ public class VaultConnectorExceptionTest {
* @param e the exception
*/
private void assertCauseConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(CAUSE.toString()));
assertThat(e.getCause(), is(CAUSE));
assertEquals(CAUSE.toString(), e.getMessage());
assertEquals(CAUSE, e.getCause());
}
/**
@ -154,7 +146,7 @@ public class VaultConnectorExceptionTest {
* @param e the exception
*/
private void assertMsgCauseConstructor(VaultConnectorException e) {
assertThat(e.getMessage(), is(MSG));
assertThat(e.getCause(), is(CAUSE));
assertEquals(MSG, e.getMessage());
assertEquals(CAUSE, e.getCause());
}
}

View File

@ -1,135 +0,0 @@
/*
* 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.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.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.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";
@TempDir
File tempDir;
@Rule
public final EnvironmentVariables environment = new EnvironmentVariables();
/**
* Test building from environment variables
*/
@Test
public void testFromEnv() throws NoSuchFieldException, IllegalAccessException, IOException {
/* Provide address only should be enough */
setenv(VAULT_ADDR, null, null, null);
HTTPVaultConnectorFactory factory = null;
HTTPVaultConnector connector;
try {
factory = VaultConnectorFactory.httpFactory().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 = VaultConnectorFactory.httpFactory().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 {
VaultConnectorFactory.httpFactory().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 = VaultConnectorFactory.httpFactory().fromEnv();
} catch (VaultConnectorException e) {
fail("Factory creation from minimal environment failed");
}
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) {
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

@ -0,0 +1,73 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.databind.ObjectMapper;
import nl.jqno.equalsverifier.EqualsVerifier;
import org.junit.jupiter.api.Test;
import java.io.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
/**
* Abstract testcase for model classes.
*
* @author Stefan Kalscheuer
* @since 1.1
*/
public abstract class AbstractModelTest<T> {
protected final Class<?> modelClass;
protected final ObjectMapper objectMapper;
/**
* Test case constructor.
*
* @param modelClass Target class to test.
*/
protected AbstractModelTest(Class<T> modelClass) {
this.modelClass = modelClass;
this.objectMapper = new ObjectMapper();
}
/**
* Create a "full" model instance.
*
* @return Model instance.
*/
protected abstract T createFull();
/**
* Test if {@link Object#equals(Object)} and {@link Object#hashCode()} are implemented, s.t. all fields are covered.
*/
@Test
void testEqualsHashcode() {
EqualsVerifier.simple().forClass(modelClass).verify();
}
/**
* Test Java serialization of a full model instance.
* Serialization and deserialization must not fail and the resulting object should equal the original object.
*/
@Test
void serializationTest() {
T original = createFull();
byte[] bytes;
try (var bos = new ByteArrayOutputStream();
var oos = new ObjectOutputStream(bos)) {
oos.writeObject(original);
bytes = bos.toByteArray();
} catch (IOException e) {
fail("Serialization failed", e);
return;
}
try (var bis = new ByteArrayInputStream(bytes);
var ois = new ObjectInputStream(bis)) {
Object copy = ois.readObject();
assertEquals(modelClass, copy.getClass(), "Invalid class after deserialization");
assertEquals(original, copy, "Deserialized object should be equal to the original");
} catch (IOException | ClassNotFoundException e) {
fail("Deserialization failed", e);
}
}
}

View File

@ -1,156 +0,0 @@
/*
* 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;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
/**
* JUnit Test for AppRole Builder.
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public class AppRoleBuilderTest {
private static final String NAME = "TestRole";
private static final String ID = "test-id";
private static final Boolean BIND_SECRET_ID = true;
private static final List<String> BOUND_CIDR_LIST = new ArrayList<>();
private static final String CIDR_1 = "192.168.1.0/24";
private static final String CIDR_2 = "172.16.0.0/16";
private static final List<String> POLICIES = new ArrayList<>();
private static final String POLICY = "policy";
private static final String POLICY_2 = "policy2";
private static final Integer SECRET_ID_NUM_USES = 10;
private static final Integer SECRET_ID_TTL = 7200;
private static final Integer TOKEN_TTL = 4800;
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\",\"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);
@BeforeAll
public static void init() {
BOUND_CIDR_LIST.add(CIDR_1);
POLICIES.add(POLICY);
}
/**
* Build role with only a name.
*/
@Test
public void buildDefaultTest() throws JsonProcessingException {
AppRole role = new AppRoleBuilder(NAME).build();
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()));
assertThat(role.getTokenTtl(), is(nullValue()));
assertThat(role.getTokenMaxTtl(), is(nullValue()));
assertThat(role.getPeriod(), is(nullValue()));
/* optional fields should be ignored, so JSON string should only contain role_name */
assertThat(new ObjectMapper().writeValueAsString(role), is(JSON_MIN));
}
/**
* Build token without all parameters set.
*/
@Test
public void buildFullTest() throws JsonProcessingException {
AppRole role = new AppRoleBuilder(NAME)
.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)
.withTokenTtl(TOKEN_TTL)
.withTokenMaxTtl(TOKEN_MAX_TTL)
.withPeriod(PERIOD)
.build();
assertThat(role.getName(), is(NAME));
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));
assertThat(role.getTokenTtl(), is(TOKEN_TTL));
assertThat(role.getTokenMaxTtl(), is(TOKEN_MAX_TTL));
assertThat(role.getPeriod(), is(PERIOD));
/* Verify that all parameters are included in JSON string */
assertThat(new ObjectMapper().writeValueAsString(role), is(JSON_FULL));
}
/**
* Test convenience methods
*/
@Test
public void convenienceMethodsTest() {
/* bind_secret_id */
AppRole role = new AppRoleBuilder(NAME).build();
assertThat(role.getBindSecretId(), is(nullValue()));
role = new AppRoleBuilder(NAME).withBindSecretID().build();
assertThat(role.getBindSecretId(), is(true));
role = new AppRoleBuilder(NAME).withoutBindSecretID().build();
assertThat(role.getBindSecretId(), is(false));
/* Add single CIDR subnet */
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)
.withSecretIdBoundCidrs(BOUND_CIDR_LIST)
.withCidrBlock(CIDR_2)
.build();
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();
assertThat(role.getPolicies(), hasSize(1));
assertThat(role.getPolicies(), contains(POLICY_2));
role = new AppRoleBuilder(NAME)
.withPolicies(POLICIES)
.withPolicy(POLICY_2)
.build();
assertThat(role.getPolicies(), hasSize(2));
assertThat(role.getPolicies(), contains(POLICY, POLICY_2));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,21 +16,14 @@
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.lang.reflect.Field;
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.hamcrest.junit.MatcherAssume.assumeThat;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
/**
@ -39,161 +32,143 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.5.0
*/
public class AppRoleSecretTest {
class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
private static final String TEST_ID = "abc123";
private static final Map<String, Object> TEST_META = new HashMap<>();
private static final List<String> TEST_CIDR = Arrays.asList("203.0.113.0/24", "198.51.100.0/24");
private static final Map<String, Object> TEST_META = Map.of(
"foo", "bar",
"number", 1337
);
private static final List<String> TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24");
static {
TEST_META.put("foo", "bar");
TEST_META.put("number", 1337);
AppRoleSecretTest() {
super(AppRoleSecret.class);
}
@Override
protected AppRoleSecret createFull() {
return new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
}
/**
* Test constructors.
*/
@Test
public void constructorTest() {
/* Empty constructor */
void constructorTest() {
// Empty constructor.
AppRoleSecret secret = new AppRoleSecret();
assertThat(secret.getId(), is(nullValue()));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(nullValue()));
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
assertNull(secret.getId());
assertNull(secret.getAccessor());
assertNull(secret.getMetadata());
assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString());
assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime());
assertNull(secret.getNumUses());
assertNull(secret.getTtl());
/* Constructor with ID */
// Constructor with ID.
secret = new AppRoleSecret(TEST_ID);
assertThat(secret.getId(), is(TEST_ID));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(nullValue()));
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
assertEquals(TEST_ID, secret.getId());
assertNull(secret.getAccessor());
assertNull(secret.getMetadata());
assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString());
assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime());
assertNull(secret.getNumUses());
assertNull(secret.getTtl());
/* Constructor with Metadata and CIDR bindings */
// Constructor with Metadata and CIDR bindings.
secret = new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
assertThat(secret.getId(), is(TEST_ID));
assertThat(secret.getAccessor(), is(nullValue()));
assertThat(secret.getMetadata(), is(TEST_META));
assertThat(secret.getCidrList(), is(TEST_CIDR));
assertThat(secret.getCidrListString(), is(String.join(",", TEST_CIDR)));
assertThat(secret.getCreationTime(), is(nullValue()));
assertThat(secret.getExpirationTime(), is(nullValue()));
assertThat(secret.getLastUpdatedTime(), is(nullValue()));
assertThat(secret.getNumUses(), is(nullValue()));
assertThat(secret.getTtl(), is(nullValue()));
assertEquals(TEST_ID, secret.getId());
assertNull(secret.getAccessor());
assertEquals(TEST_META, secret.getMetadata());
assertEquals(TEST_CIDR, secret.getCidrList());
assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString());
assertNull(secret.getCreationTime());
assertNull(secret.getExpirationTime());
assertNull(secret.getLastUpdatedTime());
assertNull(secret.getNumUses());
assertNull(secret.getTtl());
}
/**
* Test setter.
*/
@Test
public void setterTest() {
void setterTest() {
AppRoleSecret secret = new AppRoleSecret(TEST_ID);
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString());
secret.setCidrList(TEST_CIDR);
assertThat(secret.getCidrList(), is(TEST_CIDR));
assertThat(secret.getCidrListString(), is(String.join(",", TEST_CIDR)));
assertEquals(TEST_CIDR, secret.getCidrList());
assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString());
secret.setCidrList(null);
assertThat(secret.getCidrList(), is(nullValue()));
assertThat(secret.getCidrListString(), is(emptyString()));
assertNull(secret.getCidrList());
assertEquals("", secret.getCidrListString());
}
/**
* Test JSON (de)serialization.
*/
@Test
public void jsonTest() throws NoSuchFieldException, IllegalAccessException {
ObjectMapper mapper = new ObjectMapper();
/* A simple roundtrip first. All set fields should be present afterwards. */
void jsonTest() throws NoSuchFieldException, IllegalAccessException {
// A simple roundtrip first. All set fields should be present afterwards..
AppRoleSecret secret = new AppRoleSecret(TEST_ID, TEST_META, TEST_CIDR);
String secretJson = "";
try {
secretJson = mapper.writeValueAsString(secret);
} catch (JsonProcessingException e) {
e.printStackTrace();
fail("Serialization failed");
}
/* CIDR list is comma-separated when used as input, but List otherwise, hence convert string to list */
secretJson = commaSeparatedToList(secretJson);
String secretJson = assertDoesNotThrow(() -> objectMapper.writeValueAsString(secret), "Serialization failed");
// CIDR list is comma-separated when used as input, but List otherwise, hence convert string to list.
String secretJson2 = commaSeparatedToList(secretJson);
AppRoleSecret secret2;
try {
secret2 = mapper.readValue(secretJson, AppRoleSecret.class);
assertThat(secret.getId(), is(secret2.getId()));
assertThat(secret.getMetadata(), is(secret2.getMetadata()));
assertThat(secret.getCidrList(), is(secret2.getCidrList()));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
AppRoleSecret secret2 = assertDoesNotThrow(
() -> objectMapper.readValue(secretJson2, AppRoleSecret.class),
"Deserialization failed"
);
assertEquals(secret2.getId(), secret.getId());
assertEquals(secret2.getMetadata(), secret.getMetadata());
assertEquals(secret2.getCidrList(), secret.getCidrList());
/* Test fields, that should not be written to JSON */
// Test fields, that should not be written to JSON.
setPrivateField(secret, "accessor", "TEST_ACCESSOR");
assumeThat(secret.getAccessor(), is("TEST_ACCESSOR"));
assumeTrue("TEST_ACCESSOR".equals(secret.getAccessor()));
setPrivateField(secret, "creationTime", "TEST_CREATION");
assumeThat(secret.getCreationTime(), is("TEST_CREATION"));
assumeTrue("TEST_CREATION".equals(secret.getCreationTime()));
setPrivateField(secret, "expirationTime", "TEST_EXPIRATION");
assumeThat(secret.getExpirationTime(), is("TEST_EXPIRATION"));
assumeTrue("TEST_EXPIRATION".equals(secret.getExpirationTime()));
setPrivateField(secret, "lastUpdatedTime", "TEST_UPDATETIME");
assumeThat(secret.getLastUpdatedTime(), is("TEST_UPDATETIME"));
assumeTrue("TEST_UPDATETIME".equals(secret.getLastUpdatedTime()));
setPrivateField(secret, "numUses", 678);
assumeThat(secret.getNumUses(), is(678));
assumeTrue(secret.getNumUses() == 678);
setPrivateField(secret, "ttl", 12345);
assumeThat(secret.getTtl(), is(12345));
try {
secretJson = mapper.writeValueAsString(secret);
} catch (JsonProcessingException e) {
e.printStackTrace();
fail("Serialization failed");
}
try {
secret2 = mapper.readValue(commaSeparatedToList(secretJson), AppRoleSecret.class);
assertThat(secret.getId(), is(secret2.getId()));
assertThat(secret.getMetadata(), is(secret2.getMetadata()));
assertThat(secret.getCidrList(), is(secret2.getCidrList()));
assertThat(secret2.getAccessor(), is(nullValue()));
assertThat(secret2.getCreationTime(), is(nullValue()));
assertThat(secret2.getExpirationTime(), is(nullValue()));
assertThat(secret2.getLastUpdatedTime(), is(nullValue()));
assertThat(secret2.getNumUses(), is(nullValue()));
assertThat(secret2.getTtl(), is(nullValue()));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
assumeTrue(secret.getTtl() == 12345);
String secretJson3 = assertDoesNotThrow(() -> objectMapper.writeValueAsString(secret), "Serialization failed");
secret2 = assertDoesNotThrow(
() -> objectMapper.readValue(commaSeparatedToList(secretJson3), AppRoleSecret.class),
"Deserialization failed"
);
assertEquals(secret2.getId(), secret.getId());
assertEquals(secret2.getMetadata(), secret.getMetadata());
assertEquals(secret2.getCidrList(), secret.getCidrList());
assertNull(secret2.getAccessor());
assertNull(secret2.getCreationTime());
assertNull(secret2.getExpirationTime());
assertNull(secret2.getLastUpdatedTime());
assertNull(secret2.getNumUses());
assertNull(secret2.getTtl());
/* Those fields should be deserialized from JSON though */
secretJson = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
// Those fields should be deserialized from JSON though.
String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
"\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}";
try {
secret2 = mapper.readValue(secretJson, AppRoleSecret.class);
assertThat(secret2.getAccessor(), is("TEST_ACCESSOR"));
assertThat(secret2.getCreationTime(), is("TEST_CREATION"));
assertThat(secret2.getExpirationTime(), is("TEST_EXPIRATION"));
assertThat(secret2.getLastUpdatedTime(), is("TEST_LASTUPDATE"));
assertThat(secret2.getNumUses(), is(678));
assertThat(secret2.getTtl(), is(12345));
} catch (IOException e) {
e.printStackTrace();
fail("Deserialization failed");
}
secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed");
assertEquals("TEST_ACCESSOR", secret2.getAccessor());
assertEquals("TEST_CREATION", secret2.getCreationTime());
assertEquals("TEST_EXPIRATION", secret2.getExpirationTime());
assertEquals("TEST_LASTUPDATE", secret2.getLastUpdatedTime());
assertEquals(678, secret2.getNumUses());
assertEquals(12345, secret2.getTtl());
}
private static void setPrivateField(Object object, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException {
@ -208,5 +183,4 @@ public class AppRoleSecretTest {
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":\\[$1\\]")
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
}
}

View File

@ -0,0 +1,183 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link AppRole} and {@link AppRole.Builder}.
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
class AppRoleTest extends AbstractModelTest<AppRole> {
private static final String NAME = "TestRole";
private static final String ID = "test-id";
private static final Boolean BIND_SECRET_ID = true;
private static final List<String> BOUND_CIDR_LIST = new ArrayList<>();
private static final String CIDR_1 = "192.168.1.0/24";
private static final String CIDR_2 = "172.16.0.0/16";
private static final List<String> POLICIES = new ArrayList<>();
private static final String POLICY = "policy";
private static final String POLICY_2 = "policy2";
private static final Integer SECRET_ID_NUM_USES = 10;
private static final Integer SECRET_ID_TTL = 7200;
private static final Boolean ENABLE_LOCAL_SECRET_IDS = false;
private static final Integer TOKEN_TTL = 4800;
private static final Integer TOKEN_MAX_TTL = 9600;
private static final Integer TOKEN_EXPLICIT_MAX_TTL = 14400;
private static final Boolean TOKEN_NO_DEFAULT_POLICY = false;
private static final Integer TOKEN_NUM_USES = 42;
private static final Integer TOKEN_PERIOD = 1234;
private static final Token.Type TOKEN_TYPE = Token.Type.DEFAULT_SERVICE;
private static final String JSON_MIN = "{\"role_name\":\"" + NAME + "\"}";
private static final String JSON_FULL = String.format("{\"role_name\":\"%s\",\"role_id\":\"%s\",\"bind_secret_id\":%s,\"secret_id_bound_cidrs\":\"%s\",\"secret_id_num_uses\":%d,\"secret_id_ttl\":%d,\"enable_local_secret_ids\":%s,\"token_ttl\":%d,\"token_max_ttl\":%d,\"token_policies\":\"%s\",\"token_bound_cidrs\":\"%s\",\"token_explicit_max_ttl\":%d,\"token_no_default_policy\":%s,\"token_num_uses\":%d,\"token_period\":%d,\"token_type\":\"%s\"}",
NAME, ID, BIND_SECRET_ID, CIDR_1, SECRET_ID_NUM_USES, SECRET_ID_TTL, ENABLE_LOCAL_SECRET_IDS, TOKEN_TTL, TOKEN_MAX_TTL, POLICY, CIDR_1, TOKEN_EXPLICIT_MAX_TTL, TOKEN_NO_DEFAULT_POLICY, TOKEN_NUM_USES, TOKEN_PERIOD, TOKEN_TYPE.value());
AppRoleTest() {
super(AppRole.class);
}
@Override
protected AppRole createFull() {
return AppRole.builder(NAME)
.withId(ID)
.withBindSecretID(BIND_SECRET_ID)
.withSecretIdBoundCidrs(BOUND_CIDR_LIST)
.withTokenPolicies(POLICIES)
.withSecretIdNumUses(SECRET_ID_NUM_USES)
.withSecretIdTtl(SECRET_ID_TTL)
.withEnableLocalSecretIds(ENABLE_LOCAL_SECRET_IDS)
.withTokenTtl(TOKEN_TTL)
.withTokenMaxTtl(TOKEN_MAX_TTL)
.withTokenBoundCidrs(BOUND_CIDR_LIST)
.withTokenExplicitMaxTtl(TOKEN_EXPLICIT_MAX_TTL)
.withTokenNoDefaultPolicy(TOKEN_NO_DEFAULT_POLICY)
.withTokenNumUses(TOKEN_NUM_USES)
.withTokenPeriod(TOKEN_PERIOD)
.withTokenType(TOKEN_TYPE)
.build();
}
@BeforeAll
static void init() {
BOUND_CIDR_LIST.add(CIDR_1);
POLICIES.add(POLICY);
}
/**
* Build role with only a name.
*/
@Test
void buildDefaultTest() throws JsonProcessingException {
AppRole role = AppRole.builder(NAME).build();
assertNull(role.getId());
assertNull(role.getBindSecretId());
assertNull(role.getSecretIdBoundCidrs());
assertNull(role.getTokenPolicies());
assertNull(role.getSecretIdNumUses());
assertNull(role.getSecretIdTtl());
assertNull(role.getEnableLocalSecretIds());
assertNull(role.getTokenTtl());
assertNull(role.getTokenMaxTtl());
assertNull(role.getTokenBoundCidrs());
assertNull(role.getTokenExplicitMaxTtl());
assertNull(role.getTokenNoDefaultPolicy());
assertNull(role.getTokenNumUses());
assertNull(role.getTokenPeriod());
assertNull(role.getTokenType());
// Optional fields should be ignored, so JSON string should only contain role_name.
assertEquals(JSON_MIN, objectMapper.writeValueAsString(role));
}
/**
* Build token without all parameters set.
*/
@Test
void buildFullTest() throws JsonProcessingException {
AppRole role = createFull();
assertEquals(NAME, role.getName());
assertEquals(ID, role.getId());
assertEquals(BIND_SECRET_ID, role.getBindSecretId());
assertEquals(BOUND_CIDR_LIST, role.getSecretIdBoundCidrs());
assertEquals(POLICIES, role.getTokenPolicies());
assertEquals(SECRET_ID_NUM_USES, role.getSecretIdNumUses());
assertEquals(SECRET_ID_TTL, role.getSecretIdTtl());
assertEquals(ENABLE_LOCAL_SECRET_IDS, role.getEnableLocalSecretIds());
assertEquals(TOKEN_TTL, role.getTokenTtl());
assertEquals(TOKEN_MAX_TTL, role.getTokenMaxTtl());
assertEquals(BOUND_CIDR_LIST, role.getTokenBoundCidrs());
assertEquals(TOKEN_EXPLICIT_MAX_TTL, role.getTokenExplicitMaxTtl());
assertEquals(TOKEN_NO_DEFAULT_POLICY, role.getTokenNoDefaultPolicy());
assertEquals(TOKEN_NUM_USES, role.getTokenNumUses());
assertEquals(TOKEN_PERIOD, role.getTokenPeriod());
assertEquals(TOKEN_TYPE.value(), role.getTokenType());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(role));
}
/**
* Test convenience methods
*/
@Test
void convenienceMethodsTest() {
// bind_secret_id.
AppRole role = AppRole.builder(NAME).build();
assertNull(role.getBindSecretId());
role = AppRole.builder(NAME).withBindSecretID().build();
assertEquals(true, role.getBindSecretId());
role = AppRole.builder(NAME).withoutBindSecretID().build();
assertEquals(false, role.getBindSecretId());
// Add single CIDR subnet.
role = AppRole.builder(NAME).withSecretBoundCidr(CIDR_2).withTokenBoundCidr(CIDR_2).build();
assertEquals(1, role.getSecretIdBoundCidrs().size());
assertEquals(CIDR_2, role.getSecretIdBoundCidrs().get(0));
assertEquals(1, role.getTokenBoundCidrs().size());
assertEquals(CIDR_2, role.getTokenBoundCidrs().get(0));
role = AppRole.builder(NAME)
.withSecretIdBoundCidrs(BOUND_CIDR_LIST)
.withSecretBoundCidr(CIDR_2)
.withTokenBoundCidrs(BOUND_CIDR_LIST)
.withTokenBoundCidr(CIDR_2)
.build();
assertEquals(2, role.getSecretIdBoundCidrs().size());
assertTrue(role.getSecretIdBoundCidrs().containsAll(List.of(CIDR_1, CIDR_2)));
assertEquals(2, role.getTokenBoundCidrs().size());
assertTrue(role.getSecretIdBoundCidrs().containsAll(List.of(CIDR_1, CIDR_2)));
// Add single policy.
role = AppRole.builder(NAME).withTokenPolicy(POLICY_2).build();
assertEquals(1, role.getTokenPolicies().size());
assertEquals(POLICY_2, role.getTokenPolicies().get(0));
role = AppRole.builder(NAME)
.withTokenPolicies(POLICIES)
.withTokenPolicy(POLICY_2)
.build();
assertEquals(2, role.getTokenPolicies().size());
assertTrue(role.getTokenPolicies().containsAll(List.of(POLICY, POLICY_2)));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,8 +18,8 @@ package de.stklcode.jvault.connector.model;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* JUnit Test for AuthBackend model.
@ -27,19 +27,19 @@ import static org.hamcrest.Matchers.is;
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public class AuthBackendTest {
class AuthBackendTest {
/**
* Test forType() method.
*/
@Test
public void forTypeTest() {
assertThat(AuthBackend.forType("token"), is(AuthBackend.TOKEN));
assertThat(AuthBackend.forType("app-id"), is(AuthBackend.APPID));
assertThat(AuthBackend.forType("userpass"), is(AuthBackend.USERPASS));
assertThat(AuthBackend.forType("github"), is(AuthBackend.GITHUB));
assertThat(AuthBackend.forType(""), is(AuthBackend.UNKNOWN));
assertThat(AuthBackend.forType("foobar"), is(AuthBackend.UNKNOWN));
@SuppressWarnings("deprecation")
void forTypeTest() {
assertEquals(AuthBackend.TOKEN, AuthBackend.forType("token"));
assertEquals(AuthBackend.APPID, AuthBackend.forType("app-id"));
assertEquals(AuthBackend.USERPASS, AuthBackend.forType("userpass"));
assertEquals(AuthBackend.GITHUB, AuthBackend.forType("github"));
assertEquals(AuthBackend.UNKNOWN, AuthBackend.forType(""));
assertEquals(AuthBackend.UNKNOWN, AuthBackend.forType("foobar"));
}
}

View File

@ -1,161 +0,0 @@
/*
* 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;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
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.*;
/**
* JUnit Test for Token Builder.
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
public class TokenBuilderTest {
private static final String ID = "test-id";
private static final String DISPLAY_NAME = "display-name";
private static final Boolean NO_PARENT = false;
private static final Boolean NO_DEFAULT_POLICY = false;
private static final Integer TTL = 123;
private static final Integer NUM_USES = 4;
private static final List<String> POLICIES = new ArrayList<>();
private static final String POLICY = "policy";
private static final String POLICY_2 = "policy2";
private static final String POLICY_3 = "policy3";
private static final Map<String, String> META = new HashMap<>();
private static final String META_KEY = "key";
private static final String META_VALUE = "value";
private static final String META_KEY_2 = "key2";
private static final String META_VALUE_2 = "value2";
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}";
@BeforeAll
public static void init() {
POLICIES.add(POLICY);
META.put(META_KEY, META_VALUE);
}
/**
* Build token without any parameters.
*/
@Test
public void buildDefaultTest() throws JsonProcessingException {
Token token = new TokenBuilder().build();
assertThat(token.getId(), is(nullValue()));
assertThat(token.getDisplayName(), is(nullValue()));
assertThat(token.getNoParent(), is(nullValue()));
assertThat(token.getNoDefaultPolicy(), is(nullValue()));
assertThat(token.getTtl(), is(nullValue()));
assertThat(token.getNumUses(), is(nullValue()));
assertThat(token.getPolicies(), is(nullValue()));
assertThat(token.getMeta(), is(nullValue()));
assertThat(token.isRenewable(), is(nullValue()));
/* optional fields should be ignored, so JSON string should be empty */
assertThat(new ObjectMapper().writeValueAsString(token), is("{}"));
}
/**
* Build token without all parameters set.
*/
@Test
public void buildFullTest() throws JsonProcessingException {
Token token = new TokenBuilder()
.withId(ID)
.withDisplayName(DISPLAY_NAME)
.withNoParent(NO_PARENT)
.withNoDefaultPolicy(NO_DEFAULT_POLICY)
.withTtl(TTL)
.withNumUses(NUM_USES)
.withPolicies(POLICIES)
.withMeta(META)
.withRenewable(RENEWABLE)
.build();
assertThat(token.getId(), is(ID));
assertThat(token.getDisplayName(), is(DISPLAY_NAME));
assertThat(token.getNoParent(), is(NO_PARENT));
assertThat(token.getNoDefaultPolicy(), is(NO_DEFAULT_POLICY));
assertThat(token.getTtl(), is(TTL));
assertThat(token.getNumUses(), is(NUM_USES));
assertThat(token.getPolicies(), is(POLICIES));
assertThat(token.getMeta(), is(META));
assertThat(token.isRenewable(), is(RENEWABLE));
/* Verify that all parameters are included in JSON string */
assertThat(new ObjectMapper().writeValueAsString(token), is(JSON_FULL));
}
/**
* Test convenience methods
*/
@Test
public void convenienceMethodsTest() {
/* Parent */
Token token = new TokenBuilder().asOrphan().build();
assertThat(token.getNoParent(), is(true));
token = new TokenBuilder().withParent().build();
assertThat(token.getNoParent(), is(false));
/* Default policy */
token = new TokenBuilder().withDefaultPolicy().build();
assertThat(token.getNoDefaultPolicy(), is(false));
token = new TokenBuilder().withoutDefaultPolicy().build();
assertThat(token.getNoDefaultPolicy(), is(true));
/* Renewability */
token = new TokenBuilder().renewable().build();
assertThat(token.isRenewable(), is(true));
token = new TokenBuilder().notRenewable().build();
assertThat(token.isRenewable(), is(false));
/* Add single policy */
token = new TokenBuilder().withPolicy(POLICY_2).build();
assertThat(token.getPolicies(), hasSize(1));
assertThat(token.getPolicies(), contains(POLICY_2));
token = new TokenBuilder()
.withPolicies(POLICY, POLICY_2)
.withPolicy(POLICY_3)
.build();
assertThat(token.getPolicies(), hasSize(3));
assertThat(token.getPolicies(), contains(POLICY, POLICY_2, POLICY_3));
/* Add single metadata */
token = new TokenBuilder().withMeta(META_KEY_2, META_VALUE_2).build();
assertThat(token.getMeta().size(), is(1));
assertThat(token.getMeta().keySet(), contains(META_KEY_2));
assertThat(token.getMeta().get(META_KEY_2), is(META_VALUE_2));
token = new TokenBuilder()
.withMeta(META)
.withMeta(META_KEY_2, META_VALUE_2)
.build();
assertThat(token.getMeta().size(), is(2));
assertThat(token.getMeta().get(META_KEY), is(META_VALUE));
assertThat(token.getMeta().get(META_KEY_2), is(META_VALUE_2));
}
}

View File

@ -0,0 +1,212 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* Unit Test for {@link TokenRole} and {@link TokenRole.Builder}.
*
* @author Stefan Kalscheuer
* @since 0.9
*/
class TokenRoleTest extends AbstractModelTest<TokenRole> {
private static final String NAME = "test-role";
private static final String ALLOWED_POLICY_1 = "apol-1";
private static final String ALLOWED_POLICY_2 = "apol-2";
private static final String ALLOWED_POLICY_3 = "apol-3";
private static final List<String> ALLOWED_POLICIES = Arrays.asList(ALLOWED_POLICY_1, ALLOWED_POLICY_2);
private static final String ALLOWED_POLICY_GLOB_1 = "apol-g1*";
private static final String ALLOWED_POLICY_GLOB_2 = "apol-g2*";
private static final String ALLOWED_POLICY_GLOB_3 = "apol-g3*";
private static final List<String> ALLOWED_POLICIES_GLOB = Arrays.asList(ALLOWED_POLICY_GLOB_2, ALLOWED_POLICY_GLOB_3);
private static final String DISALLOWED_POLICY_1 = "dpol-1";
private static final String DISALLOWED_POLICY_2 = "dpol-2";
private static final String DISALLOWED_POLICY_3 = "dpol-3";
private static final List<String> DISALLOWED_POLICIES = Arrays.asList(DISALLOWED_POLICY_2, DISALLOWED_POLICY_3);
private static final String DISALLOWED_POLICY_GLOB_1 = "dpol-g1*";
private static final String DISALLOWED_POLICY_GLOB_2 = "dpol-g2*";
private static final String DISALLOWED_POLICY_GLOB_3 = "dpol-g3*";
private static final List<String> DISALLOWED_POLICIES_GLOB = Arrays.asList(DISALLOWED_POLICY_GLOB_1, DISALLOWED_POLICY_GLOB_2);
private static final Boolean ORPHAN = false;
private static final Boolean RENEWABLE = true;
private static final String PATH_SUFFIX = "ps";
private static final String ALLOWED_ENTITY_ALIAS_1 = "alias-1";
private static final String ALLOWED_ENTITY_ALIAS_2 = "alias-2";
private static final String ALLOWED_ENTITY_ALIAS_3 = "alias-3";
private static final List<String> ALLOWED_ENTITY_ALIASES = Arrays.asList(ALLOWED_ENTITY_ALIAS_1, ALLOWED_ENTITY_ALIAS_3);
private static final String TOKEN_BOUND_CIDR_1 = "192.0.2.0/24";
private static final String TOKEN_BOUND_CIDR_2 = "198.51.100.0/24";
private static final String TOKEN_BOUND_CIDR_3 = "203.0.113.0/24";
private static final List<String> TOKEN_BOUND_CIDRS = Arrays.asList(TOKEN_BOUND_CIDR_2, TOKEN_BOUND_CIDR_1);
private static final Integer TOKEN_EXPLICIT_MAX_TTL = 1234;
private static final Boolean TOKEN_NO_DEFAULT_POLICY = false;
private static final Integer TOKEN_NUM_USES = 5;
private static final Integer TOKEN_PERIOD = 2345;
private static final Token.Type TOKEN_TYPE = Token.Type.SERVICE;
private static final String JSON_FULL = "{" +
"\"name\":\"" + NAME + "\"," +
"\"allowed_policies\":[\"" + ALLOWED_POLICY_1 + "\",\"" + ALLOWED_POLICY_2 + "\",\"" + ALLOWED_POLICY_3 + "\"]," +
"\"allowed_policies_glob\":[\"" + ALLOWED_POLICY_GLOB_1 + "\",\"" + ALLOWED_POLICY_GLOB_2 + "\",\"" + ALLOWED_POLICY_GLOB_3 + "\"]," +
"\"disallowed_policies\":[\"" + DISALLOWED_POLICY_1 + "\",\"" + DISALLOWED_POLICY_2 + "\",\"" + DISALLOWED_POLICY_3 + "\"]," +
"\"disallowed_policies_glob\":[\"" + DISALLOWED_POLICY_GLOB_1 + "\",\"" + DISALLOWED_POLICY_GLOB_2 + "\",\"" + DISALLOWED_POLICY_GLOB_3 + "\"]," +
"\"orphan\":" + ORPHAN + "," +
"\"renewable\":" + RENEWABLE + "," +
"\"path_suffix\":\"" + PATH_SUFFIX + "\"," +
"\"allowed_entity_aliases\":[\"" + ALLOWED_ENTITY_ALIAS_1 + "\",\"" + ALLOWED_ENTITY_ALIAS_3 + "\",\"" + ALLOWED_ENTITY_ALIAS_2 + "\"]," +
"\"token_bound_cidrs\":[\"" + TOKEN_BOUND_CIDR_3 + "\",\"" + TOKEN_BOUND_CIDR_2 + "\",\"" + TOKEN_BOUND_CIDR_1 + "\"]," +
"\"token_explicit_max_ttl\":" + TOKEN_EXPLICIT_MAX_TTL + "," +
"\"token_no_default_policy\":" + TOKEN_NO_DEFAULT_POLICY + "," +
"\"token_num_uses\":" + TOKEN_NUM_USES + "," +
"\"token_period\":" + TOKEN_PERIOD + "," +
"\"token_type\":\"" + TOKEN_TYPE.value() + "\"}";
TokenRoleTest() {
super(TokenRole.class);
}
@Override
protected TokenRole createFull() {
return TokenRole.builder()
.forName(NAME)
.withAllowedPolicies(ALLOWED_POLICIES)
.withAllowedPolicy(ALLOWED_POLICY_3)
.withAllowedPolicyGlob(ALLOWED_POLICY_GLOB_1)
.withAllowedPoliciesGlob(ALLOWED_POLICIES_GLOB)
.withDisallowedPolicy(DISALLOWED_POLICY_1)
.withDisallowedPolicies(DISALLOWED_POLICIES)
.withDisallowedPoliciesGlob(DISALLOWED_POLICIES_GLOB)
.withDisallowedPolicyGlob(DISALLOWED_POLICY_GLOB_3)
.orphan(ORPHAN)
.renewable(RENEWABLE)
.withPathSuffix(PATH_SUFFIX)
.withAllowedEntityAliases(ALLOWED_ENTITY_ALIASES)
.withAllowedEntityAlias(ALLOWED_ENTITY_ALIAS_2)
.withTokenBoundCidr(TOKEN_BOUND_CIDR_3)
.withTokenBoundCidrs(TOKEN_BOUND_CIDRS)
.withTokenExplicitMaxTtl(TOKEN_EXPLICIT_MAX_TTL)
.withTokenNoDefaultPolicy(TOKEN_NO_DEFAULT_POLICY)
.withTokenNumUses(TOKEN_NUM_USES)
.withTokenPeriod(TOKEN_PERIOD)
.withTokenType(TOKEN_TYPE)
.build();
}
/**
* Build token without any parameters.
*/
@Test
void buildDefaultTest() throws JsonProcessingException {
TokenRole role = TokenRole.builder().build();
assertNull(role.getAllowedPolicies());
assertNull(role.getDisallowedPolicies());
assertNull(role.getOrphan());
assertNull(role.getRenewable());
assertNull(role.getAllowedEntityAliases());
assertNull(role.getTokenBoundCidrs());
assertNull(role.getTokenExplicitMaxTtl());
assertNull(role.getTokenNoDefaultPolicy());
assertNull(role.getTokenNumUses());
assertNull(role.getTokenPeriod());
assertNull(role.getTokenType());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(role));
}
/**
* Build token without all parameters NULL.
*/
@Test
void buildNullTest() throws JsonProcessingException {
TokenRole role = TokenRole.builder()
.forName(null)
.withAllowedPolicies(null)
.withAllowedPolicy(null)
.withDisallowedPolicy(null)
.withDisallowedPolicies(null)
.orphan(null)
.renewable(null)
.withPathSuffix(null)
.withAllowedEntityAliases(null)
.withAllowedEntityAlias(null)
.withTokenBoundCidr(null)
.withTokenBoundCidrs(null)
.withTokenExplicitMaxTtl(null)
.withTokenNoDefaultPolicy(null)
.withTokenNumUses(null)
.withTokenPeriod(null)
.withTokenType(null)
.build();
assertNull(role.getAllowedPolicies());
assertNull(role.getDisallowedPolicies());
assertNull(role.getOrphan());
assertNull(role.getRenewable());
assertNull(role.getAllowedEntityAliases());
assertNull(role.getTokenBoundCidrs());
assertNull(role.getTokenExplicitMaxTtl());
assertNull(role.getTokenNoDefaultPolicy());
assertNull(role.getTokenNumUses());
assertNull(role.getTokenPeriod());
assertNull(role.getTokenType());
// Empty builder should be equal to no-arg construction.
assertEquals(role, new TokenRole());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(role));
}
/**
* Build token without all parameters set.
*/
@Test
void buildFullTest() throws JsonProcessingException {
TokenRole role = createFull();
assertEquals(NAME, role.getName());
assertEquals(ALLOWED_POLICIES.size() + 1, role.getAllowedPolicies().size());
assertTrue(role.getAllowedPolicies().containsAll(List.of(ALLOWED_POLICY_1, ALLOWED_POLICY_2, ALLOWED_POLICY_3)));
assertEquals(ALLOWED_POLICIES_GLOB.size() + 1, role.getAllowedPoliciesGlob().size());
assertTrue(role.getAllowedPoliciesGlob().containsAll(List.of(ALLOWED_POLICY_GLOB_1, ALLOWED_POLICY_GLOB_2, ALLOWED_POLICY_GLOB_3)));
assertEquals(DISALLOWED_POLICIES.size() + 1, role.getDisallowedPolicies().size());
assertTrue(role.getDisallowedPolicies().containsAll(List.of(DISALLOWED_POLICY_1, DISALLOWED_POLICY_2, DISALLOWED_POLICY_3)));
assertEquals(DISALLOWED_POLICIES_GLOB.size() + 1, role.getDisallowedPoliciesGlob().size());
assertTrue(role.getDisallowedPoliciesGlob().containsAll(List.of(DISALLOWED_POLICY_GLOB_1, DISALLOWED_POLICY_GLOB_2, DISALLOWED_POLICY_GLOB_3)));
assertEquals(ORPHAN, role.getOrphan());
assertEquals(RENEWABLE, role.getRenewable());
assertEquals(PATH_SUFFIX, role.getPathSuffix());
assertEquals(ALLOWED_ENTITY_ALIASES.size() + 1, role.getAllowedEntityAliases().size());
assertTrue(role.getAllowedEntityAliases().containsAll(List.of(ALLOWED_ENTITY_ALIAS_1, ALLOWED_ENTITY_ALIAS_2, ALLOWED_ENTITY_ALIAS_3)));
assertEquals(TOKEN_BOUND_CIDRS.size() + 1, role.getTokenBoundCidrs().size());
assertTrue(role.getTokenBoundCidrs().containsAll(List.of(TOKEN_BOUND_CIDR_1, TOKEN_BOUND_CIDR_2, TOKEN_BOUND_CIDR_3)));
assertEquals(TOKEN_NO_DEFAULT_POLICY, role.getTokenNoDefaultPolicy());
assertEquals(TOKEN_NUM_USES, role.getTokenNumUses());
assertEquals(TOKEN_PERIOD, role.getTokenPeriod());
assertEquals(TOKEN_TYPE.value(), role.getTokenType());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(role));
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link Token} and {@link Token.Builder}.
*
* @author Stefan Kalscheuer
* @since 0.4.0
*/
class TokenTest extends AbstractModelTest<Token> {
private static final String ID = "test-id";
private static final String DISPLAY_NAME = "display-name";
private static final Boolean NO_PARENT = false;
private static final Boolean NO_DEFAULT_POLICY = false;
private static final Integer TTL = 123;
private static final Integer EXPLICIT_MAX_TTL = 456;
private static final Integer NUM_USES = 4;
private static final List<String> POLICIES = new ArrayList<>();
private static final String POLICY = "policy";
private static final String POLICY_2 = "policy2";
private static final String POLICY_3 = "policy3";
private static final Map<String, String> META = new HashMap<>();
private static final String META_KEY = "key";
private static final String META_VALUE = "value";
private static final String META_KEY_2 = "key2";
private static final String META_VALUE_2 = "value2";
private static final Boolean RENEWABLE = true;
private static final Integer PERIOD = 3600;
private static final String ENTITY_ALIAS = "alias-value";
private static final String JSON_FULL = "{\"id\":\"test-id\",\"type\":\"service\",\"display_name\":\"display-name\",\"no_parent\":false,\"no_default_policy\":false,\"ttl\":123,\"explicit_max_ttl\":456,\"num_uses\":4,\"policies\":[\"policy\"],\"meta\":{\"key\":\"value\"},\"renewable\":true,\"period\":3600,\"entity_alias\":\"alias-value\"}";
TokenTest() {
super(Token.class);
}
@Override
protected Token createFull() {
return Token.builder()
.withId(ID)
.withType(Token.Type.SERVICE)
.withDisplayName(DISPLAY_NAME)
.withNoParent(NO_PARENT)
.withNoDefaultPolicy(NO_DEFAULT_POLICY)
.withTtl(TTL)
.withExplicitMaxTtl(EXPLICIT_MAX_TTL)
.withNumUses(NUM_USES)
.withPolicies(POLICIES)
.withMeta(META)
.withRenewable(RENEWABLE)
.withPeriod(PERIOD)
.withEntityAlias(ENTITY_ALIAS)
.build();
}
@BeforeAll
static void init() {
POLICIES.add(POLICY);
META.put(META_KEY, META_VALUE);
}
/**
* Build token without any parameters.
*/
@Test
void buildDefaultTest() throws JsonProcessingException {
Token token = Token.builder().build();
assertNull(token.getId());
assertNull(token.getType());
assertNull(token.getDisplayName());
assertNull(token.getNoParent());
assertNull(token.getNoDefaultPolicy());
assertNull(token.getTtl());
assertNull(token.getExplicitMaxTtl());
assertNull(token.getNumUses());
assertNull(token.getPolicies());
assertNull(token.getMeta());
assertNull(token.isRenewable());
assertNull(token.getPeriod());
assertNull(token.getEntityAlias());
// Optional fields should be ignored, so JSON string should be empty.
assertEquals("{}", objectMapper.writeValueAsString(token));
// Empty builder should be equal to no-arg construction.
assertEquals(token, new Token());
}
/**
* Build token without all parameters set.
*/
@Test
void buildFullTest() throws JsonProcessingException {
Token token = createFull();
assertEquals(ID, token.getId());
assertEquals(Token.Type.SERVICE.value(), token.getType());
assertEquals(DISPLAY_NAME, token.getDisplayName());
assertEquals(NO_PARENT, token.getNoParent());
assertEquals(NO_DEFAULT_POLICY, token.getNoDefaultPolicy());
assertEquals(TTL, token.getTtl());
assertEquals(EXPLICIT_MAX_TTL, token.getExplicitMaxTtl());
assertEquals(NUM_USES, token.getNumUses());
assertEquals(POLICIES, token.getPolicies());
assertEquals(META, token.getMeta());
assertEquals(RENEWABLE, token.isRenewable());
assertEquals(PERIOD, token.getPeriod());
// Verify that all parameters are included in JSON string.
assertEquals(JSON_FULL, objectMapper.writeValueAsString(token));
}
/**
* Test convenience methods
*/
@Test
void convenienceMethodsTest() {
// Parent.
Token token = Token.builder().asOrphan().build();
assertEquals(true, token.getNoParent());
token = Token.builder().withParent().build();
assertEquals(false, token.getNoParent());
// Default policy.
token = Token.builder().withDefaultPolicy().build();
assertEquals(false, token.getNoDefaultPolicy());
token = Token.builder().withoutDefaultPolicy().build();
assertEquals(true, token.getNoDefaultPolicy());
// Renewability.
token = Token.builder().renewable().build();
assertEquals(true, token.isRenewable());
token = Token.builder().notRenewable().build();
assertEquals(false, token.isRenewable());
// Add single policy.
token = Token.builder().withPolicy(POLICY_2).build();
assertEquals(1, token.getPolicies().size());
assertEquals(List.of(POLICY_2), token.getPolicies());
token = Token.builder()
.withPolicies(POLICY, POLICY_2)
.withPolicy(POLICY_3)
.build();
assertEquals(3, token.getPolicies().size());
assertTrue(token.getPolicies().containsAll(List.of(POLICY, POLICY_2, POLICY_3)));
// Add single metadata.
token = Token.builder().withMeta(META_KEY_2, META_VALUE_2).build();
assertEquals(1, token.getMeta().size());
assertEquals(Set.of(META_KEY_2), token.getMeta().keySet());
assertEquals(META_VALUE_2, token.getMeta().get(META_KEY_2));
token = Token.builder()
.withMeta(META)
.withMeta(META_KEY_2, META_VALUE_2)
.build();
assertEquals(2, token.getMeta().size());
assertEquals(META_VALUE, token.getMeta().get(META_KEY));
assertEquals(META_VALUE_2, token.getMeta().get(META_KEY_2));
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,18 +16,14 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.AppRole;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link AppRoleResponse} model.
@ -35,7 +31,7 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AppRoleResponseTest {
class AppRoleResponseTest extends AbstractModelTest<AppRoleResponse> {
private static final Integer ROLE_TOKEN_TTL = 1200;
private static final Integer ROLE_TOKEN_MAX_TTL = 1800;
private static final Integer ROLE_SECRET_TTL = 600;
@ -53,10 +49,10 @@ public class AppRoleResponseTest {
" \"token_max_ttl\": " + ROLE_TOKEN_MAX_TTL + ",\n" +
" \"secret_id_ttl\": " + ROLE_SECRET_TTL + ",\n" +
" \"secret_id_num_uses\": " + ROLE_SECRET_NUM_USES + ",\n" +
" \"policies\": [\n" +
" \"token_policies\": [\n" +
" \"" + ROLE_POLICY + "\"\n" +
" ],\n" +
" \"period\": " + ROLE_PERIOD + ",\n" +
" \"token_period\": " + ROLE_PERIOD + ",\n" +
" \"bind_secret_id\": " + ROLE_BIND_SECRET + ",\n" +
" \"bound_cidr_list\": \"\"\n" +
" },\n" +
@ -65,53 +61,51 @@ public class AppRoleResponseTest {
" \"lease_id\": \"\"\n" +
"}";
private static final Map<String, Object> INVALID_DATA = new HashMap<>();
AppRoleResponseTest() {
super(AppRoleResponse.class);
}
static {
INVALID_DATA.put("policies", "fancy-policy");
@Override
protected AppRoleResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, AppRoleResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
void getDataRoundtrip() {
// Create empty Object.
AppRoleResponse res = new AppRoleResponse();
assertThat("Initial data should be empty", res.getRole(), is(nullValue()));
// Parsing invalid auth data map should fail.
try {
res.setData(INVALID_DATA);
fail("Parsing invalid data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
assertNull(res.getRole(), "Initial data should be empty");
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AppRoleResponse res = new ObjectMapper().readValue(RES_JSON, AppRoleResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract role data.
AppRole role = res.getRole();
assertThat("Role data is NULL", role, is(notNullValue()));
assertThat("Incorrect token TTL", role.getTokenTtl(), is(ROLE_TOKEN_TTL));
assertThat("Incorrect token max TTL", role.getTokenMaxTtl(), is(ROLE_TOKEN_MAX_TTL));
assertThat("Incorrect secret ID TTL", role.getSecretIdTtl(), is(ROLE_SECRET_TTL));
assertThat("Incorrect secret ID umber of uses", role.getSecretIdNumUses(), is(ROLE_SECRET_NUM_USES));
assertThat("Incorrect number of policies", role.getPolicies(), hasSize(1));
assertThat("Incorrect role policies", role.getPolicies(), contains(ROLE_POLICY));
assertThat("Incorrect role period", role.getPeriod(), is(ROLE_PERIOD));
assertThat("Incorrect role bind secret ID flag", role.getBindSecretId(), is(ROLE_BIND_SECRET));
assertThat("Incorrect biund CIDR list", role.getBoundCidrList(), is(nullValue()));
assertThat("Incorrect biund CIDR list string", role.getBoundCidrListString(), is(emptyString()));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
void jsonRoundtrip() {
AppRoleResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AppRoleResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
// Extract role data.
AppRole role = res.getRole();
assertNotNull(role, "Role data is NULL");
assertEquals(ROLE_TOKEN_TTL, role.getTokenTtl(), "Incorrect token TTL");
assertEquals(ROLE_TOKEN_MAX_TTL, role.getTokenMaxTtl(), "Incorrect token max TTL");
assertEquals(ROLE_SECRET_TTL, role.getSecretIdTtl(), "Incorrect secret ID TTL");
assertEquals(ROLE_SECRET_NUM_USES, role.getSecretIdNumUses(), "Incorrect secret ID umber of uses");
assertEquals(List.of(ROLE_POLICY), role.getTokenPolicies(), "Incorrect policies");
assertEquals(ROLE_PERIOD, role.getTokenPeriod(), "Incorrect role period");
assertEquals(ROLE_BIND_SECRET, role.getBindSecretId(), "Incorrect role bind secret ID flag");
assertNull(role.getTokenBoundCidrs(), "Incorrect bound CIDR list");
assertEquals("", role.getTokenBoundCidrsString(), "Incorrect bound CIDR list string");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,19 +16,17 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.AuthBackend;
import de.stklcode.jvault.connector.model.response.embedded.AuthMethod;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link AuthMethodsResponse} model.
@ -36,12 +34,16 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AuthMethodsResponseTest {
class AuthMethodsResponseTest extends AbstractModelTest<AuthMethodsResponse> {
private static final String GH_PATH = "github/";
private static final String GH_TYPE = "github";
private static final String GH_UUID = "4b42d1a4-0a0d-3c88-ae90-997e0c8b41be";
private static final String GH_ACCESSOR = "auth_github_badd7fd0";
private static final String GH_DESCR = "GitHub auth";
private static final String TK_PATH = "token/";
private static final String TK_TYPE = "token";
private static final String TK_UUID = "32ea9681-6bd6-6cec-eec3-d11260ba9741";
private static final String TK_ACCESSOR = "auth_token_ac0dd95a";
private static final String TK_DESCR = "token based credentials";
private static final Integer TK_LEASE_TTL = 0;
private static final Integer TK_MAX_LEASE_TTL = 0;
@ -49,8 +51,13 @@ public class AuthMethodsResponseTest {
private static final String RES_JSON = "{\n" +
" \"data\": {" +
" \"" + GH_PATH + "\": {\n" +
" \"uuid\": \"" + GH_UUID + "\",\n" +
" \"type\": \"" + GH_TYPE + "\",\n" +
" \"description\": \"" + GH_DESCR + "\"\n" +
" \"accessor\": \"" + GH_ACCESSOR + "\",\n" +
" \"description\": \"" + GH_DESCR + "\",\n" +
" \"external_entropy_access\": false,\n" +
" \"local\": false,\n" +
" \"seal_wrap\": false\n" +
" },\n" +
" \"" + TK_PATH + "\": {\n" +
" \"config\": {\n" +
@ -58,71 +65,87 @@ public class AuthMethodsResponseTest {
" \"max_lease_ttl\": " + TK_MAX_LEASE_TTL + "\n" +
" },\n" +
" \"description\": \"" + TK_DESCR + "\",\n" +
" \"type\": \"" + TK_TYPE + "\"\n" +
" \"type\": \"" + TK_TYPE + "\",\n" +
" \"uuid\": \"" + TK_UUID + "\",\n" +
" \"accessor\": \"" + TK_ACCESSOR + "\",\n" +
" \"external_entropy_access\": false,\n" +
" \"local\": true,\n" +
" \"seal_wrap\": false\n" +
" }\n" +
" }\n" +
"}";
private static final Map<String, Object> INVALID_DATA = new HashMap<>();
AuthMethodsResponseTest() {
super(AuthMethodsResponse.class);
}
static {
INVALID_DATA.put("dummy/", new Dummy());
@Override
protected AuthMethodsResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, AuthMethodsResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
void getDataRoundtrip() {
// Create empty Object.
AuthMethodsResponse res = new AuthMethodsResponse();
assertThat("Initial method map should be empty", res.getSupportedMethods(), is(anEmptyMap()));
// Parsing invalid data map should fail.
try {
res.setData(INVALID_DATA);
fail("Parsing invalid data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
assertEquals(Collections.emptyMap(), res.getSupportedMethods(), "Initial method map should be empty");
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AuthMethodsResponse res = new ObjectMapper().readValue(RES_JSON, AuthMethodsResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract auth data.
Map<String, AuthMethod> supported = res.getSupportedMethods();
assertThat("Auth data is NULL", supported, is(notNullValue()));
assertThat("Incorrect number of supported methods", supported.entrySet(), hasSize(2));
assertThat("Incorrect method paths", supported.keySet(), containsInAnyOrder(GH_PATH, TK_PATH));
void jsonRoundtrip() {
AuthMethodsResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AuthMethodsResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
// Extract auth data.
Map<String, AuthMethod> supported = res.getSupportedMethods();
assertNotNull(supported, "Auth data is NULL");
assertEquals(2, supported.size(), "Incorrect number of supported methods");
assertTrue(supported.keySet().containsAll(Set.of(GH_PATH, TK_PATH)), "Incorrect method paths");
// Verify first method.
AuthMethod method = supported.get(GH_PATH);
assertThat("Incorrect raw type for GitHub", method.getRawType(), is(GH_TYPE));
assertThat("Incorrect parsed type for GitHub", method.getType(), is(AuthBackend.GITHUB));
assertThat("Incorrect description for GitHub", method.getDescription(), is(GH_DESCR));
assertThat("Unexpected config for GitHub", method.getConfig(), is(nullValue()));
// Verify first method.
AuthMethod method = supported.get(GH_PATH);
assertEquals(GH_TYPE, method.getRawType(), "Incorrect raw type for GitHub");
assertEquals(AuthBackend.GITHUB, method.getType(), "Incorrect parsed type for GitHub");
assertEquals(GH_DESCR, method.getDescription(), "Incorrect description for GitHub");
assertNull(method.getConfig(), "Unexpected config for GitHub");
assertEquals(GH_UUID, method.getUuid(), "Unexpected UUID for GitHub");
assertEquals(GH_ACCESSOR, method.getAccessor(), "Unexpected accessor for GitHub");
assertFalse(method.isLocal(), "Unexpected local flag for GitHub");
assertFalse(method.isExternalEntropyAccess(), "Unexpected external entropy flag for GitHub");
assertFalse(method.isSealWrap(), "Unexpected seal wrap flag for GitHub");
// Verify first method.
method = supported.get(TK_PATH);
assertThat("Incorrect raw type for Token", method.getRawType(), is(TK_TYPE));
assertThat("Incorrect parsed type for Token", method.getType(), is(AuthBackend.TOKEN));
assertThat("Incorrect description for Token", method.getDescription(), is(TK_DESCR));
assertThat("Missing config for Token", method.getConfig(), is(notNullValue()));
assertThat("Unexpected config size for Token", method.getConfig().keySet(), hasSize(2));
assertThat("Incorrect lease TTL config", method.getConfig().get("default_lease_ttl"), is(TK_LEASE_TTL.toString()));
assertThat("Incorrect max lease TTL config", method.getConfig().get("max_lease_ttl"), is(TK_MAX_LEASE_TTL.toString()));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
}
private static class Dummy {
// Verify second method.
method = supported.get(TK_PATH);
assertEquals(TK_TYPE, method.getRawType(), "Incorrect raw type for Token");
assertEquals(AuthBackend.TOKEN, method.getType(), "Incorrect parsed type for Token");
assertEquals(TK_DESCR, method.getDescription(), "Incorrect description for Token");
assertEquals(TK_UUID, method.getUuid(), "Unexpected UUID for Token");
assertEquals(TK_ACCESSOR, method.getAccessor(), "Unexpected accessor for Token");
assertTrue(method.isLocal(), "Unexpected local flag for Token");
assertFalse(method.isExternalEntropyAccess(), "Unexpected external entropy flag for Token");
assertFalse(method.isSealWrap(), "Unexpected seal wrap flag for GitHub");
assertNotNull(method.getConfig(), "Missing config for Token");
assertEquals(
Map.of(
"default_lease_ttl", TK_LEASE_TTL.toString(),
"max_lease_ttl", TK_MAX_LEASE_TTL.toString()
),
method.getConfig(),
"Unexpected config for Token"
);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,18 +16,15 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.response.embedded.AuthData;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link AuthResponse} model.
@ -35,7 +32,7 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class AuthResponseTest {
class AuthResponseTest extends AbstractModelTest<AuthResponse> {
private static final String AUTH_ACCESSOR = "2c84f488-2133-4ced-87b0-570f93a76830";
private static final String AUTH_CLIENT_TOKEN = "ABCD";
private static final String AUTH_POLICY_1 = "web";
@ -44,6 +41,9 @@ public class AuthResponseTest {
private static final String AUTH_META_VALUE = "armon";
private static final Integer AUTH_LEASE_DURATION = 3600;
private static final Boolean AUTH_RENEWABLE = true;
private static final String AUTH_ENTITY_ID = "";
private static final String AUTH_TOKEN_TYPE = "service";
private static final Boolean AUTH_ORPHAN = false;
private static final String RES_JSON = "{\n" +
" \"auth\": {\n" +
@ -53,61 +53,59 @@ public class AuthResponseTest {
" \"" + AUTH_POLICY_1 + "\", \n" +
" \"" + AUTH_POLICY_2 + "\"\n" +
" ],\n" +
" \"token_policies\": [\n" +
" \"" + AUTH_POLICY_2 + "\",\n" +
" \"" + AUTH_POLICY_1 + "\" \n" +
" ],\n" +
" \"metadata\": {\n" +
" \"" + AUTH_META_KEY + "\": \"" + AUTH_META_VALUE + "\"\n" +
" },\n" +
" \"lease_duration\": " + AUTH_LEASE_DURATION + ",\n" +
" \"renewable\": " + AUTH_RENEWABLE + "\n" +
" \"renewable\": " + AUTH_RENEWABLE + ",\n" +
" \"entity_id\": \"" + AUTH_ENTITY_ID + "\",\n" +
" \"token_type\": \"" + AUTH_TOKEN_TYPE + "\",\n" +
" \"orphan\": " + AUTH_ORPHAN + "\n" +
" }\n" +
"}";
private static final Map<String, Object> INVALID_AUTH_DATA = new HashMap<>();
static {
INVALID_AUTH_DATA.put("policies", "fancy-policy");
AuthResponseTest() {
super(AuthResponse.class);
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
// Create empty Object.
AuthResponse res = new AuthResponse();
assertThat("Initial data should be empty", res.getData(), is(nullValue()));
// Parsing invalid auth data map should fail.
@Override
protected AuthResponse createFull() {
try {
res.setAuth(INVALID_AUTH_DATA);
fail("Parsing invalid auth data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
return objectMapper.readValue(RES_JSON, AuthResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
// Data method should be agnostic.
res.setData(INVALID_AUTH_DATA);
assertThat("Data not passed through", res.getData(), is(INVALID_AUTH_DATA));
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
AuthResponse res = new ObjectMapper().readValue(RES_JSON, AuthResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
// Extract auth data.
AuthData data = res.getAuth();
assertThat("Auth data is NULL", data, is(notNullValue()));
assertThat("Incorrect auth accessor", data.getAccessor(), is(AUTH_ACCESSOR));
assertThat("Incorrect auth client token", data.getClientToken(), is(AUTH_CLIENT_TOKEN));
assertThat("Incorrect auth lease duration", data.getLeaseDuration(), is(AUTH_LEASE_DURATION));
assertThat("Incorrect auth renewable flag", data.isRenewable(), is(AUTH_RENEWABLE));
assertThat("Incorrect number of policies", data.getPolicies(), hasSize(2));
assertThat("Incorrect auth policies", data.getPolicies(), containsInAnyOrder(AUTH_POLICY_1, AUTH_POLICY_2));
} catch (IOException e) {
fail("AuthResponse deserialization failed: " + e.getMessage());
}
void jsonRoundtrip() {
AuthResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, AuthResponse.class),
"AuthResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
// Extract auth data.
AuthData data = res.getAuth();
assertNotNull(data, "Auth data is NULL");
assertEquals(AUTH_ACCESSOR, data.getAccessor(), "Incorrect auth accessor");
assertEquals(AUTH_CLIENT_TOKEN, data.getClientToken(), "Incorrect auth client token");
assertEquals(AUTH_LEASE_DURATION, data.getLeaseDuration(), "Incorrect auth lease duration");
assertEquals(AUTH_RENEWABLE, data.isRenewable(), "Incorrect auth renewable flag");
assertEquals(AUTH_ORPHAN, data.isOrphan(), "Incorrect auth orphan flag");
assertEquals(AUTH_TOKEN_TYPE, data.getTokenType(), "Incorrect auth token type");
assertEquals(AUTH_ENTITY_ID, data.getEntityId(), "Incorrect auth entity id");
assertEquals(2, data.getPolicies().size(), "Incorrect number of policies");
assertTrue(data.getPolicies().containsAll(Set.of(AUTH_POLICY_1, AUTH_POLICY_2)));
assertEquals(2, data.getTokenPolicies().size(), "Incorrect number of token policies");
assertTrue(data.getTokenPolicies().containsAll(Set.of(AUTH_POLICY_2, AUTH_POLICY_1)), "Incorrect token policies");
assertEquals(Map.of(AUTH_META_KEY, AUTH_META_VALUE), data.getMetadata(), "Incorrect auth metadata");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,18 +16,12 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link CredentialsResponse} model.
@ -35,14 +29,33 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.8
*/
public class CredentialsResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
class CredentialsResponseTest extends AbstractModelTest<CredentialsResponse> {
private static final String VAL_USER = "testUserName";
private static final String VAL_PASS = "5up3r5ecr3tP455";
private static final String JSON = "{\n" +
" \"request_id\": \"68315073-6658-e3ff-2da7-67939fb91bbd\",\n" +
" \"lease_id\": \"\",\n" +
" \"lease_duration\": 2764800,\n" +
" \"renewable\": false,\n" +
" \"data\": {\n" +
" \"username\": \"" + VAL_USER + "\",\n" +
" \"password\": \"" + VAL_PASS + "\"\n" +
" },\n" +
" \"warnings\": null\n" +
"}";
static {
DATA.put("username", VAL_USER);
DATA.put("password", VAL_PASS);
CredentialsResponseTest() {
super(CredentialsResponse.class);
}
@Override
protected CredentialsResponse createFull() {
try {
return objectMapper.readValue(JSON, CredentialsResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
@ -51,16 +64,17 @@ public class CredentialsResponseTest {
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
public void getCredentialsTest() throws InvalidResponseException {
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()));
assertNull(res.getUsername(), "Username not present in data map should not return anything");
assertNull(res.getPassword(), "Password not present in data map should not return anything");
// Fill data map.
res.setData(DATA);
assertThat("Incorrect username", res.getUsername(), is(VAL_USER));
assertThat("Incorrect password", res.getPassword(), is(VAL_PASS));
res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, CredentialsResponse.class),
"Deserialization of CredentialsResponse failed"
);
assertEquals(VAL_USER, res.getUsername(), "Incorrect username");
assertEquals(VAL_PASS, res.getPassword(), "Incorrect password");
}
}

View File

@ -0,0 +1,88 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link ErrorResponse} model.
*
* @author Stefan Kalscheuer
*/
class ErrorResponseTest extends AbstractModelTest<ErrorResponse> {
private static final String ERROR_1 = "Error #1";
private static final String ERROR_2 = "Error #2";
private static final String JSON = "{\"errors\":[\"" + ERROR_1 + "\",\"" + ERROR_2 + "\"]}";
private static final String JSON_EMPTY = "{\"errors\":[]}";
ErrorResponseTest() {
super(ErrorResponse.class);
}
@Override
protected ErrorResponse createFull() {
try {
return objectMapper.readValue(JSON, ErrorResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault.
*/
@Test
void jsonRoundtrip() {
ErrorResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, ErrorResponse.class),
"ErrorResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(List.of(ERROR_1, ERROR_2), res.getErrors(), "Unexpected error messages");
assertEquals(
JSON,
assertDoesNotThrow(() -> objectMapper.writeValueAsString(res), "ErrorResponse serialization failed"),
"Unexpected JSON string after serialization"
);
}
@Test
void testToString() {
ErrorResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, ErrorResponse.class),
"ErrorResponse deserialization failed"
);
assertEquals(ERROR_1, res.toString());
res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON_EMPTY, ErrorResponse.class),
"ErrorResponse deserialization failed with empty list"
);
assertEquals("error response", res.toString());
assertEquals("error response", new ErrorResponse().toString());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,15 +16,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link AuthResponse} model.
@ -32,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.7.0
*/
public class HealthResponseTest {
class HealthResponseTest extends AbstractModelTest<HealthResponse> {
private static final String CLUSTER_ID = "c9abceea-4f46-4dab-a688-5ce55f89e228";
private static final String CLUSTER_NAME = "vault-cluster-5515c810";
private static final String VERSION = "0.9.2";
@ -52,30 +48,44 @@ public class HealthResponseTest {
" \"standby\": " + STANDBY + ",\n" +
" \"sealed\": " + SEALED + ",\n" +
" \"initialized\": " + INITIALIZED + ",\n" +
" \"replication_perf_mode\": \"" + REPL_PERF_MODE + "\",\n" +
" \"replication_performance_mode\": \"" + REPL_PERF_MODE + "\",\n" +
" \"replication_dr_mode\": \"" + REPL_DR_MODE + "\",\n" +
" \"performance_standby\": " + PERF_STANDBY + "\n" +
"}";
HealthResponseTest() {
super(HealthResponse.class);
}
@Override
protected HealthResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, HealthResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
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());
}
void jsonRoundtrip() {
HealthResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, HealthResponse.class),
"Health deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(CLUSTER_ID, res.getClusterID(), "Incorrect cluster ID");
assertEquals(CLUSTER_NAME, res.getClusterName(), "Incorrect cluster name");
assertEquals(VERSION, res.getVersion(), "Incorrect version");
assertEquals(SERVER_TIME_UTC, res.getServerTimeUTC(), "Incorrect server time");
assertEquals(STANDBY, res.isStandby(), "Incorrect standby state");
assertEquals(SEALED, res.isSealed(), "Incorrect seal state");
assertEquals(INITIALIZED, res.isInitialized(), "Incorrect initialization state");
assertEquals(PERF_STANDBY, res.isPerformanceStandby(), "Incorrect performance standby state");
assertEquals(REPL_PERF_MODE, res.getReplicationPerfMode(), "Incorrect replication perf mode");
assertEquals(REPL_DR_MODE, res.getReplicationDrMode(), "Incorrect replication DR mode");
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link HelpResponse} model.
*
* @author Stefan Kalscheuer
*/
class HelpResponseTest extends AbstractModelTest<HelpResponse> {
private static final String HELP = "Help Text.";
private static final String JSON = "{\"help\":\"" + HELP + "\"}";
HelpResponseTest() {
super(HelpResponse.class);
}
@Override
protected HelpResponse createFull() {
try {
return objectMapper.readValue(JSON, HelpResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault.
*/
@Test
void jsonRoundtrip() {
HelpResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, HelpResponse.class),
"HelpResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(HELP, res.getHelp(), "Unexpected help text");
assertEquals(
JSON,
assertDoesNotThrow(() -> objectMapper.writeValueAsString(res), "HelpResponse serialization failed"),
"Unexpected JSON string after serialization"
);
}
}

View File

@ -0,0 +1,142 @@
/*
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link MetaSecretResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
class MetaSecretResponseTest extends AbstractModelTest<MetaSecretResponse> {
private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd";
private static final String SECRET_LEASE_ID = "";
private static final Integer SECRET_LEASE_DURATION = 2764800;
private static final boolean SECRET_RENEWABLE = false;
private static final String SECRET_DATA_K1 = "excited";
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_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" +
"}";
MetaSecretResponseTest() {
super(MetaSecretResponse.class);
}
@Override
protected MetaSecretResponse createFull() {
try {
return objectMapper.readValue(SECRET_JSON_V2, MetaSecretResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
void jsonRoundtrip() {
// KV v2 secret.
MetaSecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON_V2, MetaSecretResponse.class),
"SecretResponse deserialization failed"
);
assertSecretData(res);
assertNotNull(res.getMetadata(), "SecretResponse does not contain metadata");
assertEquals(SECRET_META_CREATED, res.getMetadata().getCreatedTimeString(), "Incorrect creation date string");
assertNotNull(res.getMetadata().getCreatedTime(), "Creation date parsing failed");
assertEquals("", res.getMetadata().getDeletionTimeString(), "Incorrect deletion date string");
assertNull(res.getMetadata().getDeletionTime(), "Incorrect deletion date");
assertFalse(res.getMetadata().isDestroyed(), "Secret destroyed when not expected");
assertEquals(1, res.getMetadata().getVersion(), "Incorrect secret version");
// Deleted KV v2 secret.
res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON_V2_2, MetaSecretResponse.class),
"SecretResponse deserialization failed"
);
assertSecretData(res);
assertNotNull(res.getMetadata(), "SecretResponse does not contain metadata");
assertEquals(SECRET_META_CREATED, res.getMetadata().getCreatedTimeString(), "Incorrect creation date string");
assertNotNull(res.getMetadata().getCreatedTime(), "Creation date parsing failed");
assertEquals(SECRET_META_DELETED, res.getMetadata().getDeletionTimeString(), "Incorrect deletion date string");
assertNotNull(res.getMetadata().getDeletionTime(), "Incorrect deletion date");
assertTrue(res.getMetadata().isDestroyed(), "Secret destroyed when not expected");
assertEquals(2, res.getMetadata().getVersion(), "Incorrect secret version");
}
private void assertSecretData(SecretResponse res) {
assertNotNull(res, "Parsed response is NULL");
assertEquals(SECRET_REQUEST_ID, res.getRequestId(), "Incorrect request ID");
assertEquals(SECRET_LEASE_ID, res.getLeaseId(), "Incorrect lease ID");
assertEquals(SECRET_LEASE_DURATION, res.getLeaseDuration(), "Incorrect lease duration");
assertEquals(SECRET_RENEWABLE, res.isRenewable(), "Incorrect renewable status");
assertEquals(SECRET_WARNINGS, res.getWarnings(), "Incorrect warnings");
assertEquals(SECRET_DATA_V1, res.get(SECRET_DATA_K1), "Response does not contain correct data");
assertEquals(SECRET_DATA_V2, res.get(SECRET_DATA_K2), "Response does not contain correct data");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,17 +16,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link MetadataResponse} model.
@ -34,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.8
*/
public class MetadataResponseTest {
class MetadataResponseTest extends AbstractModelTest<MetadataResponse> {
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";
@ -69,32 +63,44 @@ public class MetadataResponseTest {
" }\n" +
"}";
MetadataResponseTest() {
super(MetadataResponse.class);
}
@Override
protected MetadataResponse createFull() {
try {
return objectMapper.readValue(META_JSON, MetadataResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
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());
}
void jsonRoundtrip() {
MetadataResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(META_JSON, MetadataResponse.class),
"MetadataResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertNotNull(res.getMetadata(), "Parsed metadata is NULL");
assertEquals(V1_TIME, res.getMetadata().getCreatedTimeString(), "Incorrect created time");
assertNotNull(res.getMetadata().getCreatedTime(), "Parting created time failed");
assertEquals(CURRENT_VERSION, res.getMetadata().getCurrentVersion(), "Incorrect current version");
assertEquals(MAX_VERSIONS, res.getMetadata().getMaxVersions(), "Incorrect max versions");
assertEquals(OLDEST_VERSION, res.getMetadata().getOldestVersion(), "Incorrect oldest version");
assertEquals(V3_TIME, res.getMetadata().getUpdatedTimeString(), "Incorrect updated time");
assertNotNull(res.getMetadata().getUpdatedTime(), "Parting updated time failed");
assertEquals(3, res.getMetadata().getVersions().size(), "Incorrect number of versions");
assertEquals(V2_TIME, res.getMetadata().getVersions().get(1).getDeletionTimeString(), "Incorrect version 1 delete time");
assertNotNull(res.getMetadata().getVersions().get(1).getDeletionTime(), "Parsing version delete time failed");
assertTrue(res.getMetadata().getVersions().get(1).isDestroyed(), "Incorrect version 1 destroyed state");
assertEquals(V2_TIME, res.getMetadata().getVersions().get(2).getCreatedTimeString(), "Incorrect version 2 created time");
assertNotNull(res.getMetadata().getVersions().get(2).getCreatedTime(), "Parsing version created failed");
assertFalse(res.getMetadata().getVersions().get(3).isDestroyed(), "Incorrect version 3 destroyed state");
}
}

View File

@ -0,0 +1,223 @@
/*
* Copyright 2016-2021 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* 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.JsonProperty;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link PlainSecretResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
class PlainSecretResponseTest extends AbstractModelTest<PlainSecretResponse> {
private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd";
private static final String SECRET_LEASE_ID = "";
private static final Integer SECRET_LEASE_DURATION = 2764800;
private static final boolean SECRET_RENEWABLE = false;
private static final String SECRET_DATA_K1 = "excited";
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 List<String> SECRET_WARNINGS = null;
private static final String SECRET_JSON = "{\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" +
" \"" + SECRET_DATA_K1 + "\": \"" + SECRET_DATA_V1 + "\",\n" +
" \"" + SECRET_DATA_K2 + "\": \"" + SECRET_DATA_V2 + "\"\n" +
" },\n" +
" \"warnings\": " + SECRET_WARNINGS + "\n" +
"}";
PlainSecretResponseTest() {
super(PlainSecretResponse.class);
}
@Override
protected PlainSecretResponse createFull() {
try {
return objectMapper.readValue(SECRET_JSON, PlainSecretResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
void jsonRoundtrip() {
SecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(SECRET_JSON, PlainSecretResponse.class),
"SecretResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(SECRET_REQUEST_ID, res.getRequestId(), "Incorrect request ID");
assertEquals(SECRET_LEASE_ID, res.getLeaseId(), "Incorrect lease ID");
assertEquals(SECRET_LEASE_DURATION, res.getLeaseDuration(), "Incorrect lease duration");
assertEquals(SECRET_RENEWABLE, res.isRenewable(), "Incorrect renewable status");
assertEquals(SECRET_WARNINGS, res.getWarnings(), "Incorrect warnings");
assertEquals(SECRET_DATA_V1, res.get(SECRET_DATA_K1), "Response does not contain correct data");
assertEquals(SECRET_DATA_V2, res.get(SECRET_DATA_K2), "Response does not contain correct data");
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
void testGetter() {
final var stringKey = "string";
final var stringVal = "test";
final var numberKey = "number";
final var numberVal = 123.45;
final var listKey = "list";
final var listVal = List.of("foo", "bar");
final var complexKey = "complex";
final var complexVal = new ComplexType("val1", 678);
SecretResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(
"{\n" +
" \"request_id\": \"req-id\",\n" +
" \"lease_id\": \"lea-id\",\n" +
" \"lease_duration\": " + 123456 + ",\n" +
" \"renewable\": true,\n" +
" \"data\": {\n" +
" \"" + stringKey + "\": \"" + stringVal + "\",\n" +
" \"" + numberKey + "\": \"" + numberVal + "\",\n" +
" \"" + listKey + "\": [\"" + String.join("\", \"", listVal) + "\"],\n" +
" \"" + complexKey + "\": {" +
" \"field1\": \"" + complexVal.field1 + "\",\n" +
" \"field2\": " + complexVal.field2 + "\n" +
" },\n" +
" \"" + complexKey + "Json\": \"" + objectMapper.writeValueAsString(complexVal).replace("\"", "\\\"") + "\"\n" +
" }\n" +
"}",
PlainSecretResponse.class
),
"SecretResponse deserialization failed"
);
assertEquals(stringVal, res.get(stringKey), "unexpected value for string (implicit)");
assertEquals(
stringVal,
assertDoesNotThrow(() -> res.get(stringKey, String.class), "getting string failed"),
"unexpected value for string (explicit)"
);
assertEquals(String.valueOf(numberVal), res.get(numberKey), "unexpected value for number (implicit)");
assertEquals(
numberVal,
assertDoesNotThrow(() -> res.get(numberKey, Double.class), "getting number failed"),
"unexpected value for number (explicit)"
);
assertEquals(
String.valueOf(numberVal),
assertDoesNotThrow(() -> res.get(numberKey, String.class), "getting number as string failed"),
"unexpected value for number as string (explicit)"
);
assertEquals(listVal, res.get(listKey), "unexpected value for list (implicit)");
assertEquals(
listVal,
assertDoesNotThrow(() -> res.get(listKey, ArrayList.class), "getting list failed"),
"unexpected value for list (explicit)"
);
assertEquals(complexVal.toMap(), res.get(complexKey), "unexpected value for complex type (implicit)");
assertEquals(
complexVal.toMap(),
assertDoesNotThrow(() -> res.get(complexKey, HashMap.class), "getting complex type as map failed"),
"unexpected value for complex type as map (explicit)"
);
assertEquals(
complexVal,
assertDoesNotThrow(() -> res.get(complexKey, ComplexType.class), "getting complex type failed"),
"unexpected value for complex type (explicit)"
);
assertThrows(
InvalidResponseException.class,
() -> res.get(complexKey, Integer.class),
"getting complex type as integer should fail"
);
assertEquals(
complexVal,
assertDoesNotThrow(() -> res.get(complexKey + "Json", ComplexType.class), "getting complex type from JSON string failed"),
"unexpected value for complex type from JSON string"
);
}
/**
* Test class for complex field mapping.
*/
private static class ComplexType {
@JsonProperty("field1")
private String field1;
@JsonProperty("field2")
private Integer field2;
private ComplexType() {
// Required for JSON deserialization.
}
private ComplexType(String field1, Integer field2) {
this.field1 = field1;
this.field2 = field2;
}
private Map<String, Object> toMap() {
return Map.of(
"field1", field1,
"field2", field2
);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
} else if (o == null || getClass() != o.getClass()) {
return false;
}
ComplexType that = (ComplexType) o;
return Objects.equals(field1, that.field1) && Objects.equals(field2, that.field2);
}
@Override
public int hashCode() {
return Objects.hash(field1, field2);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link SealResponse} model.
@ -31,16 +28,19 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SealResponseTest {
class SealResponseTest extends AbstractModelTest<SealResponse> {
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 VERSION = "1.8.2";
private static final String CLUSTER_NAME = "vault-cluster-d6ec3c7f";
private static final String CLUSTER_ID = "3e8b3fec-3749-e056-ba41-b62a63b997e8";
private static final String NONCE = "ef05d55d-4d2c-c594-a5e8-55bc88604c24";
private static final Boolean MIGRATION = false;
private static final Boolean RECOVERY_SEAL = false;
private static final String STORAGE_TYPE = "file";
private static final String RES_SEALED = "{\n" +
" \"type\": \"" + TYPE + "\",\n" +
@ -50,7 +50,10 @@ public class SealResponseTest {
" \"n\": " + SHARES + ",\n" +
" \"progress\": " + PROGRESS_SEALED + ",\n" +
" \"nonce\": \"\",\n" +
" \"version\": \"" + VERSION + "\"\n" +
" \"version\": \"" + VERSION + "\",\n" +
" \"migration\": \"" + MIGRATION + "\",\n" +
" \"recovery_seal\": \"" + RECOVERY_SEAL + "\",\n" +
" \"storage_type\": \"" + STORAGE_TYPE + "\"\n" +
"}";
private static final String RES_UNSEALED = "{\n" +
@ -63,50 +66,71 @@ public class SealResponseTest {
" \"version\": \"" + VERSION + "\",\n" +
" \"cluster_name\": \"" + CLUSTER_NAME + "\",\n" +
" \"cluster_id\": \"" + CLUSTER_ID + "\",\n" +
" \"nonce\": \"" + NONCE + "\"\n" +
" \"nonce\": \"" + NONCE + "\",\n" +
" \"migration\": \"" + MIGRATION + "\",\n" +
" \"recovery_seal\": \"" + RECOVERY_SEAL + "\",\n" +
" \"storage_type\": \"" + STORAGE_TYPE + "\"\n" +
"}";
SealResponseTest() {
super(SealResponse.class);
}
@Override
protected SealResponse createFull() {
try {
return objectMapper.readValue(RES_UNSEALED, SealResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault when sealed (JSON example close to Vault documentation).
*/
@Test
public void jsonRoundtripSealed() {
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());
}
SealResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_SEALED, SealResponse.class),
"SealResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(TYPE, res.getType(), "Incorrect seal type");
assertTrue(res.isSealed(), "Incorrect seal status");
assertTrue(res.isInitialized(), "Incorrect initialization status");
assertEquals(THRESHOLD, res.getThreshold(), "Incorrect threshold");
assertEquals(SHARES, res.getNumberOfShares(), "Incorrect number of shares");
assertEquals(PROGRESS_SEALED, res.getProgress(), "Incorrect progress");
assertEquals("", res.getNonce(), "Nonce not empty");
assertEquals(VERSION, res.getVersion(), "Incorrect version");
assertEquals(MIGRATION, res.getMigration(), "Incorrect migration");
assertEquals(RECOVERY_SEAL, res.getRecoverySeal(), "Incorrect recovery seal");
assertEquals(STORAGE_TYPE, res.getStorageType(), "Incorrect storage type");
// And the fields, that should not be filled.
assertNull(res.getClusterName(), "Cluster name should not be populated");
assertNull(res.getClusterId(), "Cluster ID should not be populated");
// 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());
}
res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_UNSEALED, SealResponse.class),
"SealResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(TYPE, res.getType(), "Incorrect seal type");
assertFalse(res.isSealed(), "Incorrect seal status");
assertTrue(res.isInitialized(), "Incorrect initialization status");
assertEquals(THRESHOLD, res.getThreshold(), "Incorrect threshold");
assertEquals(SHARES, res.getNumberOfShares(), "Incorrect number of shares");
assertEquals(PROGRESS_UNSEALED, res.getProgress(), "Incorrect progress");
assertEquals(NONCE, res.getNonce(), "Incorrect nonce");
assertEquals(VERSION, res.getVersion(), "Incorrect version");
assertEquals(CLUSTER_NAME, res.getClusterName(), "Incorrect cluster name");
assertEquals(CLUSTER_ID, res.getClusterId(), "Incorrect cluster ID");
assertEquals(MIGRATION, res.getMigration(), "Incorrect migration");
assertEquals(RECOVERY_SEAL, res.getRecoverySeal(), "Incorrect recovery seal");
assertEquals(STORAGE_TYPE, res.getStorageType(), "Incorrect storage type");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,17 +16,13 @@
package de.stklcode.jvault.connector.model.response;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link SecretListResponse} model.
@ -34,42 +30,46 @@ import static org.junit.Assert.fail;
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SecretListResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
class SecretListResponseTest extends AbstractModelTest<SecretListResponse> {
private static final String KEY1 = "key1";
private static final String KEY2 = "key-2";
private static final List<String> KEYS = Arrays.asList(KEY1, KEY2);
private static final String JSON = "{\n" +
" \"auth\": null,\n" +
" \"data\": {\n" +
" \"keys\": [" +
" \"" + KEY1 + "\",\n" +
" \"" + KEY2 + "\"\n" +
" ]\n" +
" },\n" +
" \"lease_duration\": 2764800,\n" +
" \"lease_id\": \"\",\n" +
" \"renewable\": false\n" +
"}";
static {
DATA.put("keys", KEYS);
SecretListResponseTest() {
super(SecretListResponse.class);
}
@Override
protected SecretListResponse createFull() {
try {
return objectMapper.readValue(JSON, SecretListResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
* Test JSON deserialization and key getter.
*/
@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()));
void getKeysTest() {
SecretListResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(JSON, SecretListResponse.class),
"SecretListResponse deserialization failed"
);
// 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));
assertEquals(List.of(KEY1, KEY2), res.getKeys(), "Unexpected secret keys");
}
}

View File

@ -1,206 +0,0 @@
/*
* 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 SecretResponse} model.
*
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class SecretResponseTest {
private static final Map<String, Object> DATA = new HashMap<>();
private static final String KEY_UNKNOWN = "unknown";
private static final String KEY_STRING = "test1";
private static final String VAL_STRING = "testvalue";
private static final String KEY_INTEGER = "test2";
private static final Integer VAL_INTEGER = 42;
private static final String KEY_LIST = "list";
private static final String VAL_LIST = "[\"first\",\"second\"]";
private static final String SECRET_REQUEST_ID = "68315073-6658-e3ff-2da7-67939fb91bbd";
private static final String SECRET_LEASE_ID = "";
private static final Integer SECRET_LEASE_DURATION = 2764800;
private static final boolean SECRET_RENEWABLE = false;
private static final String SECRET_DATA_K1 = "excited";
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" +
" \"lease_id\": \"" + SECRET_LEASE_ID + "\",\n" +
" \"lease_duration\": " + SECRET_LEASE_DURATION + ",\n" +
" \"renewable\": " + SECRET_RENEWABLE + ",\n" +
" \"data\": {\n" +
" \"" + SECRET_DATA_K1 + "\": \"" + SECRET_DATA_V1 + "\",\n" +
" \"" + SECRET_DATA_K2 + "\": \"" + SECRET_DATA_V2 + "\"\n" +
" },\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 {
DATA.put(KEY_STRING, VAL_STRING);
DATA.put(KEY_INTEGER, VAL_INTEGER);
DATA.put(KEY_LIST, VAL_LIST);
}
/**
* Test getter, setter and get-methods for response data.
*
* @throws InvalidResponseException Should not occur
*/
@Test
@SuppressWarnings("unchecked")
public void getDataRoundtrip() throws InvalidResponseException {
// Create empty Object.
SecretResponse res = new SecretResponse();
assertThat("Initial data should be Map", res.getData(), is(instanceOf(Map.class)));
assertThat("Initial data should be empty", res.getData().entrySet(), empty());
assertThat("Getter should return NULL on empty data map", res.get(KEY_STRING), is(nullValue()));
// Fill data map.
res.setData(DATA);
assertThat("Data setter/getter not transparent", res.getData(), is(DATA));
assertThat("Data size modified", res.getData().keySet(), hasSize(DATA.size()));
assertThat("Data keys not passed correctly", res.getData().keySet(), containsInAnyOrder(KEY_STRING, KEY_INTEGER, KEY_LIST));
assertThat("Data values not passed correctly", res.get(KEY_STRING), is(VAL_STRING));
assertThat("Data values not passed correctly", res.get(KEY_INTEGER), is(VAL_INTEGER));
assertThat("Non-Null returned on unknown key", res.get(KEY_UNKNOWN), is(nullValue()));
// Try explicit JSON conversion.
final List list = res.get(KEY_LIST, List.class);
assertThat("JSON parsing of list failed", list, is(notNullValue()));
assertThat("JSON parsing of list returned incorrect size", list.size(), is(2));
assertThat("JSON parsing of list returned incorrect elements", (List<Object>)list, contains("first", "second"));
assertThat("Non-Null returned on unknown key", res.get(KEY_UNKNOWN, Object.class), is(nullValue()));
// Requesting invalid class should result in Exception.
try {
res.get(KEY_LIST, Double.class);
fail("JSON parsing to incorrect type succeeded.");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
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

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,15 +16,11 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link SecretVersionResponse} model.
@ -32,7 +28,7 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.8
*/
public class SecretVersionResponseTest {
class SecretVersionResponseTest extends AbstractModelTest<SecretVersionResponse> {
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;
@ -46,21 +42,34 @@ public class SecretVersionResponseTest {
" }\n" +
"}";
SecretVersionResponseTest() {
super(SecretVersionResponse.class);
}
@Override
protected SecretVersionResponse createFull() {
try {
return objectMapper.readValue(META_JSON, SecretVersionResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
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());
}
void jsonRoundtrip() {
SecretVersionResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(META_JSON, SecretVersionResponse.class),
"SecretVersionResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertNotNull(res.getMetadata(), "Parsed metadata is NULL");
assertEquals(CREATION_TIME, res.getMetadata().getCreatedTimeString(), "Incorrect created time");
assertEquals(DELETION_TIME, res.getMetadata().getDeletionTimeString(), "Incorrect deletion time");
assertFalse(res.getMetadata().isDestroyed(), "Incorrect destroyed state");
assertEquals(VERSION, res.getMetadata().getVersion(), "Incorrect version");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,18 +16,16 @@
package de.stklcode.jvault.connector.model.response;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.stklcode.jvault.connector.exception.InvalidResponseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import de.stklcode.jvault.connector.model.AbstractModelTest;
import de.stklcode.jvault.connector.model.response.embedded.TokenData;
import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.util.HashMap;
import java.time.ZonedDateTime;
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;
import static org.junit.jupiter.api.Assertions.*;
/**
* JUnit Test for {@link TokenResponse} model.
@ -35,28 +33,46 @@ import static org.junit.jupiter.api.Assertions.fail;
* @author Stefan Kalscheuer
* @since 0.6.2
*/
public class TokenResponseTest {
class TokenResponseTest extends AbstractModelTest<TokenResponse> {
private static final Integer TOKEN_CREATION_TIME = 1457533232;
private static final Integer TOKEN_TTL = 2764800;
private static final Integer TOKEN_EXPLICIT_MAX_TTL = 0;
private static final String TOKEN_DISPLAY_NAME = "token";
private static final String TOKEN_META_KEY = "foo";
private static final String TOKEN_META_VALUE = "bar";
private static final Integer TOKEN_NUM_USES = 0;
private static final Boolean TOKEN_ORPHAN = false;
private static final Boolean TOKEN_RENEWABLE = true;
private static final String TOKEN_PATH = "auth/token/create";
private static final String TOKEN_POLICY_1 = "default";
private static final String TOKEN_POLICY_2 = "web";
private static final Boolean RES_RENEWABLE = false;
private static final Integer RES_TTL = 2591976;
private static final Integer RES_LEASE_DURATION = 0;
private static final String TOKEN_ACCESSOR = "VKvzT2fKHFsZFUus9LyoXCvu";
private static final String TOKEN_ENTITY_ID = "7d2e3179-f69b-450c-7179-ac8ee8bd8ca9";
private static final String TOKEN_EXPIRE_TIME = "2018-05-19T11:35:54.466476215-04:00";
private static final String TOKEN_ID = "my-token";
private static final String TOKEN_ISSUE_TIME = "2018-04-17T11:35:54.466476078-04:00";
private static final String TOKEN_TYPE = "service";
private static final String RES_JSON = "{\n" +
" \"lease_id\": \"\",\n" +
" \"renewable\": " + RES_RENEWABLE + ",\n" +
" \"lease_duration\": " + RES_LEASE_DURATION + ",\n" +
" \"data\": {\n" +
" \"accessor\": \"" + TOKEN_ACCESSOR + "\",\n" +
" \"creation_time\": " + TOKEN_CREATION_TIME + ",\n" +
" \"creation_ttl\": " + TOKEN_TTL + ",\n" +
" \"display_name\": \"" + TOKEN_DISPLAY_NAME + "\",\n" +
" \"meta\": null,\n" +
" \"entity_id\": \"" + TOKEN_ENTITY_ID + "\",\n" +
" \"expire_time\": \"" + TOKEN_EXPIRE_TIME + "\",\n" +
" \"explicit_max_ttl\": \"" + TOKEN_EXPLICIT_MAX_TTL + "\",\n" +
" \"id\": \"" + TOKEN_ID + "\",\n" +
" \"issue_time\": \"" + TOKEN_ISSUE_TIME + "\",\n" +
" \"meta\": {\n" +
" \"" + TOKEN_META_KEY + "\": \"" + TOKEN_META_VALUE + "\"\n" +
" },\n" +
" \"num_uses\": " + TOKEN_NUM_USES + ",\n" +
" \"orphan\": " + TOKEN_ORPHAN + ",\n" +
" \"path\": \"" + TOKEN_PATH + "\",\n" +
@ -64,60 +80,73 @@ public class TokenResponseTest {
" \"" + TOKEN_POLICY_1 + "\", \n" +
" \"" + TOKEN_POLICY_2 + "\"\n" +
" ],\n" +
" \"ttl\": " + RES_TTL + "\n" +
" \"renewable\": " + TOKEN_RENEWABLE + ",\n" +
" \"ttl\": " + RES_TTL + ",\n" +
" \"type\": \"" + TOKEN_TYPE + "\"\n" +
" },\n" +
" \"warnings\": null,\n" +
" \"auth\": null\n" +
"}";
private static final Map<String, Object> INVALID_TOKEN_DATA = new HashMap<>();
TokenResponseTest() {
super(TokenResponse.class);
}
static {
INVALID_TOKEN_DATA.put("num_uses", "fourtytwo");
@Override
protected TokenResponse createFull() {
try {
return objectMapper.readValue(RES_JSON, TokenResponse.class);
} catch (JsonProcessingException e) {
fail("Creation of full model instance failed", e);
return null;
}
}
/**
* Test getter, setter and get-methods for response data.
*/
@Test
public void getDataRoundtrip() {
void getDataRoundtrip() {
// Create empty Object.
TokenResponse res = new TokenResponse();
assertThat("Initial data should be empty", res.getData(), is(nullValue()));
// Parsing invalid data map should fail.
try {
res.setData(INVALID_TOKEN_DATA);
fail("Parsing invalid token data succeeded");
} catch (Exception e) {
assertThat(e, is(instanceOf(InvalidResponseException.class)));
}
assertNull(res.getData(), "Initial data should be empty");
}
/**
* Test creation from JSON value as returned by Vault (JSON example copied from Vault documentation).
*/
@Test
public void jsonRoundtrip() {
try {
TokenResponse res = new ObjectMapper().readValue(RES_JSON, TokenResponse.class);
assertThat("Parsed response is NULL", res, is(notNullValue()));
assertThat("Incorrect lease duration", res.getLeaseDuration(), is(RES_LEASE_DURATION));
assertThat("Incorrect renewable status", res.isRenewable(), is(RES_RENEWABLE));
// Extract token data.
TokenData data = res.getData();
assertThat("Token data is NULL", data, is(notNullValue()));
assertThat("Incorrect token creation time", data.getCreationTime(), is(TOKEN_CREATION_TIME));
assertThat("Incorrect token creation TTL", data.getCreationTtl(), is(TOKEN_TTL));
assertThat("Incorrect token display name", data.getName(), is(TOKEN_DISPLAY_NAME));
assertThat("Incorrect token number of uses", data.getNumUses(), is(TOKEN_NUM_USES));
assertThat("Incorrect token orphan flag", data.isOrphan(), is(TOKEN_ORPHAN));
assertThat("Incorrect token path", data.getPath(), is(TOKEN_PATH));
assertThat("Incorrect response renewable flag", res.isRenewable(), is(RES_RENEWABLE));
assertThat("Incorrect response TTL", data.getTtl(), is(RES_TTL));
assertThat("Incorrect response lease duration", res.getLeaseDuration(), is(RES_LEASE_DURATION));
} catch (IOException e) {
fail("TokenResponse deserialization failed: " + e.getMessage());
}
void jsonRoundtrip() {
TokenResponse res = assertDoesNotThrow(
() -> objectMapper.readValue(RES_JSON, TokenResponse.class),
"TokenResponse deserialization failed"
);
assertNotNull(res, "Parsed response is NULL");
assertEquals(RES_LEASE_DURATION, res.getLeaseDuration(), "Incorrect lease duration");
assertEquals(RES_RENEWABLE, res.isRenewable(), "Incorrect response renewable flag");
assertEquals(RES_LEASE_DURATION, res.getLeaseDuration(), "Incorrect response lease duration");
// Extract token data.
TokenData data = res.getData();
assertNotNull(data, "Token data is NULL");
assertEquals(TOKEN_ACCESSOR, data.getAccessor(), "Incorrect token accessor");
assertEquals(TOKEN_CREATION_TIME, data.getCreationTime(), "Incorrect token creation time");
assertEquals(TOKEN_TTL, data.getCreationTtl(), "Incorrect token creation TTL");
assertEquals(TOKEN_DISPLAY_NAME, data.getName(), "Incorrect token display name");
assertEquals(TOKEN_ENTITY_ID, data.getEntityId(), "Incorrect token entity ID");
assertEquals(TOKEN_EXPIRE_TIME, data.getExpireTimeString(), "Incorrect token expire time");
assertEquals(ZonedDateTime.parse(TOKEN_EXPIRE_TIME), data.getExpireTime(), "Incorrect parsed token expire time");
assertEquals(TOKEN_EXPLICIT_MAX_TTL, data.getExplicitMaxTtl(), "Incorrect token explicit max TTL");
assertEquals(TOKEN_ID, data.getId(), "Incorrect token ID");
assertEquals(TOKEN_ISSUE_TIME, data.getIssueTimeString(), "Incorrect token issue time");
assertEquals(ZonedDateTime.parse(TOKEN_ISSUE_TIME), data.getIssueTime(), "Incorrect parsed token issue time");
assertEquals(Map.of(TOKEN_META_KEY, TOKEN_META_VALUE), data.getMeta(), "Incorrect token metadata");
assertEquals(TOKEN_NUM_USES, data.getNumUses(), "Incorrect token number of uses");
assertEquals(TOKEN_ORPHAN, data.isOrphan(), "Incorrect token orphan flag");
assertEquals(TOKEN_PATH, data.getPath(), "Incorrect token path");
assertEquals(2, data.getPolicies().size(), "Incorrect number of token policies");
assertTrue(data.getPolicies().containsAll(List.of(TOKEN_POLICY_1, TOKEN_POLICY_2)), "Incorrect token policies");
assertEquals(TOKEN_RENEWABLE, data.isRenewable(), "Incorrect token renewable flag");
assertEquals(RES_TTL, data.getTtl(), "Incorrect token TTL");
assertEquals(TOKEN_TYPE, data.getType(), "Incorrect token type");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016-2019 Stefan Kalscheuer
* Copyright 2016-2023 Stefan Kalscheuer
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -21,8 +21,8 @@ import com.fasterxml.jackson.annotation.JsonProperty;
/**
* Simple credentials class for JSON testing.
*
* @author Stefan Kalscheuer
* @since 0.1
* @author Stefan Kalscheuer
* @since 0.1
*/
public class Credentials {
@JsonProperty("username")

View File

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

View File

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

View File

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

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