View Javadoc

1   package com.atlassian.asap.core.keys.publickey;
2   
3   import com.atlassian.asap.api.exception.CannotRetrieveKeyException;
4   import com.atlassian.asap.core.exception.PublicKeyNotFoundException;
5   import com.atlassian.asap.core.keys.KeyProvider;
6   import com.atlassian.asap.core.validator.ValidatedKeyId;
7   import com.google.common.annotations.VisibleForTesting;
8   import com.google.common.collect.ImmutableList;
9   import org.slf4j.Logger;
10  import org.slf4j.LoggerFactory;
11  
12  import java.security.PublicKey;
13  import java.util.Iterator;
14  import java.util.List;
15  
16  /**
17   * <p>Looks up keys sequentially in a given list of key providers.</p>
18   *
19   * <p>Note that the current implementation doesn't perform any caching, in
20   * particular it doesn't remember where the key was found last time. That may
21   * reduce the effectiveness of the provider's http cache if it comes later
22   * in the keyProviderChain.</p>
23   */
24  public class ChainedKeyProvider implements KeyProvider<PublicKey> {
25      private static final Logger logger = LoggerFactory.getLogger(ChainedKeyProvider.class);
26  
27  
28      private final List<KeyProvider<PublicKey>> keyProviderChain;
29  
30      @VisibleForTesting
31      ChainedKeyProvider(List<KeyProvider<PublicKey>> keyProviderChain) {
32          this.keyProviderChain = ImmutableList.copyOf(keyProviderChain);
33      }
34  
35      /**
36       * Create a chained key provider representing a given list of key repository providers.
37       * If there is only one provider in the chain, then return the provider instead of wrapping it.
38       *
39       * @param keyProviderChain set of chained key repository providers in order
40       * @return the chained key provider if there are more than one providers, or a key provider when it is
41       * the only provider in the chain
42       */
43      public static KeyProvider<PublicKey> createChainedKeyProvider(List<KeyProvider<PublicKey>> keyProviderChain) {
44          // this is an optimization to avoid wrapping the keyprovider
45          // when there is only one keyprovider
46          if (keyProviderChain.size() == 1) {
47              return keyProviderChain.get(0);
48          } else {
49              return new ChainedKeyProvider(keyProviderChain);
50          }
51      }
52  
53      @Override
54      public PublicKey getKey(ValidatedKeyId validatedKeyId) throws CannotRetrieveKeyException {
55          Iterator<KeyProvider<PublicKey>> keyProviderIterator = keyProviderChain.iterator();
56          while (keyProviderIterator.hasNext()) {
57              try {
58                  return keyProviderIterator.next().getKey(validatedKeyId);
59              } catch (PublicKeyNotFoundException ex) {
60                  if (keyProviderIterator.hasNext()) {
61                      logger.debug("Key not found in the provider, going to failover to next provider", ex);
62                  } else {
63                      // there is at least one key not found, but we don't know about the previous providers.
64                      // thus log it as debug as this situation can be caused by invalid input
65                      logger.debug("Error retrieving key from all key providers: {}", keyProviderChain, ex);
66                      throw new CannotRetrieveKeyException("Error communicating with all key providers", ex);
67                  }
68              } catch (CannotRetrieveKeyException ex) {
69                  if (keyProviderIterator.hasNext()) {
70                      logger.debug("Error retrieving key from the provider, going to failover to next provider", ex);
71                  } else {
72                      // there was at least one communication failure, so log it as error
73                      logger.error("Error retrieving key from all key providers: {}", keyProviderChain, ex);
74                      throw new CannotRetrieveKeyException("Error communicating with all key providers", ex);
75                  }
76              }
77  
78          }
79          throw new CannotRetrieveKeyException("There are no key providers available in the chain");
80      }
81  
82      /**
83       * @return the chain of key providers in the same order as they are consulted to resolve a key
84       */
85      public List<KeyProvider<PublicKey>> getKeyProviderChain() {
86          return keyProviderChain;
87      }
88  
89      @Override
90      public String toString() {
91          return this.getClass().getSimpleName() + "{" + keyProviderChain + '}';
92      }
93  }