forked from BetterCloud/vault-java-driver
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVaultConfig.java
More file actions
302 lines (281 loc) · 13.9 KB
/
VaultConfig.java
File metadata and controls
302 lines (281 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
package com.bettercloud.vault;
import lombok.Getter;
import java.io.Serializable;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>A container for the configuration settings needed to initialize a <code>Vault</code> driver instance.</p>
*
* <p>Construct instances of this class using a builder pattern, calling setter methods for each value and then
* terminating with a call to build():</p>
*
* <blockquote>
* <pre>{@code
* final VaultConfig config = new VaultConfig()
* .address("http://127.0.0.1:8200")
* .token("eace6676-4d78-c687-4e54-03cad00e3abf")
* .sslConfig(new SslConfig().verify(false).build())
* .timeout(30)
* .build();
* }</pre>
* </blockquote>
*
* @see SslConfig
*/
public class VaultConfig implements Serializable {
protected static final String VAULT_TOKEN = "VAULT_TOKEN";
private static final String VAULT_ADDR = "VAULT_ADDR";
private static final String VAULT_OPEN_TIMEOUT = "VAULT_OPEN_TIMEOUT";
private static final String VAULT_READ_TIMEOUT = "VAULT_READ_TIMEOUT";
@Getter
private Map<String, String> secretsEnginePathMap = new ConcurrentHashMap<>();
@Getter
private String address;
@Getter
private String token;
@Getter
private SslConfig sslConfig;
@Getter
private Integer openTimeout;
@Getter
private Integer readTimeout;
@Getter
private int maxRetries;
@Getter
private int retryIntervalMilliseconds;
@Getter
private Integer globalEngineVersion;
@Getter
private String nameSpace;
private EnvironmentLoader environmentLoader;
/**
* <p>The code used to load environment variables is encapsulated here, so that a mock version of that environment
* loader can be used by unit tests.</p>
*
* <p>This method is primarily intended for use by unit tests, to inject a mock environment variable when
* constructing a <code>VaultConfig</code> instance using the builder pattern approach rather than the convenience
* constructor. This method's access level was therefore originally set to <code>protected</code>, but was bumped
* up to <code>public</code> due to community request for the ability to disable environment loading altogether
* (see https://github.com/BetterCloud/vault-java-driver/issues/77).</p>
*
* <p>Note that if you do override this, however, then obviously all of the environment checking discussed in the
* documentation becomes disabled.</p>
*
* @param environmentLoader An environment variable loader implementation (presumably a mock)
* @return This object, with environmentLoader populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig environmentLoader(final EnvironmentLoader environmentLoader) {
this.environmentLoader = environmentLoader;
return this;
}
/**
* <p>Optional. Sets a global namespace to the Vault server instance, if desired. Otherwise, namespace can be applied individually to any read / write / auth call.
*
* <p>Namespace support requires Vault Enterprise Pro, please see https://learn.hashicorp.com/vault/operations/namespaces</p>
*
* @param nameSpace The namespace to use globally in this VaultConfig instance.
* @return This object, with the namespace populated, ready for additional builder-pattern method calls or else
* finalization with the build() method
*
* @throws VaultException If any error occurs
*/
public VaultConfig nameSpace(final String nameSpace) throws VaultException {
if (nameSpace != null && !nameSpace.isEmpty()) {
this.nameSpace = nameSpace;
return this;
} else throw new VaultException("A namespace cannot be empty.");
}
/**
* <p>Sets the KV Secrets Engine version of the Vault server instance.
*
* <p>If no version is explicitly set, it will be defaulted to version 2, the current version.</p>
*
* @param globalEngineVersion The Vault KV Secrets Engine version
* @return This object, with KV Secrets Engine version populated, ready for additional builder-pattern method calls or else
* finalization with the build() method
*/
public VaultConfig engineVersion(final Integer globalEngineVersion) {
this.globalEngineVersion = globalEngineVersion;
return this;
}
/**
* <p>Sets the address (URL) of the Vault server instance to which API calls should be sent.
* E.g. <code>http://127.0.0.1:8200</code>.</p>
*
* <p>If no address is explicitly set, the object will look to the <code>VAULT_ADDR</code> environment variable.</p>
*
* <p><code>address</code> is required for the Vault driver to function. If you do not supply it explicitly AND no
* environment variable value is found, then initialization of the <code>VaultConfig</code> object will fail.</p>
*
* @param address The Vault server base URL
* @return This object, with address populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig address(final String address) {
this.address = address;
return this;
}
/**
* <p>Sets the token used to access Vault.</p>
*
* <p>If no token is explicitly set, then the object will look to the <code>VAULT_TOKEN</code> environment
* variable.</p>
*
* <p>There are some cases where you might want to instantiate a <code>VaultConfig</code> object without a token
* (e.g. you plan to retrieve a token programmatically, with a call to the "userpass" auth backend, and populate
* it prior to making any other API calls).</p>
*
* @param token The token to use for accessing Vault
* @return This object, with token populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig token(final String token) {
this.token = token;
return this;
}
/**
* <p>Sets the secrets Engine paths used by Vault.</p>
*
* @param secretEngineVersions paths to use for accessing Vault secrets.
* Key: secret path, value: Engine version to use.
* Example map: "/secret/foo" , "1",
* "/secret/bar", "2"
* @return This object, with secrets paths populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig secretsEnginePathMap(final Map<String, String> secretEngineVersions) {
this.secretsEnginePathMap = new ConcurrentHashMap<>(secretEngineVersions);
return this;
}
/**
* <p>Sets the secrets Engine version be used by Vault for the provided path.</p>
*
* @param path the path to use for accessing Vault secrets.
* Example "/secret/foo"
* @return This object, with a new entry in the secrets paths map, ready for additional builder-pattern method calls or else finalization with
* the build() method
*/
public VaultConfig putSecretsEngineVersionForPath(String path, String version) {
this.secretsEnginePathMap.put(path, version);
return this;
}
/**
* <p>A container for SSL-related configuration options (e.g. certificates).</p>
*
* <p>Although typically necessary in most production environments, this is not strictly required (e.g. if your
* Vault server address begins with "http://" instead of "https://", then any SSL config will be ignored).
* However, if your Vault server uses HTTPS, and you wish to skip SSL certificate verification (NOT RECOMMENDED
* FOR PRODUCTION!), then you must supply an <code>SslConfig</code> object with {@link SslConfig#verify(Boolean)}
* explicitly set to <code>false</code>.</p>
*
* @param sslConfig SSL-related configuration options
* @return This object, with SSL configuration options populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig sslConfig(final SslConfig sslConfig) {
this.sslConfig = sslConfig;
return this;
}
/**
* <p>The number of seconds to wait before giving up on establishing an HTTP(S) connection to the Vault server.</p>
*
* <p>If no openTimeout is explicitly set, then the object will look to the <code>VAULT_OPEN_TIMEOUT</code>
* environment variable.</p>
*
* @param openTimeout Number of seconds to wait for an HTTP(S) connection to successfully establish
* @return This object, with openTimeout populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig openTimeout(final Integer openTimeout) {
this.openTimeout = openTimeout;
return this;
}
/**
* <p>After an HTTP(S) connection has already been established, this is the number of seconds to wait for all
* data to finish downloading.</p>
*
* <p>If no readTimeout is explicitly set, then the object will look to the <code>VAULT_READ_TIMEOUT</code>
* environment variable.</p>
*
* @param readTimeout Number of seconds to wait for all data to be retrieved from an established HTTP(S) connection
* @return This object, with readTimeout populated, ready for additional builder-pattern method calls or else finalization with the build() method
*/
public VaultConfig readTimeout(final Integer readTimeout) {
this.readTimeout = readTimeout;
return this;
}
/**
* <p>Sets the maximum number of times that an API operation will retry upon failure.</p>
*
* <p>This method is not meant to be called from application-level code outside of this package (hence
* the <code>protected</code> access level. It is meant to be invoked via <code>Vault.withRetries()</code>
* in a builder pattern DSL-style.</p>
*
* @param maxRetries The number of times that API operations will be retried when a failure occurs.
*/
void setMaxRetries(final int maxRetries) {
this.maxRetries = maxRetries;
}
/**
* <p>Sets the period of time (in milliseconds) that the driver will wait in between retry attempts for a
* failing API operation.</p>
*
* <p>This method is not meant to be called from application-level code outside of this package (hence
* the <code>protected</code> access level. It is meant to be invoked via <code>Vault.withRetries()</code>
* in a builder pattern DSL-style.</p>
*
* @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in between retries.
*/
void setRetryIntervalMilliseconds(final int retryIntervalMilliseconds) {
this.retryIntervalMilliseconds = retryIntervalMilliseconds;
}
/**
* <p>Sets the global Engine version for this Vault Config instance. If no KV Engine version map is provided, use this version
* globally.</p>
* If the provided KV Engine version map does not contain a requested secret, or when writing new secrets, fall back to this version.
*
* @param engineVersion The version of the Vault KV Engine to use globally.
*/
void setEngineVersion(final Integer engineVersion) {
this.globalEngineVersion = engineVersion;
}
/**
* <p>This is the terminating method in the builder pattern. The method that validates all of the fields that
* has been set already, uses environment variables when available to populate any unset fields, and returns
* a <code>VaultConfig</code> object that is ready for use.</p>
*
* @return This object, with all available config options parsed and loaded
* @throws VaultException If the <code>address</code> field was left unset, and there is no <code>VAULT_ADDR</code> environment variable value with which to populate it.
*/
public VaultConfig build() throws VaultException {
if (this.environmentLoader == null) {
this.environmentLoader = new EnvironmentLoader();
}
if (this.address == null) {
final String addressFromEnv = environmentLoader.loadVariable(VAULT_ADDR);
if (addressFromEnv != null) {
this.address = addressFromEnv;
} else {
throw new VaultException("No address is set");
}
}
if (this.token == null && environmentLoader.loadVariable(VAULT_TOKEN) != null) {
this.token = environmentLoader.loadVariable(VAULT_TOKEN);
}
if (this.openTimeout == null && environmentLoader.loadVariable(VAULT_OPEN_TIMEOUT) != null) {
try {
this.openTimeout = Integer.valueOf(environmentLoader.loadVariable(VAULT_OPEN_TIMEOUT));
} catch (NumberFormatException e) {
System.err.printf("The " + VAULT_OPEN_TIMEOUT + " environment variable contains value \"%s\", which cannot be parsed as an integer timeout period.%n",
environmentLoader.loadVariable(VAULT_OPEN_TIMEOUT));
}
}
if (this.readTimeout == null && environmentLoader.loadVariable(VAULT_READ_TIMEOUT) != null) {
try {
this.readTimeout = Integer.valueOf(environmentLoader.loadVariable(VAULT_READ_TIMEOUT));
} catch (NumberFormatException e) {
System.err.printf("The " + VAULT_READ_TIMEOUT + " environment variable contains value \"%s\", which cannot be parsed as an integer timeout period.%n",
environmentLoader.loadVariable(VAULT_READ_TIMEOUT));
}
}
if (this.sslConfig == null) {
this.sslConfig = new SslConfig().environmentLoader(this.environmentLoader).build();
}
return this;
}
}