1313
1414package com .amazonaws .encryptionsdk .kms ;
1515
16+ import static java .util .Arrays .asList ;
17+ import static java .util .Collections .emptyList ;
1618import static java .util .Collections .singletonList ;
1719
1820import java .nio .charset .StandardCharsets ;
1921import java .util .ArrayList ;
20- import java .util .Arrays ;
2122import java .util .Collection ;
2223import java .util .Collections ;
2324import java .util .List ;
3940import com .amazonaws .encryptionsdk .exception .NoSuchMasterKeyException ;
4041import com .amazonaws .encryptionsdk .exception .UnsupportedProviderException ;
4142import com .amazonaws .handlers .RequestHandler2 ;
42- import com .amazonaws .regions .DefaultAwsRegionProviderChain ;
4343import com .amazonaws .regions .Region ;
4444import com .amazonaws .regions .Regions ;
4545import com .amazonaws .services .kms .AWSKMS ;
@@ -70,12 +70,27 @@ public interface RegionalClientSupplier {
7070 AWSKMS getClient (String regionName );
7171 }
7272
73- public static class Builder implements Cloneable {
73+ public static final class Builder implements Cloneable {
7474 private String defaultRegion_ = null ;
7575 private RegionalClientSupplier regionalClientSupplier_ = null ;
7676 private AWSKMSClientBuilder templateBuilder_ = null ;
7777 private List <String > keyIds_ = new ArrayList <>();
78- private List <String > grantTokens_ = new ArrayList <>();
78+
79+ public Builder clone () {
80+ try {
81+ Builder cloned = (Builder )super .clone ();
82+
83+ if (templateBuilder_ != null ) {
84+ cloned .templateBuilder_ = cloneClientBuilder (templateBuilder_ );
85+ }
86+
87+ cloned .keyIds_ = new ArrayList <>(keyIds_ );
88+
89+ return cloned ;
90+ } catch (CloneNotSupportedException e ) {
91+ throw new Error ("Impossible: CloneNotSupportedException" , e );
92+ }
93+ }
7994
8095 /**
8196 * Adds key ID(s) to the list of keys to use on encryption.
@@ -84,7 +99,7 @@ public static class Builder implements Cloneable {
8499 * @return
85100 */
86101 public Builder withKeysForEncryption (String ... keyIds ) {
87- keyIds_ .addAll (Arrays . asList (keyIds ));
102+ keyIds_ .addAll (asList (keyIds ));
88103 return this ;
89104 }
90105
@@ -99,17 +114,6 @@ public Builder withKeysForEncryption(List<String> keyIds) {
99114 return this ;
100115 }
101116
102- /**
103- * Adds grant tokens to the provider under construction.
104- *
105- * @param tokens
106- * @return
107- */
108- public Builder withGrantTokens (List <String > tokens ) {
109- this .grantTokens_ .addAll (tokens );
110- return this ;
111- }
112-
113117 /**
114118 * Sets the default region. This region will be used when specifying key IDs for encryption or in
115119 * {@link KmsMasterKeyProvider#getMasterKey(String)} that are not full ARNs, but are instead bare key IDs or
@@ -230,9 +234,19 @@ private AWSKMSClientBuilder cloneClientBuilder(final AWSKMSClientBuilder builder
230234 * @return
231235 */
232236 public KmsMasterKeyProvider build () {
237+ // If we don't have a default region, we need to check that all key IDs will be usable
238+ if (defaultRegion_ == null ) {
239+ for (String keyId : keyIds_ ) {
240+ if (parseRegionfromKeyArn (keyId ) == null ) {
241+ throw new AwsCryptoException ("Can't use non-ARN key identifiers or aliases when " +
242+ "no default region is set" );
243+ }
244+ }
245+ }
246+
233247 RegionalClientSupplier supplier = clientFactory ();
234248
235- return new KmsMasterKeyProvider (supplier , defaultRegion_ , keyIds_ , grantTokens_ , false );
249+ return new KmsMasterKeyProvider (supplier , defaultRegion_ , keyIds_ , emptyList () , false );
236250 }
237251
238252 private RegionalClientSupplier clientFactory () {
@@ -283,17 +297,12 @@ private KmsMasterKeyProvider(
283297 this .defaultRegion_ = defaultRegion ;
284298 this .keyIds_ = Collections .unmodifiableList (new ArrayList <>(keyIds ));
285299
286- if (grantTokens != null ) {
287- this .grantTokens_ = Collections .unmodifiableList (new ArrayList <>(grantTokens ));
288- } else {
289- // Legacy support for the mutating API
290- this .grantTokens_ = new ArrayList <>();
291- }
300+ this .grantTokens_ = grantTokens ;
292301 }
293302
294303 // Helper ctor for legacy ctors
295304 private KmsMasterKeyProvider (RegionalClientSupplier supplier , String defaultRegion , List <String > keyIds ) {
296- this (supplier , defaultRegion , keyIds , null , true );
305+ this (supplier , defaultRegion , keyIds , new ArrayList <>() , true );
297306 }
298307
299308 private static RegionalClientSupplier defaultProvider () {
@@ -309,7 +318,7 @@ private static RegionalClientSupplier defaultProvider() {
309318 */
310319 @ Deprecated
311320 public KmsMasterKeyProvider () {
312- this (defaultProvider (), Regions .DEFAULT_REGION .getName (), Collections . emptyList ());
321+ this (defaultProvider (), Regions .DEFAULT_REGION .getName (), emptyList ());
313322 }
314323
315324
@@ -439,7 +448,7 @@ public KmsMasterKey getMasterKey(final String provider, final String keyId) thro
439448 throw new UnsupportedProviderException ();
440449 }
441450
442- String regionName = identifyKeyRegion (keyId );
451+ String regionName = parseRegionfromKeyArn (keyId );
443452 AWSKMS kms = regionalClientSupplier_ .getClient (regionName );
444453 if (kms == null ) {
445454 throw new AwsCryptoException ("Can't use keys from region " + regionName );
@@ -456,7 +465,7 @@ public KmsMasterKey getMasterKey(final String provider, final String keyId) thro
456465 @ Override
457466 public List <KmsMasterKey > getMasterKeysForEncryption (final MasterKeyRequest request ) {
458467 if (keyIds_ == null ) {
459- return Collections . emptyList ();
468+ return emptyList ();
460469 }
461470 List <KmsMasterKey > result = new ArrayList <>(keyIds_ .size ());
462471 for (String id : keyIds_ ) {
@@ -485,7 +494,7 @@ public DataKey<KmsMasterKey> decryptDataKey(final CryptoAlgorithm algorithm,
485494 }
486495
487496 /**
488- * @deprecated This method is inherently not thread safe. Use {@link Builder #setGrantTokens(List)} instead.
497+ * @deprecated This method is inherently not thread safe. Use {@link KmsMasterKey #setGrantTokens(List)} instead.
489498 * {@link KmsMasterKeyProvider}s constructed using the builder will throw an exception on attempts to modify the
490499 * list of grant tokens.
491500 */
@@ -496,9 +505,7 @@ public void setGrantTokens(final List<String> grantTokens) {
496505 this .grantTokens_ .clear ();
497506 this .grantTokens_ .addAll (grantTokens );
498507 } catch (UnsupportedOperationException e ) {
499- throw new IllegalStateException (
500- "Changing grant tokens are not supported when constructing using the new builder API. Set them " +
501- "at construction time instead." );
508+ throw grantTokenError ();
502509 }
503510 }
504511
@@ -508,22 +515,48 @@ public List<String> getGrantTokens() {
508515 }
509516
510517 /**
511- * @deprecated This method is inherently not thread safe. Use {@link Builder#setGrantTokens (List)} instead.
512- * {@link KmsMasterKeyProvider}s constructed using the builder will throw an exception on attempts to modify the
513- * list of grant tokens.
518+ * @deprecated This method is inherently not thread safe. Use {@link #withGrantTokens (List)} or
519+ * {@link KmsMasterKey#setGrantTokens(List)} instead. {@link KmsMasterKeyProvider}s constructed using the builder
520+ * will throw an exception on attempts to modify the list of grant tokens.
514521 */
515522 @ Deprecated
516523 @ Override
517524 public void addGrantToken (final String grantToken ) {
518525 try {
519526 grantTokens_ .add (grantToken );
520527 } catch (UnsupportedOperationException e ) {
521- throw new IllegalStateException (
522- "Changing grant tokens are not supported when constructing using the new builder API. Set them " +
523- "at construction time instead." );
528+ throw grantTokenError ();
524529 }
525530 }
526531
532+ private RuntimeException grantTokenError () {
533+ return new IllegalStateException ("This master key provider is immutable. Use withGrantTokens instead." );
534+ }
535+
536+ /**
537+ * Returns a new {@link KmsMasterKeyProvider} that is configured identically to this one, except with the given list
538+ * of grant tokens. The grant token list in the returned provider is immutable (but can be further overridden by
539+ * invoking withGrantTokens again).
540+ * @param grantTokens
541+ * @return
542+ */
543+ public KmsMasterKeyProvider withGrantTokens (List <String > grantTokens ) {
544+ grantTokens = Collections .unmodifiableList (new ArrayList <>(grantTokens ));
545+
546+ return new KmsMasterKeyProvider (regionalClientSupplier_ , defaultRegion_ , keyIds_ , grantTokens , false );
547+ }
548+
549+ /**
550+ * Returns a new {@link KmsMasterKeyProvider} that is configured identically to this one, except with the given list
551+ * of grant tokens. The grant token list in the returned provider is immutable (but can be further overridden by
552+ * invoking withGrantTokens again).
553+ * @param grantTokens
554+ * @return
555+ */
556+ public KmsMasterKeyProvider withGrantTokens (String ... grantTokens ) {
557+ return withGrantTokens (asList (grantTokens ));
558+ }
559+
527560 private static Region getStartingRegion (final String keyArn ) {
528561 final String region = parseRegionfromKeyArn (keyArn );
529562 if (region != null ) {
@@ -537,20 +570,6 @@ private static Region getStartingRegion(final String keyArn) {
537570 return Region .getRegion (Regions .DEFAULT_REGION );
538571 }
539572
540- private String identifyKeyRegion (final String keyArn ) {
541- String region = parseRegionfromKeyArn (keyArn );
542-
543- if (region != null ) {
544- return region ;
545- }
546-
547- if (defaultRegion_ == null ) {
548- throw new AwsCryptoException ("Can't use non-ARN key identifiers or aliases when no default region is set" );
549- }
550-
551- return defaultRegion_ ;
552- }
553-
554573 private static String parseRegionfromKeyArn (final String keyArn ) {
555574 final String [] parts = keyArn .split (":" , 5 );
556575
0 commit comments