8
8
import io .github .jopenlibs .vault .response .AuthResponse ;
9
9
import io .github .jopenlibs .vault .response .LogicalResponse ;
10
10
import io .github .jopenlibs .vault .response .LookupResponse ;
11
+ import io .github .jopenlibs .vault .response .UnwrapResponse ;
12
+ import io .github .jopenlibs .vault .response .WrapResponse ;
11
13
import io .github .jopenlibs .vault .rest .Rest ;
12
14
import io .github .jopenlibs .vault .rest .RestResponse ;
13
15
import java .io .Serializable ;
14
16
import java .nio .charset .StandardCharsets ;
15
17
import java .util .List ;
16
18
import java .util .Map ;
19
+ import java .util .Objects ;
17
20
import java .util .UUID ;
18
21
19
22
@@ -308,36 +311,39 @@ public AuthResponse createToken(final TokenRequest tokenRequest, final String to
308
311
// Parse parameters to JSON
309
312
final JsonObject jsonObject = Json .object ();
310
313
311
- if (tokenRequest .id != null ) jsonObject .add ("id" , tokenRequest .id .toString ());
312
- if (tokenRequest .polices != null && !tokenRequest .polices .isEmpty ()) {
313
- jsonObject .add ("policies" , Json .array (tokenRequest .polices .toArray (new String [tokenRequest .polices .size ()])));//NOPMD
314
+ if (tokenRequest .getId () != null ) jsonObject .add ("id" , tokenRequest .getId ().toString ());
315
+ if (tokenRequest .getPolices () != null && !tokenRequest .getPolices ().isEmpty ()) {
316
+ jsonObject .add (
317
+ "policies" ,
318
+ Json .array (tokenRequest .getPolices ().toArray (new String [0 ]))
319
+ ); //NOPMD
314
320
}
315
- if (tokenRequest .meta != null && !tokenRequest .meta .isEmpty ()) {
321
+ if (tokenRequest .getMeta () != null && !tokenRequest .getMeta () .isEmpty ()) {
316
322
final JsonObject metaMap = Json .object ();
317
- for (final Map .Entry <String , String > entry : tokenRequest .meta .entrySet ()) {
323
+ for (final Map .Entry <String , String > entry : tokenRequest .getMeta () .entrySet ()) {
318
324
metaMap .add (entry .getKey (), entry .getValue ());
319
325
}
320
326
jsonObject .add ("meta" , metaMap );
321
327
}
322
- if (tokenRequest .noParent != null ) jsonObject .add ("no_parent" , tokenRequest .noParent );
323
- if (tokenRequest .noDefaultPolicy != null )
324
- jsonObject .add ("no_default_policy" , tokenRequest .noDefaultPolicy );
325
- if (tokenRequest .ttl != null ) jsonObject .add ("ttl" , tokenRequest .ttl );
326
- if (tokenRequest .displayName != null ) jsonObject .add ("display_name" , tokenRequest .displayName );
327
- if (tokenRequest .numUses != null ) jsonObject .add ("num_uses" , tokenRequest .numUses );
328
- if (tokenRequest .renewable != null ) jsonObject .add ("renewable" , tokenRequest .renewable );
329
- if (tokenRequest .type != null ) jsonObject .add ("type" , tokenRequest .type );
330
- if (tokenRequest .explicitMaxTtl != null ) jsonObject .add ("explicit_max_ttl" , tokenRequest .explicitMaxTtl );
331
- if (tokenRequest .period != null ) jsonObject .add ("period" , tokenRequest .period );
332
- if (tokenRequest .entityAlias != null ) jsonObject .add ("entity_alias" , tokenRequest .entityAlias );
328
+ if (tokenRequest .getNoParent () != null ) jsonObject .add ("no_parent" , tokenRequest .getNoParent () );
329
+ if (tokenRequest .getNoDefaultPolicy () != null )
330
+ jsonObject .add ("no_default_policy" , tokenRequest .getNoDefaultPolicy () );
331
+ if (tokenRequest .getTtl () != null ) jsonObject .add ("ttl" , tokenRequest .getTtl () );
332
+ if (tokenRequest .getDisplayName () != null ) jsonObject .add ("display_name" , tokenRequest .getDisplayName () );
333
+ if (tokenRequest .getNumUses () != null ) jsonObject .add ("num_uses" , tokenRequest .getNumUses () );
334
+ if (tokenRequest .getRenewable () != null ) jsonObject .add ("renewable" , tokenRequest .getRenewable () );
335
+ if (tokenRequest .getType () != null ) jsonObject .add ("type" , tokenRequest .getType () );
336
+ if (tokenRequest .getExplicitMaxTtl () != null ) jsonObject .add ("explicit_max_ttl" , tokenRequest .getExplicitMaxTtl () );
337
+ if (tokenRequest .getPeriod () != null ) jsonObject .add ("period" , tokenRequest .getPeriod () );
338
+ if (tokenRequest .getEntityAlias () != null ) jsonObject .add ("entity_alias" , tokenRequest .getEntityAlias () );
333
339
final String requestJson = jsonObject .toString ();
334
340
335
341
final StringBuilder urlBuilder = new StringBuilder (config .getAddress ())//NOPMD
336
342
.append ("/v1/auth/" )
337
343
.append (mount )
338
344
.append ("/create" );
339
- if (tokenRequest .role != null ) {
340
- urlBuilder .append ("/" ).append (tokenRequest .role );
345
+ if (tokenRequest .getRole () != null ) {
346
+ urlBuilder .append ("/" ).append (tokenRequest .getRole () );
341
347
}
342
348
final String url = urlBuilder .toString ();
343
349
@@ -1502,37 +1508,58 @@ public void revokeSelf(final String tokenAuthMount) throws VaultException {
1502
1508
* @throws VaultException If any error occurs, or unexpected response received from Vault
1503
1509
* @see #unwrap(String)
1504
1510
*/
1505
- public AuthResponse unwrap () throws VaultException {
1511
+ public UnwrapResponse unwrap () throws VaultException {
1506
1512
return unwrap (null );
1507
1513
}
1508
1514
1509
1515
/**
1510
- * <p>Returns the original response inside the given wrapped auth token. This method is useful if you need to unwrap
1511
- * a token, while being already authenticated. Do NOT authenticate in vault with your wrapping token, since it will
1512
- * both fail authentication and invalidate the wrapping token at the same time. See {@link #unwrap()} if you need to
1513
- * do that without being authenticated.</p>
1516
+ * <p>Provide access to the {@code /sys/wrapping/unwrap} endpoint.</p>
1517
+ *
1518
+ * <p>Returns the original response inside the given wrapping token. Unlike simply reading
1519
+ * {@code cubbyhole/response} (which is deprecated), this endpoint provides additional
1520
+ * validation checks on the token, returns the original value on the wire rather than
1521
+ * a JSON string representation of it, and ensures that the response is properly audit-logged.</p>
1522
+ *
1523
+ * <p> This endpoint can be used by using a wrapping token as the client token in the API call,
1524
+ * in which case the token parameter is not required; or, a different token with permissions
1525
+ * to access this endpoint can make the call and pass in the wrapping token in
1526
+ * the token parameter. Do not use the wrapping token in both locations;
1527
+ * this will cause the wrapping token to be revoked but the value to be unable to be looked up,
1528
+ * as it will basically be a double-use of the token!</p>
1514
1529
*
1515
1530
* <p>In the example below, {@code authToken} is NOT your wrapped token, and should have unwrapping permissions.
1516
- * The unwrapped token in {@code unwrappedToken }. Example usage:</p>
1531
+ * The unwrapped data in {@link UnwrapResponse#getData() }. Example usage:</p>
1517
1532
*
1518
1533
* <blockquote>
1519
1534
* <pre>{@code
1520
1535
* final String authToken = "...";
1521
1536
* final String wrappingToken = "...";
1522
1537
* final VaultConfig config = new VaultConfig().address(...).token(authToken).build();
1523
1538
* final Vault vault = new Vault(config);
1524
- * final AuthResponse response = vault.auth().unwrap(wrappingToken);
1525
- * final String unwrappedToken = response.getAuthClientToken();
1539
+ *
1540
+ * final WrapResponse wrapResponse = vault.auth().wrap(
1541
+ * // Data to wrap
1542
+ * new JsonObject()
1543
+ * .add("foo", "bar")
1544
+ * .add("zoo", "zar"),
1545
+ *
1546
+ * // TTL of the response-wrapping token
1547
+ * 60
1548
+ * );
1549
+ *
1550
+ * final UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
1551
+ * final JsonObject unwrappedData = response.getData(); // original data
1526
1552
* }</pre>
1527
1553
* </blockquote>
1528
1554
*
1529
- * @param wrappedToken Specifies the wrapping token ID, do NOT also put this in your {@link VaultConfig#token },
1530
- * if token is {@code null}, this method will unwrap the auth token in {@link VaultConfig#token }
1555
+ * @param wrappedToken Specifies the wrapping token ID, do NOT also put this in your {@link VaultConfig#getToken() },
1556
+ * if token is {@code null}, this method will unwrap the auth token in {@link VaultConfig#getToken() }
1531
1557
* @return The response information returned from Vault
1532
1558
* @throws VaultException If any error occurs, or unexpected response received from Vault
1559
+ * @see #wrap(JsonObject, int)
1533
1560
* @see #unwrap()
1534
1561
*/
1535
- public AuthResponse unwrap (final String wrappedToken ) throws VaultException {
1562
+ public UnwrapResponse unwrap (final String wrappedToken ) throws VaultException {
1536
1563
int retryCount = 0 ;
1537
1564
while (true ) {
1538
1565
try {
@@ -1567,7 +1594,7 @@ public AuthResponse unwrap(final String wrappedToken) throws VaultException {
1567
1594
if (!mimeType .equals ("application/json" )) {
1568
1595
throw new VaultException ("Vault responded with MIME type: " + mimeType , restResponse .getStatus ());
1569
1596
}
1570
- return new AuthResponse (restResponse , retryCount );
1597
+ return new UnwrapResponse (restResponse , retryCount );
1571
1598
} catch (final Exception e ) {
1572
1599
// If there are retries to perform, then pause for the configured interval and then execute the
1573
1600
// loop again...
@@ -1589,4 +1616,114 @@ public AuthResponse unwrap(final String wrappedToken) throws VaultException {
1589
1616
}
1590
1617
}
1591
1618
1619
+ /**
1620
+ * <p>Provide access to the {@code /sys/wrapping/wrap} endpoint.</p>
1621
+ *
1622
+ * <p>This provides a powerful mechanism for information sharing in many environments.
1623
+ * In the types of scenarios, often the best practical option is to provide cover
1624
+ * for the secret information, be able to detect malfeasance (interception, tampering),
1625
+ * and limit lifetime of the secret's exposure.
1626
+ * Response wrapping performs all three of these duties:</p>
1627
+ *
1628
+ * <ul>
1629
+ * <li>It provides cover by ensuring that the value being transmitted across the wire is
1630
+ * not the actual secret but a reference to such a secret, namely the response-wrapping token.
1631
+ * Information stored in logs or captured along the way do not directly see the sensitive information.
1632
+ * </li>
1633
+ * <li>It provides malfeasance detection by ensuring that only a single party can ever
1634
+ * unwrap the token and see what's inside. A client receiving a token that cannot be unwrapped
1635
+ * can trigger an immediate security incident. In addition, a client can inspect
1636
+ * a given token before unwrapping to ensure that its origin is from the expected
1637
+ * location in Vault.
1638
+ * </li>
1639
+ * <li>It limits the lifetime of secret exposure because the response-wrapping token has
1640
+ * a lifetime that is separate from the wrapped secret (and often can be much shorter),
1641
+ * so if a client fails to come up and unwrap the token, the token can expire very quickly.
1642
+ * </li>
1643
+ * </ul>
1644
+ *
1645
+ * <blockquote>
1646
+ * <pre>{@code
1647
+ * final String authToken = "...";
1648
+ * final String wrappingToken = "...";
1649
+ * final VaultConfig config = new VaultConfig().address(...).token(authToken).build();
1650
+ * final Vault vault = new Vault(config);
1651
+ *
1652
+ * final WrapResponse wrapResponse = vault.auth().wrap(
1653
+ * // Data to wrap
1654
+ * new JsonObject()
1655
+ * .add("foo", "bar")
1656
+ * .add("zoo", "zar"),
1657
+ *
1658
+ * // TTL of the response-wrapping token
1659
+ * 60
1660
+ * );
1661
+ *
1662
+ * final UnwrapResponse unwrapResponse = vault.auth().unwrap(wrapResponse.getToken());
1663
+ * final JsonObject unwrappedData = response.getData(); // original data
1664
+ * }</pre>
1665
+ * </blockquote>
1666
+ *
1667
+ * @param jsonObject User data to wrap.
1668
+ * @param ttlInSec Wrap TTL in seconds
1669
+ * @return The response information returned from Vault
1670
+ * @throws VaultException If any error occurs, or unexpected response received from Vault
1671
+ * @see #unwrap(String)
1672
+ */
1673
+ public WrapResponse wrap (final JsonObject jsonObject , int ttlInSec ) throws VaultException {
1674
+ Objects .requireNonNull (jsonObject );
1675
+
1676
+ int retryCount = 0 ;
1677
+ while (true ) {
1678
+ try {
1679
+ // Parse parameters to JSON
1680
+ final String requestJson = jsonObject .toString ();
1681
+ final String url = config .getAddress () + "/v1/sys/wrapping/wrap" ;
1682
+
1683
+ // HTTP request to Vault
1684
+ final RestResponse restResponse = new Rest ()
1685
+ .url (url )
1686
+ .header ("X-Vault-Token" , config .getToken ())
1687
+ .header ("X-Vault-Wrap-TTL" , Integer .toString (ttlInSec ))
1688
+ .header ("X-Vault-Namespace" , this .nameSpace )
1689
+ .body (requestJson .getBytes (StandardCharsets .UTF_8 ))
1690
+ .connectTimeoutSeconds (config .getOpenTimeout ())
1691
+ .readTimeoutSeconds (config .getReadTimeout ())
1692
+ .sslVerification (config .getSslConfig ().isVerify ())
1693
+ .sslContext (config .getSslConfig ().getSslContext ())
1694
+ .post ();
1695
+
1696
+ // Validate restResponse
1697
+ if (restResponse .getStatus () != 200 ) {
1698
+ throw new VaultException ("Vault responded with HTTP status code: " + restResponse .getStatus ()
1699
+ + "\n Response body: " + new String (restResponse .getBody (), StandardCharsets .UTF_8 ),
1700
+ restResponse .getStatus ());
1701
+ }
1702
+
1703
+ final String mimeType = restResponse .getMimeType () == null ? "null" : restResponse .getMimeType ();
1704
+ if (!mimeType .equals ("application/json" )) {
1705
+ throw new VaultException ("Vault responded with MIME type: " + mimeType , restResponse .getStatus ());
1706
+ }
1707
+
1708
+ return new WrapResponse (restResponse , retryCount );
1709
+ } catch (final Exception e ) {
1710
+ // If there are retries to perform, then pause for the configured interval and then execute the
1711
+ // loop again...
1712
+ if (retryCount < config .getMaxRetries ()) {
1713
+ retryCount ++;
1714
+ try {
1715
+ final int retryIntervalMilliseconds = config .getRetryIntervalMilliseconds ();
1716
+ Thread .sleep (retryIntervalMilliseconds );
1717
+ } catch (InterruptedException e1 ) {
1718
+ e1 .printStackTrace ();
1719
+ }
1720
+ } else if (e instanceof VaultException ) {
1721
+ // ... otherwise, give up.
1722
+ throw (VaultException ) e ;
1723
+ } else {
1724
+ throw new VaultException (e );
1725
+ }
1726
+ }
1727
+ }
1728
+ }
1592
1729
}
0 commit comments