Compare commits
4 Commits
feat/split
...
main
Author | SHA1 | Date | |
---|---|---|---|
635cf19e54
|
|||
f5e40ca032
|
|||
15f514f877
|
|||
f79ed98986
|
4
.github/workflows/ci-it.yml
vendored
4
.github/workflows/ci-it.yml
vendored
@@ -15,10 +15,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
jdk: [ 11, 17, 21 ]
|
jdk: [ 11, 17, 21 ]
|
||||||
vault: [ '1.2.0', '1.20.0' ]
|
vault: [ '1.2.0', '1.20.3' ]
|
||||||
include:
|
include:
|
||||||
- jdk: 21
|
- jdk: 21
|
||||||
vault: '1.20.0'
|
vault: '1.20.3'
|
||||||
analysis: true
|
analysis: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
@@ -1,14 +1,17 @@
|
|||||||
## unreleased
|
## 1.5.3 (2025-09-09)
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* Updated Jackson to 2.20.0 (#106)
|
* Updated Jackson to 2.20.0 (#106)
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
* Extract API paths into a utility class (#108)
|
* Extract API paths into a utility class (#108)
|
||||||
|
* Encode user-provided URL parts (#109)
|
||||||
|
* Add `token_bound_cidrs` field to `AppRoleSecret` model (#110)
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
* Prevent potential off-by-1 error in internal `mapOf()` helper (#107)
|
* Prevent potential off-by-1 error in internal `mapOf()` helper (#107)
|
||||||
|
|
||||||
|
|
||||||
## 1.5.2 (2025-07-16)
|
## 1.5.2 (2025-07-16)
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
@@ -40,7 +40,7 @@ Java Vault Connector is a connector library for [Vault](https://www.vaultproject
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>de.stklcode.jvault</groupId>
|
<groupId>de.stklcode.jvault</groupId>
|
||||||
<artifactId>jvault-connector</artifactId>
|
<artifactId>jvault-connector</artifactId>
|
||||||
<version>1.5.2</version>
|
<version>1.5.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -109,11 +109,11 @@ Token token = Token.builder()
|
|||||||
.withDisplayName("new test token")
|
.withDisplayName("new test token")
|
||||||
.withPolicies("pol1", "pol2")
|
.withPolicies("pol1", "pol2")
|
||||||
.build();
|
.build();
|
||||||
vault.token().create(token);
|
vault.createToken(token);
|
||||||
|
|
||||||
// Create AppRole credentials
|
// Create AppRole credentials
|
||||||
vault.appRole().create("testrole", policyList);
|
vault.createAppRole("testrole", policyList);
|
||||||
AppRoleSecretResponse secret = vault.appRole().createSecret("testrole");
|
AppRoleSecretResponse secret = vault.createAppRoleSecret("testrole");
|
||||||
```
|
```
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
7
pom.xml
7
pom.xml
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<groupId>de.stklcode.jvault</groupId>
|
<groupId>de.stklcode.jvault</groupId>
|
||||||
<artifactId>jvault-connector</artifactId>
|
<artifactId>jvault-connector</artifactId>
|
||||||
<version>2.0.0-SNAPSHOT</version>
|
<version>1.5.3</version>
|
||||||
|
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<connection>scm:git:git://github.com/stklcode/jvaultconnector.git</connection>
|
<connection>scm:git:git://github.com/stklcode/jvaultconnector.git</connection>
|
||||||
<developerConnection>scm:git:git@github.com:stklcode/jvaultconnector.git</developerConnection>
|
<developerConnection>scm:git:git@github.com:stklcode/jvaultconnector.git</developerConnection>
|
||||||
<url>https://github.com/stklcode/jvaultconnector</url>
|
<url>https://github.com/stklcode/jvaultconnector</url>
|
||||||
<tag>HEAD</tag>
|
<tag>v1.5.3</tag>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<issueManagement>
|
<issueManagement>
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<project.build.outputTimestamp>2025-09-09T09:45:59Z</project.build.outputTimestamp>
|
||||||
<argLine />
|
<argLine />
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -178,7 +179,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||||
<artifactId>sonar-maven-plugin</artifactId>
|
<artifactId>sonar-maven-plugin</artifactId>
|
||||||
<version> 5.2.0.4988</version>
|
<version>5.2.0.4988</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</pluginManagement>
|
</pluginManagement>
|
||||||
|
@@ -1,217 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016-2025 Stefan Kalscheuer
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.stklcode.jvault.connector;
|
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|
||||||
import de.stklcode.jvault.connector.model.AppRole;
|
|
||||||
import de.stklcode.jvault.connector.model.AppRoleSecret;
|
|
||||||
import de.stklcode.jvault.connector.model.Token;
|
|
||||||
import de.stklcode.jvault.connector.model.TokenRole;
|
|
||||||
import de.stklcode.jvault.connector.model.response.*;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AppRole client interface.
|
|
||||||
* Provides methods to interact with Vault's AppRole API.
|
|
||||||
*
|
|
||||||
* @since 2.0.0 extracted from {@link VaultConnector}
|
|
||||||
*/
|
|
||||||
public interface AppRoleClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new AppRole role from given metamodel.
|
|
||||||
*
|
|
||||||
* @param role The role
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
boolean create(final AppRole role) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole role with default policy.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default boolean create(final String roleName) throws VaultConnectorException {
|
|
||||||
return create(roleName, new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole role with policies.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param policies The policies to associate with
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default boolean create(final String roleName, final List<String> policies) throws VaultConnectorException {
|
|
||||||
return create(roleName, policies, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole role with default policy and custom ID.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param roleID A custom role ID
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default boolean create(final String roleName, final String roleID) throws VaultConnectorException {
|
|
||||||
return create(roleName, new ArrayList<>(), roleID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole role with policies and custom ID.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param policies The policies to associate with
|
|
||||||
* @param roleID A custom role ID
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default boolean create(final String roleName, final List<String> policies, final String roleID)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
return create(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete AppRole role from Vault.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
boolean delete(final String roleName) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup an AppRole role.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return Result of the lookup
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
AppRoleResponse lookup(final String roleName) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve ID for an AppRole role.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return The role ID
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
String getRoleID(final String roleName) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set custom ID for an AppRole role.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param roleID The role ID
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
boolean setRoleID(final String roleName, final String roleID) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new random generated AppRole secret.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return The secret ID
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default AppRoleSecretResponse createSecret(final String roleName) throws VaultConnectorException {
|
|
||||||
return createSecret(roleName, new AppRoleSecret());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole secret with custom ID.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param secretID A custom secret ID
|
|
||||||
* @return The secret ID
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
default AppRoleSecretResponse createSecret(final String roleName, final String secretID)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
return createSecret(roleName, new AppRoleSecret(secretID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register new AppRole secret with custom ID.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param secret The secret meta object
|
|
||||||
* @return The secret ID
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
AppRoleSecretResponse createSecret(final String roleName, final AppRoleSecret secret)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup an AppRole secret.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param secretID The secret ID
|
|
||||||
* @return Result of the lookup
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
AppRoleSecretResponse lookupSecret(final String roleName, final String secretID)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy an AppRole secret.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @param secretID The secret meta object
|
|
||||||
* @return The secret ID
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.4.0
|
|
||||||
*/
|
|
||||||
boolean destroySecret(final String roleName, final String secretID) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List existing (accessible) AppRole roles.
|
|
||||||
*
|
|
||||||
* @return List of roles
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
List<String> listRoles() throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List existing (accessible) secret IDs for AppRole role.
|
|
||||||
*
|
|
||||||
* @param roleName The role name
|
|
||||||
* @return List of roles
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
List<String> listSecrets(final String roleName) throws VaultConnectorException;
|
|
||||||
}
|
|
@@ -31,6 +31,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static de.stklcode.jvault.connector.internal.RequestHelper.encode;
|
||||||
import static de.stklcode.jvault.connector.internal.VaultApiPath.*;
|
import static de.stklcode.jvault.connector.internal.VaultApiPath.*;
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
@@ -106,11 +107,55 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
authorized = false;
|
authorized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SealResponse sealStatus() throws VaultConnectorException {
|
||||||
|
return request.get(SYS_SEAL_STATUS, emptyMap(), token, SealResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void seal() throws VaultConnectorException {
|
||||||
|
request.put(SYS_SEAL, emptyMap(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
|
||||||
|
Map<String, String> param = mapOfStrings(
|
||||||
|
"key", key,
|
||||||
|
"reset", reset
|
||||||
|
);
|
||||||
|
|
||||||
|
return request.put(SYS_UNSEAL, param, token, SealResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HealthResponse getHealth() throws VaultConnectorException {
|
||||||
|
|
||||||
|
return request.get(
|
||||||
|
SYS_HEALTH,
|
||||||
|
// Force status code to be 200, so we don't need to modify the request sequence.
|
||||||
|
Map.of(
|
||||||
|
"standbycode", "200", // Default: 429.
|
||||||
|
"sealedcode", "200", // Default: 503.
|
||||||
|
"uninitcode", "200" // Default: 501.
|
||||||
|
),
|
||||||
|
token,
|
||||||
|
HealthResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean isAuthorized() {
|
public final boolean isAuthorized() {
|
||||||
return authorized && (tokenTTL == 0 || tokenTTL >= System.currentTimeMillis());
|
return authorized && (tokenTTL == 0 || tokenTTL >= System.currentTimeMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
|
||||||
|
/* Issue request and parse response */
|
||||||
|
AuthMethodsResponse amr = request.get(SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
|
||||||
|
|
||||||
|
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final TokenResponse authToken(final String token) throws VaultConnectorException {
|
public final TokenResponse authToken(final String token) throws VaultConnectorException {
|
||||||
/* set token */
|
/* set token */
|
||||||
@@ -126,7 +171,7 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
public final AuthResponse authUserPass(final String username, final String password)
|
public final AuthResponse authUserPass(final String username, final String password)
|
||||||
throws VaultConnectorException {
|
throws VaultConnectorException {
|
||||||
final Map<String, String> payload = singletonMap("password", password);
|
final Map<String, String> payload = singletonMap("password", password);
|
||||||
return queryAuth(AUTH_USERPASS_LOGIN + username, payload);
|
return queryAuth(AUTH_USERPASS_LOGIN + encode(username), payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -135,7 +180,7 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
"role_id", roleID,
|
"role_id", roleID,
|
||||||
"secret_id", secretID
|
"secret_id", secretID
|
||||||
);
|
);
|
||||||
return queryAuth(AUTH_APPROLE + LOGIN, payload);
|
return queryAuth(AUTH_APPROLE + "login", payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -158,6 +203,142 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
return auth;
|
return auth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean createAppRole(final AppRole role) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Issue request and expect code 204 with empty response */
|
||||||
|
request.postWithoutResponse(AUTH_APPROLE_ROLE + encode(role.getName()), role, token);
|
||||||
|
|
||||||
|
/* Set custom ID if provided */
|
||||||
|
return !(role.getId() != null && !role.getId().isEmpty()) || setAppRoleID(role.getName(), role.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
/* Request HTTP response and parse Secret */
|
||||||
|
return request.get(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName),
|
||||||
|
emptyMap(),
|
||||||
|
token,
|
||||||
|
AppRoleResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean deleteAppRole(final String roleName) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Issue request and expect code 204 with empty response */
|
||||||
|
request.deleteWithoutResponse(AUTH_APPROLE_ROLE + encode(roleName), token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getAppRoleID(final String roleName) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
/* Issue request, parse response and extract Role ID */
|
||||||
|
return request.get(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/role-id",
|
||||||
|
emptyMap(),
|
||||||
|
token,
|
||||||
|
RawDataResponse.class
|
||||||
|
).getData().get("role_id").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean setAppRoleID(final String roleName, final String roleID) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Issue request and expect code 204 with empty response */
|
||||||
|
request.postWithoutResponse(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/role-id",
|
||||||
|
singletonMap("role_id", roleID),
|
||||||
|
token
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
if (secret.getId() != null && !secret.getId().isEmpty()) {
|
||||||
|
return request.post(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/custom-secret-id",
|
||||||
|
secret,
|
||||||
|
token,
|
||||||
|
AppRoleSecretResponse.class
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return request.post(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/secret-id",
|
||||||
|
secret, token,
|
||||||
|
AppRoleSecretResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Issue request and parse secret response */
|
||||||
|
return request.post(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/secret-id/lookup",
|
||||||
|
new AppRoleSecret(secretID),
|
||||||
|
token,
|
||||||
|
AppRoleSecretResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean destroyAppRoleSecret(final String roleName, final String secretID)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Issue request and expect code 204 with empty response */
|
||||||
|
request.postWithoutResponse(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/secret-id/destroy",
|
||||||
|
new AppRoleSecret(secretID),
|
||||||
|
token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<String> listAppRoles() throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
SecretListResponse secrets = request.get(
|
||||||
|
AUTH_APPROLE + "role?list=true",
|
||||||
|
emptyMap(),
|
||||||
|
token,
|
||||||
|
SecretListResponse.class
|
||||||
|
);
|
||||||
|
|
||||||
|
return secrets.getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
SecretListResponse secrets = request.get(
|
||||||
|
AUTH_APPROLE_ROLE + encode(roleName) + "/secret-id?list=true",
|
||||||
|
emptyMap(),
|
||||||
|
token,
|
||||||
|
SecretListResponse.class
|
||||||
|
);
|
||||||
|
|
||||||
|
return secrets.getKeys();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final SecretResponse read(final String key) throws VaultConnectorException {
|
public final SecretResponse read(final String key) throws VaultConnectorException {
|
||||||
requireAuth();
|
requireAuth();
|
||||||
@@ -165,6 +346,66 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
return request.get(key, emptyMap(), token, PlainSecretResponse.class);
|
return request.get(key, emptyMap(), token, PlainSecretResponse.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
/* Request HTTP response and parse secret metadata */
|
||||||
|
Map<String, String> args = mapOfStrings("version", version);
|
||||||
|
|
||||||
|
return request.get(mount + SECRET_DATA + key, args, token, MetaSecretResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final MetadataResponse readSecretMetadata(final String mount, final String key)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Request HTTP response and parse secret metadata */
|
||||||
|
return request.get(mount + SECRET_METADATA + key, emptyMap(), token, MetadataResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateSecretMetadata(final String mount,
|
||||||
|
final String key,
|
||||||
|
final Integer maxVersions,
|
||||||
|
final boolean casRequired) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
Map<String, Object> payload = mapOf(
|
||||||
|
"max_versions", maxVersions,
|
||||||
|
"cas_required", casRequired
|
||||||
|
);
|
||||||
|
|
||||||
|
write(mount + SECRET_METADATA + key, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final SecretVersionResponse writeSecretData(final String mount,
|
||||||
|
final String key,
|
||||||
|
final Map<String, Object> data,
|
||||||
|
final Integer cas) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
if (key == null || key.isEmpty()) {
|
||||||
|
throw new InvalidRequestException("Secret path must not be empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add CAS value to options map if present.
|
||||||
|
Map<String, Object> options = mapOf("cas", cas);
|
||||||
|
|
||||||
|
/* Issue request and parse metadata response */
|
||||||
|
return request.post(
|
||||||
|
mount + SECRET_DATA + key,
|
||||||
|
Map.of(
|
||||||
|
"data", data,
|
||||||
|
"options", options
|
||||||
|
),
|
||||||
|
token,
|
||||||
|
SecretVersionResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final List<String> list(final String path) throws VaultConnectorException {
|
public final List<String> list(final String path) throws VaultConnectorException {
|
||||||
requireAuth();
|
requireAuth();
|
||||||
@@ -206,12 +447,63 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
request.deleteWithoutResponse(key, token);
|
request.deleteWithoutResponse(key, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void deleteLatestSecretVersion(final String mount, final String key) throws VaultConnectorException {
|
||||||
|
delete(mount + SECRET_DATA + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void deleteAllSecretVersions(final String mount, final String key) throws VaultConnectorException {
|
||||||
|
delete(mount + SECRET_METADATA + key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void deleteSecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
handleSecretVersions(mount, SECRET_DELETE, key, versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void undeleteSecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
handleSecretVersions(mount, SECRET_UNDELETE, key, versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void destroySecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
handleSecretVersions(mount, SECRET_DESTROY, key, versions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common method to bundle secret version operations.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param pathPart Path part to query.
|
||||||
|
* @param key Secret key.
|
||||||
|
* @param versions Versions to handle.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
private void handleSecretVersions(final String mount,
|
||||||
|
final String pathPart,
|
||||||
|
final String key,
|
||||||
|
final int... versions) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Request HTTP response and expect empty result */
|
||||||
|
Map<String, Object> payload = singletonMap("versions", versions);
|
||||||
|
|
||||||
|
/* Issue request and expect code 204 with empty response */
|
||||||
|
request.postWithoutResponse(mount + pathPart + key, payload, token);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void revoke(final String leaseID) throws VaultConnectorException {
|
public final void revoke(final String leaseID) throws VaultConnectorException {
|
||||||
requireAuth();
|
requireAuth();
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
/* Issue request and expect code 204 with empty response */
|
||||||
request.putWithoutResponse(SYS_LEASES_REVOKE + leaseID, emptyMap(), token);
|
request.putWithoutResponse(SYS_LEASES_REVOKE + encode(leaseID), emptyMap(), token);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -228,28 +520,21 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KV2ClientImpl kv2() {
|
public final AuthResponse createToken(final Token token) throws VaultConnectorException {
|
||||||
return new KV2ClientImpl();
|
return createTokenInternal(token, AUTH_TOKEN + TOKEN_CREATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TokenClientImpl token() {
|
public final AuthResponse createToken(final Token token, final boolean orphan) throws VaultConnectorException {
|
||||||
return new TokenClientImpl();
|
return createTokenInternal(token, AUTH_TOKEN + TOKEN_CREATE_ORPHAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AppRoleClient appRole() {
|
public final AuthResponse createToken(final Token token, final String role) throws VaultConnectorException {
|
||||||
return new AppRoleClientImpl();
|
if (role == null || role.isEmpty()) {
|
||||||
}
|
throw new InvalidRequestException("No role name specified.");
|
||||||
|
}
|
||||||
@Override
|
return createTokenInternal(token, AUTH_TOKEN + TOKEN_CREATE + "/" + encode(role));
|
||||||
public TransitClientImpl transit() {
|
|
||||||
return new TransitClientImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SysClientImpl sys() {
|
|
||||||
return new SysClientImpl();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -259,6 +544,124 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
tokenTTL = 0;
|
tokenTTL = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create token.
|
||||||
|
* Centralized method to handle different token creation requests.
|
||||||
|
*
|
||||||
|
* @param token the token
|
||||||
|
* @param path request path
|
||||||
|
* @return the response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
private AuthResponse createTokenInternal(final Token token, final String path) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
if (token == null) {
|
||||||
|
throw new InvalidRequestException("Token must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.post(path, token, this.token, AuthResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TokenResponse lookupToken(final String token) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
/* Request HTTP response and parse Secret */
|
||||||
|
return request.get(
|
||||||
|
AUTH_TOKEN + TOKEN_LOOKUP,
|
||||||
|
singletonMap("token", token),
|
||||||
|
token,
|
||||||
|
TokenResponse.class
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
throw new InvalidRequestException("Role name must be provided.");
|
||||||
|
} else if (role == null) {
|
||||||
|
throw new InvalidRequestException("Role must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue request and expect code 204 with empty response.
|
||||||
|
request.postWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + encode(name), role, token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
// Request HTTP response and parse response.
|
||||||
|
return request.get(AUTH_TOKEN + TOKEN_ROLES + "/" + encode(name), emptyMap(), token, TokenRoleResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> listTokenRoles() throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
return list(AUTH_TOKEN + TOKEN_ROLES);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean deleteTokenRole(final String name) throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
if (name == null) {
|
||||||
|
throw new InvalidRequestException("Role name must be provided.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue request and expect code 204 with empty response.
|
||||||
|
request.deleteWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + encode(name), token);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TransitResponse transitEncrypt(final String keyName, final String plaintext)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
Map<String, Object> payload = mapOf(
|
||||||
|
"plaintext", plaintext
|
||||||
|
);
|
||||||
|
|
||||||
|
return request.post(TRANSIT_ENCRYPT + encode(keyName), payload, token, TransitResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TransitResponse transitDecrypt(final String keyName, final String ciphertext)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
Map<String, Object> payload = mapOf(
|
||||||
|
"ciphertext", ciphertext
|
||||||
|
);
|
||||||
|
|
||||||
|
return request.post(TRANSIT_DECRYPT + encode(keyName), payload, token, TransitResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final TransitResponse transitHash(final String algorithm, final String input, final String format)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
if (format != null && !"hex".equals(format) && !"base64".equals(format)) {
|
||||||
|
throw new IllegalArgumentException("Unsupported format " + format);
|
||||||
|
}
|
||||||
|
|
||||||
|
requireAuth();
|
||||||
|
|
||||||
|
Map<String, Object> payload = mapOf(
|
||||||
|
"input", input,
|
||||||
|
"format", format
|
||||||
|
);
|
||||||
|
|
||||||
|
return request.post(TRANSIT_HASH + encode(algorithm), payload, token, TransitResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for required authorization.
|
* Check for required authorization.
|
||||||
*
|
*
|
||||||
@@ -308,459 +711,4 @@ public class HTTPVaultConnector implements VaultConnector {
|
|||||||
|
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-client for KV v2 methods.
|
|
||||||
*/
|
|
||||||
public class KV2ClientImpl implements KV2Client {
|
|
||||||
@Override
|
|
||||||
public final SecretResponse readVersion(final String mount, final String key, final Integer version)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
/* Request HTTP response and parse secret metadata */
|
|
||||||
Map<String, String> args = mapOfStrings("version", version);
|
|
||||||
|
|
||||||
return request.get(mount + SECRET_DATA + key, args, token, MetaSecretResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final MetadataResponse readMetadata(final String mount, final String key)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Request HTTP response and parse secret metadata */
|
|
||||||
return request.get(mount + SECRET_METADATA + key, emptyMap(), token, MetadataResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateMetadata(final String mount,
|
|
||||||
final String key,
|
|
||||||
final Integer maxVersions,
|
|
||||||
final boolean casRequired) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
Map<String, Object> payload = mapOf(
|
|
||||||
"max_versions", maxVersions,
|
|
||||||
"cas_required", casRequired
|
|
||||||
);
|
|
||||||
|
|
||||||
write(mount + SECRET_METADATA + key, payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final SecretVersionResponse writeData(final String mount,
|
|
||||||
final String key,
|
|
||||||
final Map<String, Object> data,
|
|
||||||
final Integer cas) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
if (key == null || key.isEmpty()) {
|
|
||||||
throw new InvalidRequestException("Secret path must not be empty.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add CAS value to options map if present.
|
|
||||||
Map<String, Object> options = mapOf("cas", cas);
|
|
||||||
|
|
||||||
/* Issue request and parse metadata response */
|
|
||||||
return request.post(
|
|
||||||
mount + SECRET_DATA + key,
|
|
||||||
Map.of(
|
|
||||||
"data", data,
|
|
||||||
"options", options
|
|
||||||
),
|
|
||||||
token,
|
|
||||||
SecretVersionResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void deleteLatestVersion(final String mount, final String key) throws VaultConnectorException {
|
|
||||||
delete(mount + SECRET_DATA + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void deleteAllVersions(final String mount, final String key) throws VaultConnectorException {
|
|
||||||
delete(mount + SECRET_METADATA + key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void deleteVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
handleSecretVersions(mount, SECRET_DELETE, key, versions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void undeleteVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
handleSecretVersions(mount, SECRET_UNDELETE, key, versions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void destroyVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
handleSecretVersions(mount, SECRET_DESTROY, key, versions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Common method to bundle secret version operations.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param pathPart Path part to query.
|
|
||||||
* @param key Secret key.
|
|
||||||
* @param versions Versions to handle.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
private void handleSecretVersions(final String mount,
|
|
||||||
final String pathPart,
|
|
||||||
final String key,
|
|
||||||
final int... versions) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Request HTTP response and expect empty result */
|
|
||||||
Map<String, Object> payload = singletonMap("versions", versions);
|
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
|
||||||
request.postWithoutResponse(mount + pathPart + key, payload, token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-client for token methods.
|
|
||||||
*/
|
|
||||||
public class TokenClientImpl implements TokenClient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AuthResponse create(final Token token) throws VaultConnectorException {
|
|
||||||
return createInternal(token, AUTH_TOKEN + TOKEN_CREATE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AuthResponse create(final Token token, final boolean orphan) throws VaultConnectorException {
|
|
||||||
return createInternal(token, AUTH_TOKEN + TOKEN_CREATE_ORPHAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AuthResponse create(final Token token, final String role) throws VaultConnectorException {
|
|
||||||
if (role == null || role.isEmpty()) {
|
|
||||||
throw new InvalidRequestException("No role name specified.");
|
|
||||||
}
|
|
||||||
return createInternal(token, AUTH_TOKEN + TOKEN_CREATE + "/" + role);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create token.
|
|
||||||
* Centralized method to handle different token creation requests.
|
|
||||||
*
|
|
||||||
* @param token the token
|
|
||||||
* @param path request path
|
|
||||||
* @return the response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
private AuthResponse createInternal(final Token token, final String path) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
if (token == null) {
|
|
||||||
throw new InvalidRequestException("Token must be provided.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return request.post(path, token, HTTPVaultConnector.this.token, AuthResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final TokenResponse lookup(final String token) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Request HTTP response and parse Secret */
|
|
||||||
return request.get(
|
|
||||||
AUTH_TOKEN + TOKEN_LOOKUP,
|
|
||||||
singletonMap("token", token),
|
|
||||||
token,
|
|
||||||
TokenResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean createOrUpdateRole(final String name, final TokenRole role) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
if (name == null) {
|
|
||||||
throw new InvalidRequestException("Role name must be provided.");
|
|
||||||
} else if (role == null) {
|
|
||||||
throw new InvalidRequestException("Role must be provided.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue request and expect code 204 with empty response.
|
|
||||||
request.postWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, role, token);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TokenRoleResponse readRole(final String name) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
// Request HTTP response and parse response.
|
|
||||||
return request.get(AUTH_TOKEN + TOKEN_ROLES + "/" + name, emptyMap(), token, TokenRoleResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> listRoles() throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
return list(AUTH_TOKEN + TOKEN_ROLES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean deleteRole(final String name) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
if (name == null) {
|
|
||||||
throw new InvalidRequestException("Role name must be provided.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issue request and expect code 204 with empty response.
|
|
||||||
request.deleteWithoutResponse(AUTH_TOKEN + TOKEN_ROLES + "/" + name, token);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-client for AppRole methods.
|
|
||||||
*/
|
|
||||||
public class AppRoleClientImpl implements AppRoleClient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean create(final AppRole role) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
|
||||||
request.postWithoutResponse(String.format(AUTH_APPROLE_ROLE, role.getName(), ""), role, token);
|
|
||||||
|
|
||||||
/* Set custom ID if provided */
|
|
||||||
return !(role.getId() != null && !role.getId().isEmpty()) || setRoleID(role.getName(), role.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AppRoleResponse lookup(final String roleName) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
/* Request HTTP response and parse Secret */
|
|
||||||
return request.get(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, ""),
|
|
||||||
emptyMap(),
|
|
||||||
token,
|
|
||||||
AppRoleResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean delete(final String roleName) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
|
||||||
request.deleteWithoutResponse(String.format(AUTH_APPROLE_ROLE, roleName, ""), token);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getRoleID(final String roleName) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
/* Issue request, parse response and extract Role ID */
|
|
||||||
return request.get(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
|
|
||||||
emptyMap(),
|
|
||||||
token,
|
|
||||||
RawDataResponse.class
|
|
||||||
).getData().get("role_id").toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean setRoleID(final String roleName, final String roleID) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
|
||||||
request.postWithoutResponse(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/role-id"),
|
|
||||||
singletonMap("role_id", roleID),
|
|
||||||
token
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AppRoleSecretResponse createSecret(final String roleName, final AppRoleSecret secret)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
if (secret.getId() != null && !secret.getId().isEmpty()) {
|
|
||||||
return request.post(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/custom-secret-id"),
|
|
||||||
secret,
|
|
||||||
token,
|
|
||||||
AppRoleSecretResponse.class
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return request.post(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id"),
|
|
||||||
secret, token,
|
|
||||||
AppRoleSecretResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final AppRoleSecretResponse lookupSecret(final String roleName, final String secretID)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Issue request and parse secret response */
|
|
||||||
return request.post(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/lookup"),
|
|
||||||
new AppRoleSecret(secretID),
|
|
||||||
token,
|
|
||||||
AppRoleSecretResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean destroySecret(final String roleName, final String secretID)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
/* Issue request and expect code 204 with empty response */
|
|
||||||
request.postWithoutResponse(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id/destroy"),
|
|
||||||
new AppRoleSecret(secretID),
|
|
||||||
token);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final List<String> listRoles() throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
SecretListResponse secrets = request.get(
|
|
||||||
AUTH_APPROLE + "/role?list=true",
|
|
||||||
emptyMap(),
|
|
||||||
token,
|
|
||||||
SecretListResponse.class
|
|
||||||
);
|
|
||||||
|
|
||||||
return secrets.getKeys();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final List<String> listSecrets(final String roleName) throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
SecretListResponse secrets = request.get(
|
|
||||||
String.format(AUTH_APPROLE_ROLE, roleName, "/secret-id?list=true"),
|
|
||||||
emptyMap(),
|
|
||||||
token,
|
|
||||||
SecretListResponse.class
|
|
||||||
);
|
|
||||||
|
|
||||||
return secrets.getKeys();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-client for transit methods.
|
|
||||||
*/
|
|
||||||
public class TransitClientImpl implements TransitClient {
|
|
||||||
@Override
|
|
||||||
public final TransitResponse encrypt(final String keyName, final String plaintext)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
Map<String, Object> payload = mapOf(
|
|
||||||
"plaintext", plaintext
|
|
||||||
);
|
|
||||||
|
|
||||||
return request.post(TRANSIT_ENCRYPT + keyName, payload, token, TransitResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final TransitResponse decrypt(final String keyName, final String ciphertext)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
Map<String, Object> payload = mapOf(
|
|
||||||
"ciphertext", ciphertext
|
|
||||||
);
|
|
||||||
|
|
||||||
return request.post(TRANSIT_DECRYPT + keyName, payload, token, TransitResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final TransitResponse hash(final String algorithm, final String input, final String format)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
if (format != null && !"hex".equals(format) && !"base64".equals(format)) {
|
|
||||||
throw new IllegalArgumentException("Unsupported format " + format);
|
|
||||||
}
|
|
||||||
|
|
||||||
requireAuth();
|
|
||||||
|
|
||||||
Map<String, Object> payload = mapOf(
|
|
||||||
"input", input,
|
|
||||||
"format", format
|
|
||||||
);
|
|
||||||
|
|
||||||
return request.post(TRANSIT_HASH + algorithm, payload, token, TransitResponse.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sub-client for system methods.
|
|
||||||
*/
|
|
||||||
public class SysClientImpl implements SysClient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final SealResponse sealStatus() throws VaultConnectorException {
|
|
||||||
return request.get(SYS_SEAL_STATUS, emptyMap(), token, SealResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void seal() throws VaultConnectorException {
|
|
||||||
request.put(SYS_SEAL, emptyMap(), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException {
|
|
||||||
Map<String, String> param = mapOfStrings(
|
|
||||||
"key", key,
|
|
||||||
"reset", reset
|
|
||||||
);
|
|
||||||
|
|
||||||
return request.put(SYS_UNSEAL, param, token, SealResponse.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HealthResponse getHealth() throws VaultConnectorException {
|
|
||||||
|
|
||||||
return request.get(
|
|
||||||
SYS_HEALTH,
|
|
||||||
// Force status code to be 200, so we don't need to modify the request sequence.
|
|
||||||
Map.of(
|
|
||||||
"standbycode", "200", // Default: 429.
|
|
||||||
"sealedcode", "200", // Default: 503.
|
|
||||||
"uninitcode", "200" // Default: 501.
|
|
||||||
),
|
|
||||||
token,
|
|
||||||
HealthResponse.class
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final List<AuthBackend> getAuthBackends() throws VaultConnectorException {
|
|
||||||
/* Issue request and parse response */
|
|
||||||
AuthMethodsResponse amr = request.get(SYS_AUTH, emptyMap(), token, AuthMethodsResponse.class);
|
|
||||||
|
|
||||||
return amr.getSupportedMethods().values().stream().map(AuthMethod::getType).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016-2025 Stefan Kalscheuer
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.stklcode.jvault.connector;
|
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|
||||||
import de.stklcode.jvault.connector.model.response.MetadataResponse;
|
|
||||||
import de.stklcode.jvault.connector.model.response.SecretResponse;
|
|
||||||
import de.stklcode.jvault.connector.model.response.SecretVersionResponse;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* KV v2 client interface.
|
|
||||||
* Provides methods to interact with Vault's KV v2 API.
|
|
||||||
*
|
|
||||||
* @since 2.0.0 extracted from {@link VaultConnector}
|
|
||||||
*/
|
|
||||||
public interface KV2Client {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the latest secret data for specific version from Vault.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/data/<key>} is read here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @return Secret response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
default SecretResponse readData(final String mount, final String key) throws VaultConnectorException {
|
|
||||||
return readVersion(mount, key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write secret to Vault.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/data/<key>} is written here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @param data Secret content. Value must be be JSON serializable.
|
|
||||||
* @return Metadata for the created/updated secret.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
default SecretVersionResponse writeData(final String mount,
|
|
||||||
final String key,
|
|
||||||
final Map<String, Object> data) throws VaultConnectorException {
|
|
||||||
return writeData(mount, key, data, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write secret to Vault.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/data/<key>} is written here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @param data Secret content. Value must be be JSON serializable.
|
|
||||||
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
|
|
||||||
* @return Metadata for the created/updated secret.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
SecretVersionResponse writeData(final String mount,
|
|
||||||
final String key,
|
|
||||||
final Map<String, Object> data,
|
|
||||||
final Integer cas) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve secret data from Vault.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/data/<key>} is read here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
|
|
||||||
* @return Secret response.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
SecretResponse readVersion(final String mount, final String key, final Integer version)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve secret metadata from Vault.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/metadata/<key>} is read here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @return Metadata response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
MetadataResponse readMetadata(final String mount, final String key) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update secret metadata.
|
|
||||||
* <br>
|
|
||||||
* Path {@code <mount>/metadata/<key>} is written here.
|
|
||||||
* Only available for KV v2 secrets.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret identifier
|
|
||||||
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
|
|
||||||
* @param casRequired Specify if Check-And-Set is required for this secret.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void updateMetadata(final String mount,
|
|
||||||
final String key,
|
|
||||||
final Integer maxVersions,
|
|
||||||
final boolean casRequired) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete latest version of a secret from Vault.
|
|
||||||
* <br>
|
|
||||||
* Only available for KV v2 stores.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret path.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void deleteLatestVersion(final String mount, final String key) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete latest version of a secret from Vault.
|
|
||||||
* <br>
|
|
||||||
* Prefix {@code secret/} is automatically added to path.
|
|
||||||
* Only available for KV v2 stores.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret path.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void deleteAllVersions(final String mount, final String key) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete secret versions from Vault.
|
|
||||||
* <br>
|
|
||||||
* Only available for KV v2 stores.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret path.
|
|
||||||
* @param versions Versions of the secret to delete.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void deleteVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undelete (restore) secret versions from Vault.
|
|
||||||
* Only available for KV v2 stores.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret path.
|
|
||||||
* @param versions Versions of the secret to undelete.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void undeleteVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy secret versions from Vault.
|
|
||||||
* Only available for KV v2 stores.
|
|
||||||
*
|
|
||||||
* @param mount Secret store mount point (without leading or trailing slash).
|
|
||||||
* @param key Secret path.
|
|
||||||
* @param versions Versions of the secret to destroy.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.8
|
|
||||||
*/
|
|
||||||
void destroyVersions(final String mount, final String key, final int... versions)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
}
|
|
@@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016-2025 Stefan Kalscheuer
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.stklcode.jvault.connector;
|
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|
||||||
import de.stklcode.jvault.connector.model.AuthBackend;
|
|
||||||
import de.stklcode.jvault.connector.model.Token;
|
|
||||||
import de.stklcode.jvault.connector.model.TokenRole;
|
|
||||||
import de.stklcode.jvault.connector.model.response.*;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sys client interface.
|
|
||||||
* Provides methods to interact with Vault's system API.
|
|
||||||
*
|
|
||||||
* @since 2.0.0 extracted from {@link VaultConnector}
|
|
||||||
*/
|
|
||||||
public interface SysClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve status of vault seal.
|
|
||||||
*
|
|
||||||
* @return Seal status
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
SealResponse sealStatus() throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Seal vault.
|
|
||||||
*
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
void seal() throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unseal vault.
|
|
||||||
*
|
|
||||||
* @param key A single master share key
|
|
||||||
* @param reset Discard previously provided keys (optional)
|
|
||||||
* @return Response with seal status
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unseal vault.
|
|
||||||
*
|
|
||||||
* @param key A single master share key
|
|
||||||
* @return Response with seal status
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
default SealResponse unseal(final String key) throws VaultConnectorException {
|
|
||||||
return unseal(key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Query server health information.
|
|
||||||
*
|
|
||||||
* @return Health information.
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.7.0
|
|
||||||
*/
|
|
||||||
HealthResponse getHealth() throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all available authentication backends.
|
|
||||||
*
|
|
||||||
* @return List of backends
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
List<AuthBackend> getAuthBackends() throws VaultConnectorException;
|
|
||||||
|
|
||||||
}
|
|
@@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016-2025 Stefan Kalscheuer
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.stklcode.jvault.connector;
|
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|
||||||
import de.stklcode.jvault.connector.model.Token;
|
|
||||||
import de.stklcode.jvault.connector.model.TokenRole;
|
|
||||||
import de.stklcode.jvault.connector.model.response.AuthResponse;
|
|
||||||
import de.stklcode.jvault.connector.model.response.TokenResponse;
|
|
||||||
import de.stklcode.jvault.connector.model.response.TokenRoleResponse;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Token client interface.
|
|
||||||
* Provides methods to interact with Vault's token API.
|
|
||||||
*
|
|
||||||
* @since 2.0.0 extracted from {@link VaultConnector}
|
|
||||||
*/
|
|
||||||
public interface TokenClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new token.
|
|
||||||
*
|
|
||||||
* @param token the token
|
|
||||||
* @return the result response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
AuthResponse create(final Token token) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new token.
|
|
||||||
*
|
|
||||||
* @param token the token
|
|
||||||
* @param orphan create orphan token
|
|
||||||
* @return the result response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
AuthResponse create(final Token token, boolean orphan) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new token for specific role.
|
|
||||||
*
|
|
||||||
* @param token the token
|
|
||||||
* @param role the role name
|
|
||||||
* @return the result response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
AuthResponse create(final Token token, final String role) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup token information.
|
|
||||||
*
|
|
||||||
* @param token the token
|
|
||||||
* @return the result response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
*/
|
|
||||||
TokenResponse lookup(final String token) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new or update an existing token role.
|
|
||||||
*
|
|
||||||
* @param role the role entity (name must be set)
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
default boolean createOrUpdateRole(final TokenRole role) throws VaultConnectorException {
|
|
||||||
return createOrUpdateRole(role.getName(), role);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new or update an existing token role.
|
|
||||||
*
|
|
||||||
* @param name the role name (overrides name possibly set in role entity)
|
|
||||||
* @param role the role entity
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
boolean createOrUpdateRole(final String name, final TokenRole role) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup token information.
|
|
||||||
*
|
|
||||||
* @param name the role name
|
|
||||||
* @return the result response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
TokenRoleResponse readRole(final String name) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List available token roles from Vault.
|
|
||||||
*
|
|
||||||
* @return List of token roles
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
List<String> listRoles() throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a token role.
|
|
||||||
*
|
|
||||||
* @param name the role name to delete
|
|
||||||
* @return {@code true} on success
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 0.9
|
|
||||||
*/
|
|
||||||
boolean deleteRole(final String name) throws VaultConnectorException;
|
|
||||||
}
|
|
@@ -1,107 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2016-2025 Stefan Kalscheuer
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package de.stklcode.jvault.connector;
|
|
||||||
|
|
||||||
import de.stklcode.jvault.connector.exception.VaultConnectorException;
|
|
||||||
import de.stklcode.jvault.connector.model.response.TransitResponse;
|
|
||||||
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transit client interface.
|
|
||||||
* Provides methods to interact with Vault's transit API.
|
|
||||||
*
|
|
||||||
* @since 2.0.0 extracted from {@link VaultConnector}
|
|
||||||
*/
|
|
||||||
public interface TransitClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt plaintext via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param keyName Transit key name
|
|
||||||
* @param plaintext Text to encrypt (Base64 encoded)
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
TransitResponse encrypt(final String keyName, final String plaintext) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt plaintext via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param keyName Transit key name
|
|
||||||
* @param plaintext Binary data to encrypt
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
default TransitResponse encrypt(final String keyName, final byte[] plaintext)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
return encrypt(keyName, Base64.getEncoder().encodeToString(plaintext));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt ciphertext via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param keyName Transit key name
|
|
||||||
* @param ciphertext Text to decrypt
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
TransitResponse decrypt(final String keyName, final String ciphertext) throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash data in hex format via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param algorithm Specifies the hash algorithm to use
|
|
||||||
* @param input Data to hash
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
default TransitResponse hash(final String algorithm, final String input) throws VaultConnectorException {
|
|
||||||
return hash(algorithm, input, "hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash data via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param algorithm Specifies the hash algorithm to use
|
|
||||||
* @param input Data to hash (Base64 encoded)
|
|
||||||
* @param format Specifies the output encoding (hex/base64)
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
TransitResponse hash(final String algorithm, final String input, final String format)
|
|
||||||
throws VaultConnectorException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hash data via transit engine from Vault.
|
|
||||||
*
|
|
||||||
* @param algorithm Specifies the hash algorithm to use
|
|
||||||
* @param input Data to hash
|
|
||||||
* @return Transit response
|
|
||||||
* @throws VaultConnectorException on error
|
|
||||||
* @since 1.5.0
|
|
||||||
*/
|
|
||||||
default TransitResponse hash(final String algorithm, final byte[] input, final String format)
|
|
||||||
throws VaultConnectorException {
|
|
||||||
return hash(algorithm, Base64.getEncoder().encodeToString(input), format);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -37,6 +37,59 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
*/
|
*/
|
||||||
void resetAuth();
|
void resetAuth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve status of vault seal.
|
||||||
|
*
|
||||||
|
* @return Seal status
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
SealResponse sealStatus() throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Seal vault.
|
||||||
|
*
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
void seal() throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unseal vault.
|
||||||
|
*
|
||||||
|
* @param key A single master share key
|
||||||
|
* @param reset Discard previously provided keys (optional)
|
||||||
|
* @return Response with seal status
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
SealResponse unseal(final String key, final Boolean reset) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unseal vault.
|
||||||
|
*
|
||||||
|
* @param key A single master share key
|
||||||
|
* @return Response with seal status
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
default SealResponse unseal(final String key) throws VaultConnectorException {
|
||||||
|
return unseal(key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query server health information.
|
||||||
|
*
|
||||||
|
* @return Health information.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.7.0
|
||||||
|
*/
|
||||||
|
HealthResponse getHealth() throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available authentication backends.
|
||||||
|
*
|
||||||
|
* @return List of backends
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
List<AuthBackend> getAuthBackends() throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authorize to Vault using token.
|
* Authorize to Vault using token.
|
||||||
*
|
*
|
||||||
@@ -79,6 +132,187 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
*/
|
*/
|
||||||
AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException;
|
AuthResponse authAppRole(final String roleID, final String secretID) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new AppRole role from given metamodel.
|
||||||
|
*
|
||||||
|
* @param role The role
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
boolean createAppRole(final AppRole role) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole role with default policy.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default boolean createAppRole(final String roleName) throws VaultConnectorException {
|
||||||
|
return createAppRole(roleName, new ArrayList<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole role with policies.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param policies The policies to associate with
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default boolean createAppRole(final String roleName, final List<String> policies) throws VaultConnectorException {
|
||||||
|
return createAppRole(roleName, policies, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole role with default policy and custom ID.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param roleID A custom role ID
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default boolean createAppRole(final String roleName, final String roleID) throws VaultConnectorException {
|
||||||
|
return createAppRole(roleName, new ArrayList<>(), roleID);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole role with policies and custom ID.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param policies The policies to associate with
|
||||||
|
* @param roleID A custom role ID
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default boolean createAppRole(final String roleName, final List<String> policies, final String roleID)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
return createAppRole(AppRole.builder(roleName).withTokenPolicies(policies).withId(roleID).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete AppRole role from Vault.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
boolean deleteAppRole(final String roleName) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an AppRole role.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return Result of the lookup
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
AppRoleResponse lookupAppRole(final String roleName) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve ID for an AppRole role.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return The role ID
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
String getAppRoleID(final String roleName) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set custom ID for an AppRole role.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param roleID The role ID
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
boolean setAppRoleID(final String roleName, final String roleID) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new random generated AppRole secret.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return The secret ID
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default AppRoleSecretResponse createAppRoleSecret(final String roleName) throws VaultConnectorException {
|
||||||
|
return createAppRoleSecret(roleName, new AppRoleSecret());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole secret with custom ID.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param secretID A custom secret ID
|
||||||
|
* @return The secret ID
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
default AppRoleSecretResponse createAppRoleSecret(final String roleName, final String secretID)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
return createAppRoleSecret(roleName, new AppRoleSecret(secretID));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register new AppRole secret with custom ID.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param secret The secret meta object
|
||||||
|
* @return The secret ID
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
AppRoleSecretResponse createAppRoleSecret(final String roleName, final AppRoleSecret secret)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an AppRole secret.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param secretID The secret ID
|
||||||
|
* @return Result of the lookup
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
AppRoleSecretResponse lookupAppRoleSecret(final String roleName, final String secretID)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy an AppRole secret.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @param secretID The secret meta object
|
||||||
|
* @return The secret ID
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.4.0
|
||||||
|
*/
|
||||||
|
boolean destroyAppRoleSecret(final String roleName, final String secretID) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List existing (accessible) AppRole roles.
|
||||||
|
*
|
||||||
|
* @return List of roles
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
List<String> listAppRoles() throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List existing (accessible) secret IDs for AppRole role.
|
||||||
|
*
|
||||||
|
* @param roleName The role name
|
||||||
|
* @return List of roles
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
*/
|
||||||
|
List<String> listAppRoleSecrets(final String roleName) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get authorization status.
|
* Get authorization status.
|
||||||
*
|
*
|
||||||
@@ -96,6 +330,108 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
*/
|
*/
|
||||||
SecretResponse read(final String key) throws VaultConnectorException;
|
SecretResponse read(final String key) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the latest secret data for specific version from Vault.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/data/<key>} is read here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @return Secret response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
default SecretResponse readSecretData(final String mount, final String key) throws VaultConnectorException {
|
||||||
|
return readSecretVersion(mount, key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write secret to Vault.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/data/<key>} is written here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @param data Secret content. Value must be be JSON serializable.
|
||||||
|
* @return Metadata for the created/updated secret.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
default SecretVersionResponse writeSecretData(final String mount,
|
||||||
|
final String key,
|
||||||
|
final Map<String, Object> data) throws VaultConnectorException {
|
||||||
|
return writeSecretData(mount, key, data, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write secret to Vault.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/data/<key>} is written here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @param data Secret content. Value must be be JSON serializable.
|
||||||
|
* @param cas Use Check-And-Set operation, i.e. only allow writing if current version matches this value.
|
||||||
|
* @return Metadata for the created/updated secret.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
SecretVersionResponse writeSecretData(final String mount,
|
||||||
|
final String key,
|
||||||
|
final Map<String, Object> data,
|
||||||
|
final Integer cas) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve secret data from Vault.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/data/<key>} is read here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @param version Version to read. If {@code null} or zero, the latest version will be returned.
|
||||||
|
* @return Secret response.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
SecretResponse readSecretVersion(final String mount, final String key, final Integer version)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve secret metadata from Vault.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/metadata/<key>} is read here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @return Metadata response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
MetadataResponse readSecretMetadata(final String mount, final String key) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update secret metadata.
|
||||||
|
* <br>
|
||||||
|
* Path {@code <mount>/metadata/<key>} is written here.
|
||||||
|
* Only available for KV v2 secrets.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret identifier
|
||||||
|
* @param maxVersions Maximum number of versions (fallback to backend default if {@code null})
|
||||||
|
* @param casRequired Specify if Check-And-Set is required for this secret.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void updateSecretMetadata(final String mount,
|
||||||
|
final String key,
|
||||||
|
final Integer maxVersions,
|
||||||
|
final boolean casRequired) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List available nodes from Vault.
|
* List available nodes from Vault.
|
||||||
*
|
*
|
||||||
@@ -151,6 +487,71 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
*/
|
*/
|
||||||
void delete(final String key) throws VaultConnectorException;
|
void delete(final String key) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete latest version of a secret from Vault.
|
||||||
|
* <br>
|
||||||
|
* Only available for KV v2 stores.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret path.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void deleteLatestSecretVersion(final String mount, final String key) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete latest version of a secret from Vault.
|
||||||
|
* <br>
|
||||||
|
* Prefix {@code secret/} is automatically added to path.
|
||||||
|
* Only available for KV v2 stores.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret path.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void deleteAllSecretVersions(final String mount, final String key) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete secret versions from Vault.
|
||||||
|
* <br>
|
||||||
|
* Only available for KV v2 stores.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret path.
|
||||||
|
* @param versions Versions of the secret to delete.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void deleteSecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undelete (restore) secret versions from Vault.
|
||||||
|
* Only available for KV v2 stores.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret path.
|
||||||
|
* @param versions Versions of the secret to undelete.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void undeleteSecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy secret versions from Vault.
|
||||||
|
* Only available for KV v2 stores.
|
||||||
|
*
|
||||||
|
* @param mount Secret store mount point (without leading or trailing slash).
|
||||||
|
* @param key Secret path.
|
||||||
|
* @param versions Versions of the secret to destroy.
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.8
|
||||||
|
*/
|
||||||
|
void destroySecretVersions(final String mount, final String key, final int... versions)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revoke given lease immediately.
|
* Revoke given lease immediately.
|
||||||
*
|
*
|
||||||
@@ -181,44 +582,170 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
SecretResponse renew(final String leaseID, final Integer increment) throws VaultConnectorException;
|
SecretResponse renew(final String leaseID, final Integer increment) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client for KV v2 API.
|
* Create a new token.
|
||||||
*
|
*
|
||||||
* @return KV v2 client
|
* @param token the token
|
||||||
* @since 2.0.0
|
* @return the result response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
*/
|
*/
|
||||||
KV2Client kv2();
|
AuthResponse createToken(final Token token) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client for token API.
|
* Create a new token.
|
||||||
*
|
*
|
||||||
* @return Token client
|
* @param token the token
|
||||||
* @since 2.0.0
|
* @param orphan create orphan token
|
||||||
|
* @return the result response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
*/
|
*/
|
||||||
TokenClient token();
|
AuthResponse createToken(final Token token, boolean orphan) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client for AppRole API.
|
* Create a new token for specific role.
|
||||||
*
|
*
|
||||||
* @return AppRole client
|
* @param token the token
|
||||||
* @since 2.0.0
|
* @param role the role name
|
||||||
|
* @return the result response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
*/
|
*/
|
||||||
AppRoleClient appRole();
|
AuthResponse createToken(final Token token, final String role) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client for transit API.
|
* Lookup token information.
|
||||||
*
|
*
|
||||||
* @return Transit client
|
* @param token the token
|
||||||
* @since 2.0.0
|
* @return the result response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
*/
|
*/
|
||||||
TransitClient transit();
|
TokenResponse lookupToken(final String token) throws VaultConnectorException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get client for system API.
|
* Create a new or update an existing token role.
|
||||||
*
|
*
|
||||||
* @return System client
|
* @param role the role entity (name must be set)
|
||||||
* @since 2.0.0
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.9
|
||||||
*/
|
*/
|
||||||
SysClient sys();
|
default boolean createOrUpdateTokenRole(final TokenRole role) throws VaultConnectorException {
|
||||||
|
return createOrUpdateTokenRole(role.getName(), role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new or update an existing token role.
|
||||||
|
*
|
||||||
|
* @param name the role name (overrides name possibly set in role entity)
|
||||||
|
* @param role the role entity
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
boolean createOrUpdateTokenRole(final String name, final TokenRole role) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup token information.
|
||||||
|
*
|
||||||
|
* @param name the role name
|
||||||
|
* @return the result response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
TokenRoleResponse readTokenRole(final String name) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List available token roles from Vault.
|
||||||
|
*
|
||||||
|
* @return List of token roles
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
List<String> listTokenRoles() throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a token role.
|
||||||
|
*
|
||||||
|
* @param name the role name to delete
|
||||||
|
* @return {@code true} on success
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 0.9
|
||||||
|
*/
|
||||||
|
boolean deleteTokenRole(final String name) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt plaintext via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param keyName Transit key name
|
||||||
|
* @param plaintext Text to encrypt (Base64 encoded)
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
TransitResponse transitEncrypt(final String keyName, final String plaintext) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt plaintext via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param keyName Transit key name
|
||||||
|
* @param plaintext Binary data to encrypt
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
default TransitResponse transitEncrypt(final String keyName, final byte[] plaintext)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
return transitEncrypt(keyName, Base64.getEncoder().encodeToString(plaintext));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt ciphertext via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param keyName Transit key name
|
||||||
|
* @param ciphertext Text to decrypt
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
TransitResponse transitDecrypt(final String keyName, final String ciphertext) throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash data in hex format via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param algorithm Specifies the hash algorithm to use
|
||||||
|
* @param input Data to hash
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
default TransitResponse transitHash(final String algorithm, final String input) throws VaultConnectorException {
|
||||||
|
return transitHash(algorithm, input, "hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash data via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param algorithm Specifies the hash algorithm to use
|
||||||
|
* @param input Data to hash (Base64 encoded)
|
||||||
|
* @param format Specifies the output encoding (hex/base64)
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
TransitResponse transitHash(final String algorithm, final String input, final String format)
|
||||||
|
throws VaultConnectorException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hash data via transit engine from Vault.
|
||||||
|
*
|
||||||
|
* @param algorithm Specifies the hash algorithm to use
|
||||||
|
* @param input Data to hash
|
||||||
|
* @return Transit response
|
||||||
|
* @throws VaultConnectorException on error
|
||||||
|
* @since 1.5.0
|
||||||
|
*/
|
||||||
|
default TransitResponse transitHash(final String algorithm, final byte[] input, final String format)
|
||||||
|
throws VaultConnectorException {
|
||||||
|
return transitHash(algorithm, Base64.getEncoder().encodeToString(input), format);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read credentials for MySQL backend at default mount point.
|
* Read credentials for MySQL backend at default mount point.
|
||||||
@@ -289,5 +816,4 @@ public interface VaultConnector extends AutoCloseable, Serializable {
|
|||||||
throws VaultConnectorException {
|
throws VaultConnectorException {
|
||||||
return (CredentialsResponse) read(mount + "/creds/" + role);
|
return (CredentialsResponse) read(mount + "/creds/" + role);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,6 +25,7 @@ import java.security.cert.CertificateException;
|
|||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -263,9 +264,9 @@ public final class RequestHelper implements Serializable {
|
|||||||
|
|
||||||
if (!payload.isEmpty()) {
|
if (!payload.isEmpty()) {
|
||||||
uriBuilder.append("?").append(
|
uriBuilder.append("?").append(
|
||||||
payload.entrySet().stream().map(par ->
|
payload.entrySet().stream()
|
||||||
URLEncoder.encode(par.getKey(), UTF_8) + "=" + URLEncoder.encode(par.getValue(), UTF_8)
|
.map(par -> encode(par.getKey()) + "=" + encode(par.getValue()))
|
||||||
).collect(Collectors.joining("&"))
|
.collect(Collectors.joining("&"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +308,17 @@ public final class RequestHelper implements Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode URL part.
|
||||||
|
*
|
||||||
|
* @param part Path part to URL-encode and insert into the template
|
||||||
|
* @return Encoded URL part
|
||||||
|
* @since 1.5.3
|
||||||
|
*/
|
||||||
|
public static String encode(final String part) {
|
||||||
|
return URLEncoder.encode(Objects.requireNonNullElse(part, ""), UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute prepared HTTP request and return result.
|
* Execute prepared HTTP request and return result.
|
||||||
*
|
*
|
||||||
|
@@ -40,8 +40,8 @@ public final class VaultApiPath {
|
|||||||
// Auth paths
|
// Auth paths
|
||||||
public static final String AUTH_TOKEN = AUTH + "/token";
|
public static final String AUTH_TOKEN = AUTH + "/token";
|
||||||
public static final String AUTH_USERPASS_LOGIN = AUTH + "/userpass/login/";
|
public static final String AUTH_USERPASS_LOGIN = AUTH + "/userpass/login/";
|
||||||
public static final String AUTH_APPROLE = AUTH + "/approle";
|
public static final String AUTH_APPROLE = AUTH + "/approle/";
|
||||||
public static final String AUTH_APPROLE_ROLE = AUTH_APPROLE + "/role/%s%s";
|
public static final String AUTH_APPROLE_ROLE = AUTH_APPROLE + "role/";
|
||||||
|
|
||||||
// Token operations
|
// Token operations
|
||||||
public static final String TOKEN_LOOKUP = "/lookup";
|
public static final String TOKEN_LOOKUP = "/lookup";
|
||||||
@@ -57,9 +57,6 @@ public final class VaultApiPath {
|
|||||||
public static final String SECRET_UNDELETE = "/undelete/";
|
public static final String SECRET_UNDELETE = "/undelete/";
|
||||||
public static final String SECRET_DESTROY = "/destroy/";
|
public static final String SECRET_DESTROY = "/destroy/";
|
||||||
|
|
||||||
// Generic paths
|
|
||||||
public static final String LOGIN = "/login";
|
|
||||||
|
|
||||||
// Transit engine paths
|
// Transit engine paths
|
||||||
public static final String TRANSIT_ENCRYPT = TRANSIT + "/encrypt/";
|
public static final String TRANSIT_ENCRYPT = TRANSIT + "/encrypt/";
|
||||||
public static final String TRANSIT_DECRYPT = TRANSIT + "/decrypt/";
|
public static final String TRANSIT_DECRYPT = TRANSIT + "/decrypt/";
|
||||||
|
@@ -32,7 +32,7 @@ import java.util.Objects;
|
|||||||
*/
|
*/
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
public final class AppRoleSecret implements Serializable {
|
public final class AppRoleSecret implements Serializable {
|
||||||
private static final long serialVersionUID = -3401074170145792641L;
|
private static final long serialVersionUID = 3079272087137299819L;
|
||||||
|
|
||||||
@JsonProperty("secret_id")
|
@JsonProperty("secret_id")
|
||||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||||
@@ -47,6 +47,8 @@ public final class AppRoleSecret implements Serializable {
|
|||||||
|
|
||||||
private List<String> cidrList;
|
private List<String> cidrList;
|
||||||
|
|
||||||
|
private List<String> tokenBoundCidrs;
|
||||||
|
|
||||||
@JsonProperty(value = "creation_time", access = JsonProperty.Access.WRITE_ONLY)
|
@JsonProperty(value = "creation_time", access = JsonProperty.Access.WRITE_ONLY)
|
||||||
private String creationTime;
|
private String creationTime;
|
||||||
|
|
||||||
@@ -137,6 +139,36 @@ public final class AppRoleSecret implements Serializable {
|
|||||||
return String.join(",", cidrList);
|
return String.join(",", cidrList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of bound CIDR subnets of associated tokens
|
||||||
|
* @since 1.5.3
|
||||||
|
*/
|
||||||
|
public List<String> getTokenBoundCidrs() {
|
||||||
|
return tokenBoundCidrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param boundCidrList list of subnets in CIDR notation to bind role to
|
||||||
|
* @since 1.5.3
|
||||||
|
*/
|
||||||
|
@JsonSetter("token_bound_cidrs")
|
||||||
|
public void setTokenBoundCidrs(final List<String> boundCidrList) {
|
||||||
|
this.tokenBoundCidrs = boundCidrList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list of subnets in CIDR notation as comma-separated {@link String}
|
||||||
|
* @since 1.5.3
|
||||||
|
*/
|
||||||
|
@JsonGetter("token_bound_cidrs")
|
||||||
|
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||||
|
public String getTokenBoundCidrsString() {
|
||||||
|
if (tokenBoundCidrs == null || tokenBoundCidrs.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return String.join(",", tokenBoundCidrs);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Creation time
|
* @return Creation time
|
||||||
*/
|
*/
|
||||||
@@ -184,6 +216,7 @@ public final class AppRoleSecret implements Serializable {
|
|||||||
Objects.equals(accessor, that.accessor) &&
|
Objects.equals(accessor, that.accessor) &&
|
||||||
Objects.equals(metadata, that.metadata) &&
|
Objects.equals(metadata, that.metadata) &&
|
||||||
Objects.equals(cidrList, that.cidrList) &&
|
Objects.equals(cidrList, that.cidrList) &&
|
||||||
|
Objects.equals(tokenBoundCidrs, that.tokenBoundCidrs) &&
|
||||||
Objects.equals(creationTime, that.creationTime) &&
|
Objects.equals(creationTime, that.creationTime) &&
|
||||||
Objects.equals(expirationTime, that.expirationTime) &&
|
Objects.equals(expirationTime, that.expirationTime) &&
|
||||||
Objects.equals(lastUpdatedTime, that.lastUpdatedTime) &&
|
Objects.equals(lastUpdatedTime, that.lastUpdatedTime) &&
|
||||||
@@ -193,7 +226,7 @@ public final class AppRoleSecret implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(id, accessor, metadata, cidrList, creationTime, expirationTime, lastUpdatedTime, numUses,
|
return Objects.hash(id, accessor, metadata, cidrList, tokenBoundCidrs, creationTime, expirationTime,
|
||||||
ttl);
|
lastUpdatedTime, numUses, ttl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -52,7 +52,7 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
|||||||
* @since 0.1
|
* @since 0.1
|
||||||
*/
|
*/
|
||||||
class HTTPVaultConnectorIT {
|
class HTTPVaultConnectorIT {
|
||||||
private static String VAULT_VERSION = "1.20.0"; // The vault version this test is supposed to run against.
|
private static String VAULT_VERSION = "1.20.3"; // The vault version this test is supposed to run against.
|
||||||
private static final String KEY1 = "E38bkCm0VhUvpdCKGQpcohhD9XmcHJ/2hreOSY019Lho";
|
private static final String KEY1 = "E38bkCm0VhUvpdCKGQpcohhD9XmcHJ/2hreOSY019Lho";
|
||||||
private static final String KEY2 = "O5OHwDleY3IiPdgw61cgHlhsrEm6tVJkrxhF6QAnILd1";
|
private static final String KEY2 = "O5OHwDleY3IiPdgw61cgHlhsrEm6tVJkrxhF6QAnILd1";
|
||||||
private static final String KEY3 = "mw7Bm3nbt/UWa/juDjjL2EPQ04kiJ0saC5JEXwJvXYsB";
|
private static final String KEY3 = "mw7Bm3nbt/UWa/juDjjL2EPQ04kiJ0saC5JEXwJvXYsB";
|
||||||
@@ -95,10 +95,10 @@ class HTTPVaultConnectorIT {
|
|||||||
connector = builder.build();
|
connector = builder.build();
|
||||||
|
|
||||||
// Unseal Vault and check result.
|
// Unseal Vault and check result.
|
||||||
SealResponse sealStatus = connector.sys().unseal(KEY1);
|
SealResponse sealStatus = connector.unseal(KEY1);
|
||||||
assumeTrue(sealStatus != null, "Seal status could not be determined after startup");
|
assumeTrue(sealStatus != null, "Seal status could not be determined after startup");
|
||||||
assumeTrue(sealStatus.isSealed(), "Vault is not sealed after startup");
|
assumeTrue(sealStatus.isSealed(), "Vault is not sealed after startup");
|
||||||
sealStatus = connector.sys().unseal(KEY2);
|
sealStatus = connector.unseal(KEY2);
|
||||||
assumeTrue(sealStatus != null, "Seal status could not be determined");
|
assumeTrue(sealStatus != null, "Seal status could not be determined");
|
||||||
assumeFalse(sealStatus.isSealed(), "Vault is not unsealed");
|
assumeFalse(sealStatus.isSealed(), "Vault is not unsealed");
|
||||||
assumeTrue(sealStatus.isInitialized(), "Vault is not initialized"); // Initialized flag of Vault 0.11.2 (#20).
|
assumeTrue(sealStatus.isInitialized(), "Vault is not initialized"); // Initialized flag of Vault 0.11.2 (#20).
|
||||||
@@ -337,7 +337,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Try to read accessible path with known value.
|
// Try to read accessible path with known value.
|
||||||
SecretResponse res = assertDoesNotThrow(
|
SecretResponse res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readData(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretData(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Valid secret path could not be read"
|
"Valid secret path could not be read"
|
||||||
);
|
);
|
||||||
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
|
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
|
||||||
@@ -346,7 +346,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Try to read different version of same secret.
|
// Try to read different version of same secret.
|
||||||
res = assertDoesNotThrow(
|
res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readVersion(MOUNT_KV2, SECRET2_KEY, 1),
|
() -> connector.readSecretVersion(MOUNT_KV2, SECRET2_KEY, 1),
|
||||||
"Valid secret version could not be read"
|
"Valid secret version could not be read"
|
||||||
);
|
);
|
||||||
assertEquals(1, res.getMetadata().getVersion(), "Unexpected secret version");
|
assertEquals(1, res.getMetadata().getVersion(), "Unexpected secret version");
|
||||||
@@ -365,7 +365,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// First get the current version of the secret.
|
// First get the current version of the secret.
|
||||||
MetadataResponse res = assertDoesNotThrow(
|
MetadataResponse res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading secret metadata failed"
|
"Reading secret metadata failed"
|
||||||
);
|
);
|
||||||
int currentVersion = res.getMetadata().getCurrentVersion();
|
int currentVersion = res.getMetadata().getCurrentVersion();
|
||||||
@@ -374,7 +374,7 @@ class HTTPVaultConnectorIT {
|
|||||||
Map<String, Object> data = new HashMap<>();
|
Map<String, Object> data = new HashMap<>();
|
||||||
data.put("value", SECRET2_VALUE3);
|
data.put("value", SECRET2_VALUE3);
|
||||||
SecretVersionResponse res2 = assertDoesNotThrow(
|
SecretVersionResponse res2 = assertDoesNotThrow(
|
||||||
() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data),
|
() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data),
|
||||||
"Writing secret to KV v2 store failed"
|
"Writing secret to KV v2 store failed"
|
||||||
);
|
);
|
||||||
assertEquals(currentVersion + 1, res2.getMetadata().getVersion(), "Version not updated after writing secret");
|
assertEquals(currentVersion + 1, res2.getMetadata().getVersion(), "Version not updated after writing secret");
|
||||||
@@ -382,7 +382,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Verify the content.
|
// Verify the content.
|
||||||
SecretResponse res3 = assertDoesNotThrow(
|
SecretResponse res3 = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readData(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretData(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading secret from KV v2 store failed"
|
"Reading secret from KV v2 store failed"
|
||||||
);
|
);
|
||||||
assertEquals(SECRET2_VALUE3, res3.get("value"), "Data not updated correctly");
|
assertEquals(SECRET2_VALUE3, res3.get("value"), "Data not updated correctly");
|
||||||
@@ -391,13 +391,13 @@ class HTTPVaultConnectorIT {
|
|||||||
Map<String, Object> data4 = singletonMap("value", SECRET2_VALUE4);
|
Map<String, Object> data4 = singletonMap("value", SECRET2_VALUE4);
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data4, currentVersion2 - 1),
|
() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data4, currentVersion2 - 1),
|
||||||
"Writing secret to KV v2 with invalid CAS value succeeded"
|
"Writing secret to KV v2 with invalid CAS value succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
// And finally with a correct CAS value.
|
// And finally with a correct CAS value.
|
||||||
Map<String, Object> data5 = singletonMap("value", SECRET2_VALUE4);
|
Map<String, Object> data5 = singletonMap("value", SECRET2_VALUE4);
|
||||||
assertDoesNotThrow(() -> connector.kv2().writeData(MOUNT_KV2, SECRET2_KEY, data5, currentVersion2));
|
assertDoesNotThrow(() -> connector.writeSecretData(MOUNT_KV2, SECRET2_KEY, data5, currentVersion2));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -412,7 +412,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Read current metadata first.
|
// Read current metadata first.
|
||||||
MetadataResponse res = assertDoesNotThrow(
|
MetadataResponse res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading secret metadata failed"
|
"Reading secret metadata failed"
|
||||||
);
|
);
|
||||||
Integer maxVersions = res.getMetadata().getMaxVersions();
|
Integer maxVersions = res.getMetadata().getMaxVersions();
|
||||||
@@ -420,13 +420,13 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Now update the metadata.
|
// Now update the metadata.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().updateMetadata(MOUNT_KV2, SECRET2_KEY, maxVersions + 1, true),
|
() -> connector.updateSecretMetadata(MOUNT_KV2, SECRET2_KEY, maxVersions + 1, true),
|
||||||
"Updating secret metadata failed"
|
"Updating secret metadata failed"
|
||||||
);
|
);
|
||||||
|
|
||||||
// And verify the result.
|
// And verify the result.
|
||||||
res = assertDoesNotThrow(
|
res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading secret metadata failed"
|
"Reading secret metadata failed"
|
||||||
);
|
);
|
||||||
assertEquals(maxVersions + 1, res.getMetadata().getMaxVersions(), "Unexpected maximum number of versions");
|
assertEquals(maxVersions + 1, res.getMetadata().getMaxVersions(), "Unexpected maximum number of versions");
|
||||||
@@ -444,7 +444,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Try to read accessible path with known value.
|
// Try to read accessible path with known value.
|
||||||
MetadataResponse res = assertDoesNotThrow(
|
MetadataResponse res = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Valid secret path could not be read"
|
"Valid secret path could not be read"
|
||||||
);
|
);
|
||||||
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
|
assertNotNull(res.getMetadata(), "Metadata not populated for KV v2 secret");
|
||||||
@@ -467,21 +467,21 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Try to delete non-existing versions.
|
// Try to delete non-existing versions.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().deleteVersions(MOUNT_KV2, SECRET2_KEY, 5, 42),
|
() -> connector.deleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 5, 42),
|
||||||
"Revealed non-existence of secret versions"
|
"Revealed non-existence of secret versions"
|
||||||
);
|
);
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Revealed non-existence of secret versions"
|
"Revealed non-existence of secret versions"
|
||||||
);
|
);
|
||||||
|
|
||||||
// Now delete existing version and verify.
|
// Now delete existing version and verify.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().deleteVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
() -> connector.deleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
||||||
"Deleting existing version failed"
|
"Deleting existing version failed"
|
||||||
);
|
);
|
||||||
MetadataResponse meta = assertDoesNotThrow(
|
MetadataResponse meta = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading deleted secret metadata failed"
|
"Reading deleted secret metadata failed"
|
||||||
);
|
);
|
||||||
assertNotNull(
|
assertNotNull(
|
||||||
@@ -491,11 +491,11 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Undelete the just deleted version.
|
// Undelete the just deleted version.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().undeleteVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
() -> connector.undeleteSecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
||||||
"Undeleting existing version failed"
|
"Undeleting existing version failed"
|
||||||
);
|
);
|
||||||
meta = assertDoesNotThrow(
|
meta = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading deleted secret metadata failed"
|
"Reading deleted secret metadata failed"
|
||||||
);
|
);
|
||||||
assertNull(
|
assertNull(
|
||||||
@@ -505,11 +505,11 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Now destroy it.
|
// Now destroy it.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().destroyVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
() -> connector.destroySecretVersions(MOUNT_KV2, SECRET2_KEY, 1),
|
||||||
"Destroying existing version failed"
|
"Destroying existing version failed"
|
||||||
);
|
);
|
||||||
meta = assertDoesNotThrow(
|
meta = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading destroyed secret metadata failed"
|
"Reading destroyed secret metadata failed"
|
||||||
);
|
);
|
||||||
assertTrue(
|
assertTrue(
|
||||||
@@ -519,11 +519,11 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Delete latest version.
|
// Delete latest version.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().deleteLatestVersion(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.deleteLatestSecretVersion(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Deleting latest version failed"
|
"Deleting latest version failed"
|
||||||
);
|
);
|
||||||
meta = assertDoesNotThrow(
|
meta = assertDoesNotThrow(
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading deleted secret metadata failed"
|
"Reading deleted secret metadata failed"
|
||||||
);
|
);
|
||||||
assertNotNull(
|
assertNotNull(
|
||||||
@@ -533,12 +533,12 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Delete all versions.
|
// Delete all versions.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.kv2().deleteAllVersions(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.deleteAllSecretVersions(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Deleting latest version failed"
|
"Deleting latest version failed"
|
||||||
);
|
);
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.kv2().readMetadata(MOUNT_KV2, SECRET2_KEY),
|
() -> connector.readSecretMetadata(MOUNT_KV2, SECRET2_KEY),
|
||||||
"Reading metadata of deleted secret should not succeed"
|
"Reading metadata of deleted secret should not succeed"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -620,21 +620,21 @@ class HTTPVaultConnectorIT {
|
|||||||
// Try unauthorized access first.
|
// Try unauthorized access first.
|
||||||
assumeFalse(connector.isAuthorized());
|
assumeFalse(connector.isAuthorized());
|
||||||
|
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().listRoles());
|
assertThrows(AuthorizationRequiredException.class, () -> connector.listAppRoles());
|
||||||
|
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().listSecrets(""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.listAppRoleSecrets(""));
|
||||||
|
|
||||||
// Authorize.
|
// Authorize.
|
||||||
authRoot();
|
authRoot();
|
||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
// Verify pre-existing rules.
|
// Verify pre-existing rules.
|
||||||
List<String> res = assertDoesNotThrow(() -> connector.appRole().listRoles(), "Role listing failed");
|
List<String> res = assertDoesNotThrow(() -> connector.listAppRoles(), "Role listing failed");
|
||||||
assertEquals(2, res.size(), "Unexpected number of AppRoles");
|
assertEquals(2, res.size(), "Unexpected number of AppRoles");
|
||||||
assertTrue(res.containsAll(List.of(APPROLE_ROLE_NAME, APPROLE_ROLE2_NAME)), "Pre-configured roles not listed");
|
assertTrue(res.containsAll(List.of(APPROLE_ROLE_NAME, APPROLE_ROLE2_NAME)), "Pre-configured roles not listed");
|
||||||
|
|
||||||
// Check secret IDs.
|
// Check secret IDs.
|
||||||
res = assertDoesNotThrow(() -> connector.appRole().listSecrets(APPROLE_ROLE_NAME), "AppRole secret listing failed");
|
res = assertDoesNotThrow(() -> connector.listAppRoleSecrets(APPROLE_ROLE_NAME), "AppRole secret listing failed");
|
||||||
assertEquals(List.of(APPROLE_SECRET_ACCESSOR), res, "Pre-configured AppRole secret not listed");
|
assertEquals(List.of(APPROLE_SECRET_ACCESSOR), res, "Pre-configured AppRole secret not listed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -647,14 +647,14 @@ class HTTPVaultConnectorIT {
|
|||||||
void createAppRoleTest() {
|
void createAppRoleTest() {
|
||||||
// Try unauthorized access first.
|
// Try unauthorized access first.
|
||||||
assumeFalse(connector.isAuthorized());
|
assumeFalse(connector.isAuthorized());
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().create(new AppRole()));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.createAppRole(new AppRole()));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().lookup(""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.lookupAppRole(""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().delete(""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.deleteAppRole(""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().getRoleID(""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.getAppRoleID(""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().setRoleID("", ""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.setAppRoleID("", ""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().createSecret("", ""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.createAppRoleSecret("", ""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().lookupSecret("", ""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.lookupAppRoleSecret("", ""));
|
||||||
assertThrows(AuthorizationRequiredException.class, () -> connector.appRole().destroySecret("", ""));
|
assertThrows(AuthorizationRequiredException.class, () -> connector.destroyAppRoleSecret("", ""));
|
||||||
|
|
||||||
// Authorize.
|
// Authorize.
|
||||||
authRoot();
|
authRoot();
|
||||||
@@ -666,23 +666,23 @@ class HTTPVaultConnectorIT {
|
|||||||
AppRole role = AppRole.builder(roleName).build();
|
AppRole role = AppRole.builder(roleName).build();
|
||||||
|
|
||||||
// Create role.
|
// Create role.
|
||||||
boolean createRes = assertDoesNotThrow(() -> connector.appRole().create(role), "Role creation failed");
|
boolean createRes = assertDoesNotThrow(() -> connector.createAppRole(role), "Role creation failed");
|
||||||
assertTrue(createRes, "Role creation failed");
|
assertTrue(createRes, "Role creation failed");
|
||||||
|
|
||||||
// Lookup role.
|
// Lookup role.
|
||||||
AppRoleResponse res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName), "Role lookup failed");
|
AppRoleResponse res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName), "Role lookup failed");
|
||||||
assertNotNull(res.getRole(), "Role lookup returned no role");
|
assertNotNull(res.getRole(), "Role lookup returned no role");
|
||||||
|
|
||||||
// Lookup role ID.
|
// Lookup role ID.
|
||||||
String roleID = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName), "Role ID lookup failed");
|
String roleID = assertDoesNotThrow(() -> connector.getAppRoleID(roleName), "Role ID lookup failed");
|
||||||
assertNotEquals("", roleID, "Role ID lookup returned empty ID");
|
assertNotEquals("", roleID, "Role ID lookup returned empty ID");
|
||||||
|
|
||||||
// Set custom role ID.
|
// Set custom role ID.
|
||||||
String roleID2 = "custom-role-id";
|
String roleID2 = "custom-role-id";
|
||||||
assertDoesNotThrow(() -> connector.appRole().setRoleID(roleName, roleID2), "Setting custom role ID failed");
|
assertDoesNotThrow(() -> connector.setAppRoleID(roleName, roleID2), "Setting custom role ID failed");
|
||||||
|
|
||||||
// Verify role ID.
|
// Verify role ID.
|
||||||
String res2 = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName), "Role ID lookup failed");
|
String res2 = assertDoesNotThrow(() -> connector.getAppRoleID(roleName), "Role ID lookup failed");
|
||||||
assertEquals(roleID2, res2, "Role ID lookup returned wrong ID");
|
assertEquals(roleID2, res2, "Role ID lookup returned wrong ID");
|
||||||
|
|
||||||
// Update role model with custom flags.
|
// Update role model with custom flags.
|
||||||
@@ -691,44 +691,44 @@ class HTTPVaultConnectorIT {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Create role.
|
// Create role.
|
||||||
boolean res3 = assertDoesNotThrow(() -> connector.appRole().create(role2), "Role creation failed");
|
boolean res3 = assertDoesNotThrow(() -> connector.createAppRole(role2), "Role creation failed");
|
||||||
assertTrue(res3, "No result given");
|
assertTrue(res3, "No result given");
|
||||||
|
|
||||||
// Lookup updated role.
|
// Lookup updated role.
|
||||||
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName), "Role lookup failed");
|
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName), "Role lookup failed");
|
||||||
assertNotNull(res.getRole(), "Role lookup returned no role");
|
assertNotNull(res.getRole(), "Role lookup returned no role");
|
||||||
assertEquals(321, res.getRole().getTokenPeriod(), "Token period not set for role");
|
assertEquals(321, res.getRole().getTokenPeriod(), "Token period not set for role");
|
||||||
|
|
||||||
// Create role by name.
|
// Create role by name.
|
||||||
String roleName2 = "RoleByName";
|
String roleName2 = "RoleByName";
|
||||||
assertDoesNotThrow(() -> connector.appRole().create(roleName2), "Creation of role by name failed");
|
assertDoesNotThrow(() -> connector.createAppRole(roleName2), "Creation of role by name failed");
|
||||||
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName2), "Creation of role by name failed");
|
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName2), "Creation of role by name failed");
|
||||||
assertNotNull(res.getRole(), "Role lookuo returned not value");
|
assertNotNull(res.getRole(), "Role lookuo returned not value");
|
||||||
|
|
||||||
// Create role by name with custom ID.
|
// Create role by name with custom ID.
|
||||||
String roleName3 = "RoleByName";
|
String roleName3 = "RoleByName";
|
||||||
String roleID3 = "RolyByNameID";
|
String roleID3 = "RolyByNameID";
|
||||||
assertDoesNotThrow(() -> connector.appRole().create(roleName3, roleID3), "Creation of role by name failed");
|
assertDoesNotThrow(() -> connector.createAppRole(roleName3, roleID3), "Creation of role by name failed");
|
||||||
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName3), "Creation of role by name failed");
|
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName3), "Creation of role by name failed");
|
||||||
assertNotNull(res.getRole(), "Role lookuo returned not value");
|
assertNotNull(res.getRole(), "Role lookuo returned not value");
|
||||||
|
|
||||||
res2 = assertDoesNotThrow(() -> connector.appRole().getRoleID(roleName3), "Creation of role by name failed");
|
res2 = assertDoesNotThrow(() -> connector.getAppRoleID(roleName3), "Creation of role by name failed");
|
||||||
assertEquals(roleID3, res2, "Role lookuo returned wrong ID");
|
assertEquals(roleID3, res2, "Role lookuo returned wrong ID");
|
||||||
|
|
||||||
// Create role by name with policies.
|
// Create role by name with policies.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.appRole().create(roleName3, Collections.singletonList("testpolicy")),
|
() -> connector.createAppRole(roleName3, Collections.singletonList("testpolicy")),
|
||||||
"Creation of role by name failed"
|
"Creation of role by name failed"
|
||||||
);
|
);
|
||||||
res = assertDoesNotThrow(() -> connector.appRole().lookup(roleName3), "Creation of role by name failed");
|
res = assertDoesNotThrow(() -> connector.lookupAppRole(roleName3), "Creation of role by name failed");
|
||||||
// Note: As of Vault 0.8.3 default policy is not added automatically, so this test should return 1, not 2.
|
// Note: As of Vault 0.8.3 default policy is not added automatically, so this test should return 1, not 2.
|
||||||
assertEquals(List.of("testpolicy"), res.getRole().getTokenPolicies(), "Role lookup returned unexpected policies");
|
assertEquals(List.of("testpolicy"), res.getRole().getTokenPolicies(), "Role lookup returned unexpected policies");
|
||||||
|
|
||||||
// Delete role.
|
// Delete role.
|
||||||
assertDoesNotThrow(() -> connector.appRole().delete(roleName3), "Deletion of role failed");
|
assertDoesNotThrow(() -> connector.deleteAppRole(roleName3), "Deletion of role failed");
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().lookup(roleName3),
|
() -> connector.lookupAppRole(roleName3),
|
||||||
"Deleted role could be looked up"
|
"Deleted role could be looked up"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -745,7 +745,7 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// Create default (random) secret for existing role.
|
// Create default (random) secret for existing role.
|
||||||
AppRoleSecretResponse res = assertDoesNotThrow(
|
AppRoleSecretResponse res = assertDoesNotThrow(
|
||||||
() -> connector.appRole().createSecret(APPROLE_ROLE_NAME),
|
() -> connector.createAppRoleSecret(APPROLE_ROLE_NAME),
|
||||||
"AppRole secret creation failed"
|
"AppRole secret creation failed"
|
||||||
);
|
);
|
||||||
assertNotNull(res.getSecret(), "No secret returned");
|
assertNotNull(res.getSecret(), "No secret returned");
|
||||||
@@ -753,26 +753,26 @@ class HTTPVaultConnectorIT {
|
|||||||
// Create secret with custom ID.
|
// Create secret with custom ID.
|
||||||
String secretID = "customSecretId";
|
String secretID = "customSecretId";
|
||||||
res = assertDoesNotThrow(
|
res = assertDoesNotThrow(
|
||||||
() -> connector.appRole().createSecret(APPROLE_ROLE_NAME, secretID),
|
() -> connector.createAppRoleSecret(APPROLE_ROLE_NAME, secretID),
|
||||||
"AppRole secret creation failed"
|
"AppRole secret creation failed"
|
||||||
);
|
);
|
||||||
assertEquals(secretID, res.getSecret().getId(), "Unexpected secret ID returned");
|
assertEquals(secretID, res.getSecret().getId(), "Unexpected secret ID returned");
|
||||||
|
|
||||||
// Lookup secret.
|
// Lookup secret.
|
||||||
res = assertDoesNotThrow(
|
res = assertDoesNotThrow(
|
||||||
() -> connector.appRole().lookupSecret(APPROLE_ROLE_NAME, secretID),
|
() -> connector.lookupAppRoleSecret(APPROLE_ROLE_NAME, secretID),
|
||||||
"AppRole secret lookup failed"
|
"AppRole secret lookup failed"
|
||||||
);
|
);
|
||||||
assertNotNull(res.getSecret(), "No secret information returned");
|
assertNotNull(res.getSecret(), "No secret information returned");
|
||||||
|
|
||||||
// Destroy secret.
|
// Destroy secret.
|
||||||
assertDoesNotThrow(
|
assertDoesNotThrow(
|
||||||
() -> connector.appRole().destroySecret(APPROLE_ROLE_NAME, secretID),
|
() -> connector.destroyAppRoleSecret(APPROLE_ROLE_NAME, secretID),
|
||||||
"AppRole secret destruction failed"
|
"AppRole secret destruction failed"
|
||||||
);
|
);
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().lookupSecret(APPROLE_ROLE_NAME, secretID),
|
() -> connector.lookupAppRoleSecret(APPROLE_ROLE_NAME, secretID),
|
||||||
"Destroyed AppRole secret successfully read"
|
"Destroyed AppRole secret successfully read"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -825,7 +825,7 @@ class HTTPVaultConnectorIT {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
// Create token.
|
// Create token.
|
||||||
AuthResponse res = assertDoesNotThrow(() -> connector.token().create(token), "Token creation failed");
|
AuthResponse res = assertDoesNotThrow(() -> connector.createToken(token), "Token creation failed");
|
||||||
assertNotNull(res, "No result given");
|
assertNotNull(res, "No result given");
|
||||||
assertEquals("test-id", res.getAuth().getClientToken(), "Invalid token ID returned");
|
assertEquals("test-id", res.getAuth().getClientToken(), "Invalid token ID returned");
|
||||||
assertEquals(List.of("root"), res.getAuth().getPolicies(), "Expected inherited root policy");
|
assertEquals(List.of("root"), res.getAuth().getPolicies(), "Expected inherited root policy");
|
||||||
@@ -847,7 +847,7 @@ class HTTPVaultConnectorIT {
|
|||||||
.withoutDefaultPolicy()
|
.withoutDefaultPolicy()
|
||||||
.withMeta("foo", "bar")
|
.withMeta("foo", "bar")
|
||||||
.build();
|
.build();
|
||||||
res = assertDoesNotThrow(() -> connector.token().create(token2), "Token creation failed");
|
res = assertDoesNotThrow(() -> connector.createToken(token2), "Token creation failed");
|
||||||
assertEquals("test-id2", res.getAuth().getClientToken(), "Invalid token ID returned");
|
assertEquals("test-id2", res.getAuth().getClientToken(), "Invalid token ID returned");
|
||||||
assertEquals(List.of("testpolicy"), res.getAuth().getPolicies(), "Invalid policies returned");
|
assertEquals(List.of("testpolicy"), res.getAuth().getPolicies(), "Invalid policies returned");
|
||||||
assertNotNull(res.getAuth().getMetadata(), "Metadata not given");
|
assertNotNull(res.getAuth().getMetadata(), "Metadata not given");
|
||||||
@@ -866,7 +866,7 @@ class HTTPVaultConnectorIT {
|
|||||||
.build();
|
.build();
|
||||||
InvalidResponseException e = assertThrows(
|
InvalidResponseException e = assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.token().create(token3),
|
() -> connector.createToken(token3),
|
||||||
"Overwriting token should fail as of Vault 0.8.0"
|
"Overwriting token should fail as of Vault 0.8.0"
|
||||||
);
|
);
|
||||||
assertEquals(400, e.getStatusCode());
|
assertEquals(400, e.getStatusCode());
|
||||||
@@ -880,7 +880,7 @@ class HTTPVaultConnectorIT {
|
|||||||
.withoutDefaultPolicy()
|
.withoutDefaultPolicy()
|
||||||
.withType(Token.Type.BATCH)
|
.withType(Token.Type.BATCH)
|
||||||
.build();
|
.build();
|
||||||
res = assertDoesNotThrow(() -> connector.token().create(token4), "Token creation failed");
|
res = assertDoesNotThrow(() -> connector.createToken(token4), "Token creation failed");
|
||||||
assertTrue(
|
assertTrue(
|
||||||
// Expecting batch token. "hvb." Prefix as of Vault 1.10, "b." before.
|
// Expecting batch token. "hvb." Prefix as of Vault 1.10, "b." before.
|
||||||
res.getAuth().getClientToken().startsWith("b.") || res.getAuth().getClientToken().startsWith("hvb."),
|
res.getAuth().getClientToken().startsWith("b.") || res.getAuth().getClientToken().startsWith("hvb."),
|
||||||
@@ -908,12 +908,12 @@ class HTTPVaultConnectorIT {
|
|||||||
.withId("my-token")
|
.withId("my-token")
|
||||||
.withType(Token.Type.SERVICE)
|
.withType(Token.Type.SERVICE)
|
||||||
.build();
|
.build();
|
||||||
assertDoesNotThrow(() -> connector.token().create(token), "Token creation failed");
|
assertDoesNotThrow(() -> connector.createToken(token), "Token creation failed");
|
||||||
|
|
||||||
authRoot();
|
authRoot();
|
||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
TokenResponse res = assertDoesNotThrow(() -> connector.token().lookup("my-token"), "Token creation failed");
|
TokenResponse res = assertDoesNotThrow(() -> connector.lookupToken("my-token"), "Token creation failed");
|
||||||
assertEquals(token.getId(), res.getData().getId(), "Unexpected token ID");
|
assertEquals(token.getId(), res.getData().getId(), "Unexpected token ID");
|
||||||
assertEquals(1, res.getData().getPolicies().size(), "Unexpected number of policies");
|
assertEquals(1, res.getData().getPolicies().size(), "Unexpected number of policies");
|
||||||
assertTrue(res.getData().getPolicies().contains("root"), "Unexpected policy");
|
assertTrue(res.getData().getPolicies().contains("root"), "Unexpected policy");
|
||||||
@@ -936,14 +936,14 @@ class HTTPVaultConnectorIT {
|
|||||||
final TokenRole role = TokenRole.builder().build();
|
final TokenRole role = TokenRole.builder().build();
|
||||||
|
|
||||||
boolean creationRes = assertDoesNotThrow(
|
boolean creationRes = assertDoesNotThrow(
|
||||||
() -> connector.token().createOrUpdateRole(roleName, role),
|
() -> connector.createOrUpdateTokenRole(roleName, role),
|
||||||
"Token role creation failed"
|
"Token role creation failed"
|
||||||
);
|
);
|
||||||
assertTrue(creationRes, "Token role creation failed");
|
assertTrue(creationRes, "Token role creation failed");
|
||||||
|
|
||||||
// Read the role.
|
// Read the role.
|
||||||
TokenRoleResponse res = assertDoesNotThrow(
|
TokenRoleResponse res = assertDoesNotThrow(
|
||||||
() -> connector.token().readRole(roleName),
|
() -> connector.readTokenRole(roleName),
|
||||||
"Reading token role failed"
|
"Reading token role failed"
|
||||||
);
|
);
|
||||||
assertNotNull(res, "Token role response must not be null");
|
assertNotNull(res, "Token role response must not be null");
|
||||||
@@ -963,12 +963,12 @@ class HTTPVaultConnectorIT {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
creationRes = assertDoesNotThrow(
|
creationRes = assertDoesNotThrow(
|
||||||
() -> connector.token().createOrUpdateRole(role2),
|
() -> connector.createOrUpdateTokenRole(role2),
|
||||||
"Token role update failed"
|
"Token role update failed"
|
||||||
);
|
);
|
||||||
assertTrue(creationRes, "Token role update failed");
|
assertTrue(creationRes, "Token role update failed");
|
||||||
|
|
||||||
res = assertDoesNotThrow(() -> connector.token().readRole(roleName), "Reading token role failed");
|
res = assertDoesNotThrow(() -> connector.readTokenRole(roleName), "Reading token role failed");
|
||||||
assertNotNull(res, "Token role response must not be null");
|
assertNotNull(res, "Token role response must not be null");
|
||||||
assertNotNull(res.getData(), "Token role must not be null");
|
assertNotNull(res.getData(), "Token role must not be null");
|
||||||
assertEquals(roleName, res.getData().getName(), "Token role name not as expected");
|
assertEquals(roleName, res.getData().getName(), "Token role name not as expected");
|
||||||
@@ -977,15 +977,15 @@ class HTTPVaultConnectorIT {
|
|||||||
assertEquals(42, res.getData().getTokenNumUses(), "Unexpected number of token uses after update");
|
assertEquals(42, res.getData().getTokenNumUses(), "Unexpected number of token uses after update");
|
||||||
|
|
||||||
// List roles.
|
// List roles.
|
||||||
List<String> listRes = assertDoesNotThrow(() -> connector.token().listRoles(), "Listing token roles failed");
|
List<String> listRes = assertDoesNotThrow(() -> connector.listTokenRoles(), "Listing token roles failed");
|
||||||
assertNotNull(listRes, "Token role list must not be null");
|
assertNotNull(listRes, "Token role list must not be null");
|
||||||
assertEquals(List.of(roleName), listRes, "Unexpected token role list");
|
assertEquals(List.of(roleName), listRes, "Unexpected token role list");
|
||||||
|
|
||||||
// Delete the role.
|
// Delete the role.
|
||||||
creationRes = assertDoesNotThrow(() -> connector.token().deleteRole(roleName), "Token role deletion failed");
|
creationRes = assertDoesNotThrow(() -> connector.deleteTokenRole(roleName), "Token role deletion failed");
|
||||||
assertTrue(creationRes, "Token role deletion failed");
|
assertTrue(creationRes, "Token role deletion failed");
|
||||||
assertThrows(InvalidResponseException.class, () -> connector.token().readRole(roleName), "Reading nonexistent token role should fail");
|
assertThrows(InvalidResponseException.class, () -> connector.readTokenRole(roleName), "Reading nonexistent token role should fail");
|
||||||
assertThrows(InvalidResponseException.class, () -> connector.token().listRoles(), "Listing nonexistent token roles should fail");
|
assertThrows(InvalidResponseException.class, () -> connector.listTokenRoles(), "Listing nonexistent token roles should fail");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1000,14 +1000,14 @@ class HTTPVaultConnectorIT {
|
|||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
TransitResponse transitResponse = assertDoesNotThrow(
|
TransitResponse transitResponse = assertDoesNotThrow(
|
||||||
() -> connector.transit().encrypt("my-key", "dGVzdCBtZQ=="),
|
() -> connector.transitEncrypt("my-key", "dGVzdCBtZQ=="),
|
||||||
"Failed to encrypt via transit"
|
"Failed to encrypt via transit"
|
||||||
);
|
);
|
||||||
assertNotNull(transitResponse.getCiphertext());
|
assertNotNull(transitResponse.getCiphertext());
|
||||||
assertTrue(transitResponse.getCiphertext().startsWith("vault:v1:"));
|
assertTrue(transitResponse.getCiphertext().startsWith("vault:v1:"));
|
||||||
|
|
||||||
transitResponse = assertDoesNotThrow(
|
transitResponse = assertDoesNotThrow(
|
||||||
() -> connector.transit().encrypt("my-key", "test me".getBytes(UTF_8)),
|
() -> connector.transitEncrypt("my-key", "test me".getBytes(UTF_8)),
|
||||||
"Failed to encrypt binary data via transit"
|
"Failed to encrypt binary data via transit"
|
||||||
);
|
);
|
||||||
assertNotNull(transitResponse.getCiphertext());
|
assertNotNull(transitResponse.getCiphertext());
|
||||||
@@ -1022,7 +1022,7 @@ class HTTPVaultConnectorIT {
|
|||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
TransitResponse transitResponse = assertDoesNotThrow(
|
TransitResponse transitResponse = assertDoesNotThrow(
|
||||||
() -> connector.transit().decrypt("my-key", "vault:v1:1mhLVkBAR2nrFtIkJF/qg57DWfRj0FWgR6tvkGO8XOnL6sw="),
|
() -> connector.transitDecrypt("my-key", "vault:v1:1mhLVkBAR2nrFtIkJF/qg57DWfRj0FWgR6tvkGO8XOnL6sw="),
|
||||||
"Failed to decrypt via transit"
|
"Failed to decrypt via transit"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1036,21 +1036,21 @@ class HTTPVaultConnectorIT {
|
|||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
TransitResponse transitResponse = assertDoesNotThrow(
|
TransitResponse transitResponse = assertDoesNotThrow(
|
||||||
() -> connector.transit().hash("sha2-512", "dGVzdCBtZQ=="),
|
() -> connector.transitHash("sha2-512", "dGVzdCBtZQ=="),
|
||||||
"Failed to hash via transit"
|
"Failed to hash via transit"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEquals("7677af0ee4effaa9f35e9b1e82d182f79516ab8321786baa23002de7c06851059492dd37d5fc3791f17d81d4b58198d24a6fd8bbd62c42c1c30b371da500f193", transitResponse.getSum());
|
assertEquals("7677af0ee4effaa9f35e9b1e82d182f79516ab8321786baa23002de7c06851059492dd37d5fc3791f17d81d4b58198d24a6fd8bbd62c42c1c30b371da500f193", transitResponse.getSum());
|
||||||
|
|
||||||
TransitResponse transitResponseBase64 = assertDoesNotThrow(
|
TransitResponse transitResponseBase64 = assertDoesNotThrow(
|
||||||
() -> connector.transit().hash("sha2-256", "dGVzdCBtZQ==", "base64"),
|
() -> connector.transitHash("sha2-256", "dGVzdCBtZQ==", "base64"),
|
||||||
"Failed to hash via transit with base64 output"
|
"Failed to hash via transit with base64 output"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEquals("5DfYkW7cvGLkfy36cXhqmZcygEy9HpnFNB4WWXKOl1M=", transitResponseBase64.getSum());
|
assertEquals("5DfYkW7cvGLkfy36cXhqmZcygEy9HpnFNB4WWXKOl1M=", transitResponseBase64.getSum());
|
||||||
|
|
||||||
transitResponseBase64 = assertDoesNotThrow(
|
transitResponseBase64 = assertDoesNotThrow(
|
||||||
() -> connector.transit().hash("sha2-256", "test me".getBytes(UTF_8), "base64"),
|
() -> connector.transitHash("sha2-256", "test me".getBytes(UTF_8), "base64"),
|
||||||
"Failed to hash binary data via transit"
|
"Failed to hash binary data via transit"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1072,7 +1072,7 @@ class HTTPVaultConnectorIT {
|
|||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
|
|
||||||
List<AuthBackend> supportedBackends = assertDoesNotThrow(
|
List<AuthBackend> supportedBackends = assertDoesNotThrow(
|
||||||
() -> connector.sys().getAuthBackends(),
|
() -> connector.getAuthBackends(),
|
||||||
"Could not list supported auth backends"
|
"Could not list supported auth backends"
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1132,22 +1132,22 @@ class HTTPVaultConnectorIT {
|
|||||||
@Test
|
@Test
|
||||||
@DisplayName("Seal test")
|
@DisplayName("Seal test")
|
||||||
void sealTest() throws VaultConnectorException {
|
void sealTest() throws VaultConnectorException {
|
||||||
SealResponse sealStatus = connector.sys().sealStatus();
|
SealResponse sealStatus = connector.sealStatus();
|
||||||
assumeFalse(sealStatus.isSealed());
|
assumeFalse(sealStatus.isSealed());
|
||||||
|
|
||||||
// Unauthorized sealing should fail.
|
// Unauthorized sealing should fail.
|
||||||
assertThrows(VaultConnectorException.class, () -> connector.sys().seal(), "Unauthorized sealing succeeded");
|
assertThrows(VaultConnectorException.class, connector::seal, "Unauthorized sealing succeeded");
|
||||||
assertFalse(sealStatus.isSealed(), "Vault sealed, although sealing failed");
|
assertFalse(sealStatus.isSealed(), "Vault sealed, although sealing failed");
|
||||||
|
|
||||||
// Root user should be able to seal.
|
// Root user should be able to seal.
|
||||||
authRoot();
|
authRoot();
|
||||||
assumeTrue(connector.isAuthorized());
|
assumeTrue(connector.isAuthorized());
|
||||||
assertDoesNotThrow(() -> connector.sys().seal(), "Sealing failed");
|
assertDoesNotThrow(connector::seal, "Sealing failed");
|
||||||
sealStatus = connector.sys().sealStatus();
|
sealStatus = connector.sealStatus();
|
||||||
assertTrue(sealStatus.isSealed(), "Vault not sealed");
|
assertTrue(sealStatus.isSealed(), "Vault not sealed");
|
||||||
sealStatus = connector.sys().unseal(KEY2);
|
sealStatus = connector.unseal(KEY2);
|
||||||
assertTrue(sealStatus.isSealed(), "Vault unsealed with only 1 key");
|
assertTrue(sealStatus.isSealed(), "Vault unsealed with only 1 key");
|
||||||
sealStatus = connector.sys().unseal(KEY3);
|
sealStatus = connector.unseal(KEY3);
|
||||||
assertFalse(sealStatus.isSealed(), "Vault not unsealed");
|
assertFalse(sealStatus.isSealed(), "Vault not unsealed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1157,7 +1157,7 @@ class HTTPVaultConnectorIT {
|
|||||||
@Test
|
@Test
|
||||||
@DisplayName("Health test")
|
@DisplayName("Health test")
|
||||||
void healthTest() {
|
void healthTest() {
|
||||||
HealthResponse res = assertDoesNotThrow(() -> connector.sys().getHealth(), "Retrieving health status failed");
|
HealthResponse res = assertDoesNotThrow(connector::getHealth, "Retrieving health status failed");
|
||||||
assertNotNull(res, "Health response should be set");
|
assertNotNull(res, "Health response should be set");
|
||||||
assertEquals(VAULT_VERSION, res.getVersion(), "Unexpected version");
|
assertEquals(VAULT_VERSION, res.getVersion(), "Unexpected version");
|
||||||
assertTrue(res.isInitialized(), "Unexpected init status");
|
assertTrue(res.isInitialized(), "Unexpected init status");
|
||||||
@@ -1166,11 +1166,11 @@ class HTTPVaultConnectorIT {
|
|||||||
|
|
||||||
// No seal vault and verify correct status.
|
// No seal vault and verify correct status.
|
||||||
authRoot();
|
authRoot();
|
||||||
assertDoesNotThrow(() -> connector.sys().seal(), "Unexpected exception on sealing");
|
assertDoesNotThrow(connector::seal, "Unexpected exception on sealing");
|
||||||
SealResponse sealStatus = assertDoesNotThrow(() -> connector.sys().sealStatus());
|
SealResponse sealStatus = assertDoesNotThrow(connector::sealStatus);
|
||||||
assumeTrue(sealStatus.isSealed());
|
assumeTrue(sealStatus.isSealed());
|
||||||
connector.resetAuth(); // Should work unauthenticated
|
connector.resetAuth(); // Should work unauthenticated
|
||||||
res = assertDoesNotThrow(() -> connector.sys().getHealth(), "Retrieving health status failed when sealed");
|
res = assertDoesNotThrow(connector::getHealth, "Retrieving health status failed when sealed");
|
||||||
assertTrue(res.isSealed(), "Unexpected seal status");
|
assertTrue(res.isSealed(), "Unexpected seal status");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -54,51 +54,51 @@ class HTTPVaultConnectorTest {
|
|||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
void requestExceptionTest(WireMockRuntimeInfo wireMock) throws IOException, URISyntaxException {
|
void requestExceptionTest(WireMockRuntimeInfo wireMock) throws IOException, URISyntaxException {
|
||||||
try (var connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build()) {
|
HTTPVaultConnector connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withTimeout(250).build();
|
||||||
// Test invalid response code.
|
|
||||||
final int responseCode = 400;
|
|
||||||
mockHttpResponse(responseCode, "", "application/json");
|
|
||||||
VaultConnectorException e = assertThrows(
|
|
||||||
InvalidResponseException.class,
|
|
||||||
() -> connector.sys().getHealth(),
|
|
||||||
"Querying health status succeeded on invalid instance"
|
|
||||||
);
|
|
||||||
assertEquals("Invalid response code", e.getMessage(), "Unexpected exception message");
|
|
||||||
assertEquals(responseCode, ((InvalidResponseException) e).getStatusCode(), "Unexpected status code in exception");
|
|
||||||
assertNull(((InvalidResponseException) e).getResponse(), "Response message where none was expected");
|
|
||||||
|
|
||||||
// Simulate permission denied response.
|
// Test invalid response code.
|
||||||
mockHttpResponse(responseCode, "{\"errors\":[\"permission denied\"]}", "application/json");
|
final int responseCode = 400;
|
||||||
assertThrows(
|
mockHttpResponse(responseCode, "", "application/json");
|
||||||
PermissionDeniedException.class,
|
VaultConnectorException e = assertThrows(
|
||||||
() -> connector.sys().getHealth(),
|
InvalidResponseException.class,
|
||||||
"Querying health status succeeded on invalid instance"
|
connector::getHealth,
|
||||||
);
|
"Querying health status succeeded on invalid instance"
|
||||||
}
|
);
|
||||||
|
assertEquals("Invalid response code", e.getMessage(), "Unexpected exception message");
|
||||||
|
assertEquals(responseCode, ((InvalidResponseException) e).getStatusCode(), "Unexpected status code in exception");
|
||||||
|
assertNull(((InvalidResponseException) e).getResponse(), "Response message where none was expected");
|
||||||
|
|
||||||
|
// Simulate permission denied response.
|
||||||
|
mockHttpResponse(responseCode, "{\"errors\":[\"permission denied\"]}", "application/json");
|
||||||
|
assertThrows(
|
||||||
|
PermissionDeniedException.class,
|
||||||
|
connector::getHealth,
|
||||||
|
"Querying health status succeeded on invalid instance"
|
||||||
|
);
|
||||||
|
|
||||||
// Test exception thrown during request.
|
// Test exception thrown during request.
|
||||||
try (ServerSocket s = new ServerSocket(0);
|
try (ServerSocket s = new ServerSocket(0)) {
|
||||||
var connector = HTTPVaultConnector.builder("http://localst:" + s.getLocalPort() + "/").withTimeout(250).build()) {
|
connector = HTTPVaultConnector.builder("http://localst:" + s.getLocalPort() + "/").withTimeout(250).build();
|
||||||
var e = assertThrows(
|
|
||||||
ConnectionException.class,
|
|
||||||
() -> connector.sys().getHealth(),
|
|
||||||
"Querying health status succeeded on invalid instance"
|
|
||||||
);
|
|
||||||
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
|
||||||
assertInstanceOf(IOException.class, e.getCause(), "Unexpected cause");
|
|
||||||
}
|
}
|
||||||
|
e = assertThrows(
|
||||||
|
ConnectionException.class,
|
||||||
|
connector::getHealth,
|
||||||
|
"Querying health status succeeded on invalid instance"
|
||||||
|
);
|
||||||
|
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
||||||
|
assertInstanceOf(IOException.class, e.getCause(), "Unexpected cause");
|
||||||
|
|
||||||
// Now simulate a failing request that succeeds on second try.
|
// Now simulate a failing request that succeeds on second try.
|
||||||
try (var connector3 = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withNumberOfRetries(1).withTimeout(250).build()) {
|
connector = HTTPVaultConnector.builder(wireMock.getHttpBaseUrl()).withNumberOfRetries(1).withTimeout(250).build();
|
||||||
stubFor(
|
|
||||||
WireMock.any(anyUrl())
|
stubFor(
|
||||||
.willReturn(aResponse().withStatus(500))
|
WireMock.any(anyUrl())
|
||||||
.willReturn(aResponse().withStatus(500))
|
.willReturn(aResponse().withStatus(500))
|
||||||
.willReturn(aResponse().withStatus(500))
|
.willReturn(aResponse().withStatus(500))
|
||||||
.willReturn(aResponse().withStatus(200).withBody("{}").withHeader("Content-Type", "application/json"))
|
.willReturn(aResponse().withStatus(500))
|
||||||
);
|
.willReturn(aResponse().withStatus(200).withBody("{}").withHeader("Content-Type", "application/json"))
|
||||||
assertDoesNotThrow(() -> connector3.sys().getHealth(), "Request failed unexpectedly");
|
);
|
||||||
}
|
assertDoesNotThrow(connector::getHealth, "Request failed unexpectedly");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,7 +160,7 @@ class HTTPVaultConnectorTest {
|
|||||||
}
|
}
|
||||||
ConnectionException e = assertThrows(
|
ConnectionException e = assertThrows(
|
||||||
ConnectionException.class,
|
ConnectionException.class,
|
||||||
() -> connector.sys().sealStatus(),
|
connector::sealStatus,
|
||||||
"Querying seal status succeeded on invalid instance"
|
"Querying seal status succeeded on invalid instance"
|
||||||
);
|
);
|
||||||
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
||||||
@@ -178,7 +178,7 @@ class HTTPVaultConnectorTest {
|
|||||||
}
|
}
|
||||||
ConnectionException e = assertThrows(
|
ConnectionException e = assertThrows(
|
||||||
ConnectionException.class,
|
ConnectionException.class,
|
||||||
() -> connector.sys().getHealth(),
|
connector::getHealth,
|
||||||
"Querying health status succeeded on invalid instance"
|
"Querying health status succeeded on invalid instance"
|
||||||
);
|
);
|
||||||
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
assertEquals("Unable to connect to Vault server", e.getMessage(), "Unexpected exception message");
|
||||||
@@ -196,21 +196,21 @@ class HTTPVaultConnectorTest {
|
|||||||
mockHttpResponse(200, "invalid", "application/json");
|
mockHttpResponse(200, "invalid", "application/json");
|
||||||
|
|
||||||
// Now test the methods.
|
// Now test the methods.
|
||||||
assertParseError(() -> connector.sys().sealStatus(), "sys().sealStatus() succeeded on invalid instance");
|
assertParseError(connector::sealStatus, "sealStatus() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.sys().unseal("key"), "sys().unseal() succeeded on invalid instance");
|
assertParseError(() -> connector.unseal("key"), "unseal() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.sys().getHealth(), "sys().getHealth() succeeded on invalid instance");
|
assertParseError(connector::getHealth, "getHealth() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.sys().getAuthBackends(), "sys().getAuthBackends() succeeded on invalid instance");
|
assertParseError(connector::getAuthBackends, "getAuthBackends() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.authToken("token"), "authToken() succeeded on invalid instance");
|
assertParseError(() -> connector.authToken("token"), "authToken() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().lookup("roleName"), "appRole().lookup() succeeded on invalid instance");
|
assertParseError(() -> connector.lookupAppRole("roleName"), "lookupAppRole() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().getRoleID("roleName"), "appRole().getRoleID() succeeded on invalid instance");
|
assertParseError(() -> connector.getAppRoleID("roleName"), "getAppRoleID() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().createSecret("roleName"), "appRole().createSecret() succeeded on invalid instance");
|
assertParseError(() -> connector.createAppRoleSecret("roleName"), "createAppRoleSecret() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().lookupSecret("roleName", "secretID"), "appRole().lookupSecret() succeeded on invalid instance");
|
assertParseError(() -> connector.lookupAppRoleSecret("roleName", "secretID"), "lookupAppRoleSecret() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().listRoles(), "appRole().listRoles() succeeded on invalid instance");
|
assertParseError(connector::listAppRoles, "listAppRoles() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.appRole().listSecrets("roleName"), "appRole().listSecrets() succeeded on invalid instance");
|
assertParseError(() -> connector.listAppRoleSecrets("roleName"), "listAppRoleSecrets() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.read("key"), "read() succeeded on invalid instance");
|
assertParseError(() -> connector.read("key"), "read() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.list("path"), "list() succeeded on invalid instance");
|
assertParseError(() -> connector.list("path"), "list() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.renew("leaseID"), "renew() succeeded on invalid instance");
|
assertParseError(() -> connector.renew("leaseID"), "renew() succeeded on invalid instance");
|
||||||
assertParseError(() -> connector.token().lookup("token"), "token().lookup() succeeded on invalid instance");
|
assertParseError(() -> connector.lookupToken("token"), "lookupToken() succeeded on invalid instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertParseError(Executable executable, String message) {
|
private void assertParseError(Executable executable, String message) {
|
||||||
@@ -232,32 +232,32 @@ class HTTPVaultConnectorTest {
|
|||||||
// Now test the methods expecting a 204.
|
// Now test the methods expecting a 204.
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().create("appID", Collections.singletonList("policy")),
|
() -> connector.createAppRole("appID", Collections.singletonList("policy")),
|
||||||
"appRole().create() with 200 response succeeded"
|
"createAppRole() with 200 response succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.delete("roleName"),
|
() -> connector.deleteAppRole("roleName"),
|
||||||
"appRole().delete() with 200 response succeeded"
|
"deleteAppRole() with 200 response succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().setRoleID("roleName", "roleID"),
|
() -> connector.setAppRoleID("roleName", "roleID"),
|
||||||
"appRole().setRoleID() with 200 response succeeded"
|
"setAppRoleID() with 200 response succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().destroySecret("roleName", "secretID"),
|
() -> connector.destroyAppRoleSecret("roleName", "secretID"),
|
||||||
"appRole().destroySecret() with 200 response succeeded"
|
"destroyAppRoleSecret() with 200 response succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
InvalidResponseException.class,
|
InvalidResponseException.class,
|
||||||
() -> connector.appRole().destroySecret("roleName", "secretUD"),
|
() -> connector.destroyAppRoleSecret("roleName", "secretUD"),
|
||||||
"appRole().destroySecret() with 200 response succeeded"
|
"destroyAppRoleSecret() with 200 response succeeded"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertThrows(
|
assertThrows(
|
||||||
|
@@ -39,6 +39,7 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
"number", 1337
|
"number", 1337
|
||||||
);
|
);
|
||||||
private static final List<String> TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24");
|
private static final List<String> TEST_CIDR = List.of("203.0.113.0/24", "198.51.100.0/24");
|
||||||
|
private static final List<String> TEST_TOKEN_CIDR = List.of("192.0.2.0/24", "198.51.100.0/24");
|
||||||
|
|
||||||
AppRoleSecretTest() {
|
AppRoleSecretTest() {
|
||||||
super(AppRoleSecret.class);
|
super(AppRoleSecret.class);
|
||||||
@@ -61,6 +62,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
assertNull(secret.getMetadata());
|
assertNull(secret.getMetadata());
|
||||||
assertNull(secret.getCidrList());
|
assertNull(secret.getCidrList());
|
||||||
assertEquals("", secret.getCidrListString());
|
assertEquals("", secret.getCidrListString());
|
||||||
|
assertNull(secret.getTokenBoundCidrs());
|
||||||
|
assertEquals("", secret.getTokenBoundCidrsString());
|
||||||
assertNull(secret.getCreationTime());
|
assertNull(secret.getCreationTime());
|
||||||
assertNull(secret.getExpirationTime());
|
assertNull(secret.getExpirationTime());
|
||||||
assertNull(secret.getLastUpdatedTime());
|
assertNull(secret.getLastUpdatedTime());
|
||||||
@@ -74,6 +77,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
assertNull(secret.getMetadata());
|
assertNull(secret.getMetadata());
|
||||||
assertNull(secret.getCidrList());
|
assertNull(secret.getCidrList());
|
||||||
assertEquals("", secret.getCidrListString());
|
assertEquals("", secret.getCidrListString());
|
||||||
|
assertNull(secret.getTokenBoundCidrs());
|
||||||
|
assertEquals("", secret.getTokenBoundCidrsString());
|
||||||
assertNull(secret.getCreationTime());
|
assertNull(secret.getCreationTime());
|
||||||
assertNull(secret.getExpirationTime());
|
assertNull(secret.getExpirationTime());
|
||||||
assertNull(secret.getLastUpdatedTime());
|
assertNull(secret.getLastUpdatedTime());
|
||||||
@@ -87,6 +92,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
assertEquals(TEST_META, secret.getMetadata());
|
assertEquals(TEST_META, secret.getMetadata());
|
||||||
assertEquals(TEST_CIDR, secret.getCidrList());
|
assertEquals(TEST_CIDR, secret.getCidrList());
|
||||||
assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString());
|
assertEquals(String.join(",", TEST_CIDR), secret.getCidrListString());
|
||||||
|
assertNull(secret.getTokenBoundCidrs());
|
||||||
|
assertEquals("", secret.getTokenBoundCidrsString());
|
||||||
assertNull(secret.getCreationTime());
|
assertNull(secret.getCreationTime());
|
||||||
assertNull(secret.getExpirationTime());
|
assertNull(secret.getExpirationTime());
|
||||||
assertNull(secret.getLastUpdatedTime());
|
assertNull(secret.getLastUpdatedTime());
|
||||||
@@ -108,6 +115,15 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
secret.setCidrList(null);
|
secret.setCidrList(null);
|
||||||
assertNull(secret.getCidrList());
|
assertNull(secret.getCidrList());
|
||||||
assertEquals("", secret.getCidrListString());
|
assertEquals("", secret.getCidrListString());
|
||||||
|
|
||||||
|
assertNull(secret.getTokenBoundCidrs());
|
||||||
|
assertEquals("", secret.getTokenBoundCidrsString());
|
||||||
|
secret.setTokenBoundCidrs(TEST_TOKEN_CIDR);
|
||||||
|
assertEquals(TEST_TOKEN_CIDR, secret.getTokenBoundCidrs());
|
||||||
|
assertEquals(String.join(",", TEST_TOKEN_CIDR), secret.getTokenBoundCidrsString());
|
||||||
|
secret.setTokenBoundCidrs(null);
|
||||||
|
assertNull(secret.getTokenBoundCidrs());
|
||||||
|
assertEquals("", secret.getTokenBoundCidrsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,7 +175,8 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
|
|
||||||
// Those fields should be deserialized from JSON though.
|
// Those fields should be deserialized from JSON though.
|
||||||
String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
|
String secretJson4 = "{\"secret_id\":\"abc123\",\"metadata\":{\"number\":1337,\"foo\":\"bar\"}," +
|
||||||
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
|
"\"cidr_list\":[\"203.0.113.0/24\",\"198.51.100.0/24\"],\"cidr_list\":[\"192.0.2.0/24\",\"198.51.100.0/24\"]," +
|
||||||
|
"\"secret_id_accessor\":\"TEST_ACCESSOR\"," +
|
||||||
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
|
"\"creation_time\":\"TEST_CREATION\",\"expiration_time\":\"TEST_EXPIRATION\"," +
|
||||||
"\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}";
|
"\"last_updated_time\":\"TEST_LASTUPDATE\",\"secret_id_num_uses\":678,\"secret_id_ttl\":12345}";
|
||||||
secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed");
|
secret2 = assertDoesNotThrow(() -> objectMapper.readValue(secretJson4, AppRoleSecret.class), "Deserialization failed");
|
||||||
@@ -181,6 +198,7 @@ class AppRoleSecretTest extends AbstractModelTest<AppRoleSecret> {
|
|||||||
|
|
||||||
private static String commaSeparatedToList(String json) {
|
private static String commaSeparatedToList(String json) {
|
||||||
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]")
|
return json.replaceAll("\"cidr_list\":\"([^\"]*)\"", "\"cidr_list\":[$1]")
|
||||||
|
.replaceAll("\"token_bound_cidrs\":\"([^\"]*)\"", "\"token_bound_cidrs\":[$1]")
|
||||||
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
|
.replaceAll("(\\d+\\.\\d+\\.\\d+\\.\\d+/\\d+)", "\"$1\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user