1 /*
2  * Copyright 2012-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */

15 package com.amazonaws.auth;
16
17 import java.util.LinkedList;
18 import java.util.List;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import com.amazonaws.SdkClientException;
24
25 /**
26  * {@link AWSCredentialsProvider} implementation that chains together multiple
27  * credentials providers. When a caller first requests credentials from this provider,
28  * it calls all the providers in the chain, in the original order specified,
29  * until one can provide credentials, and then returns those credentials. If all
30  * of the credential providers in the chain have been called, and none of them
31  * can provide credentials, then this class will throw an exception indicated
32  * that no credentials are available.
33  * <p>
34  * By defaultthis class will remember the first credentials provider in the chain
35  * that was able to provide credentials, and will continue to use that provider when
36  * credentials are requested in the future, instead of traversing the chain each time.
37  * This behavior can be controlled through the {@link #setReuseLastProvider(boolean)} method.
38  */

39 public class AWSCredentialsProviderChain implements AWSCredentialsProvider {
40
41     private static final Log log = LogFactory.getLog(AWSCredentialsProviderChain.class);
42
43     private final List<AWSCredentialsProvider> credentialsProviders =
44             new LinkedList<AWSCredentialsProvider>();
45
46     private boolean reuseLastProvider = true;
47     private AWSCredentialsProvider lastUsedProvider;
48
49     /**
50      * Constructs a new AWSCredentialsProviderChain with the specified credential providers. When
51      * credentials are requested from this provider, it will call each of these credential providers
52      * in the same order specified here until one of them returns AWS security credentials.
53      *
54      * @param credentialsProviders
55      *            The chain of credentials providers.
56      */

57     public AWSCredentialsProviderChain(List<? extends AWSCredentialsProvider> credentialsProviders) {
58         if (credentialsProviders == null || credentialsProviders.size() == 0) {
59             throw new IllegalArgumentException("No credential providers specified");
60         }
61         this.credentialsProviders.addAll(credentialsProviders);
62     }
63
64     /**
65      * Constructs a new AWSCredentialsProviderChain with the specified credential providers. When
66      * credentials are requested from this provider, it will call each of these credential providers
67      * in the same order specified here until one of them returns AWS security credentials.
68      *
69      * @param credentialsProviders
70      *            The chain of credentials providers.
71      */

72     public AWSCredentialsProviderChain(AWSCredentialsProvider... credentialsProviders) {
73         if (credentialsProviders == null || credentialsProviders.length == 0) {
74             throw new IllegalArgumentException("No credential providers specified");
75         }
76
77         for (AWSCredentialsProvider provider : credentialsProviders) {
78             this.credentialsProviders.add(provider);
79         }
80     }
81
82     /**
83      * Returns true if this chain will reuse the last successful credentials
84      * provider for future credentials requests, otherwise, false if it will
85      * search through the chain each time.
86      *
87      * @return True if this chain will reuse the last successful credentials
88      *         provider for future credentials requests.
89      */

90     public boolean getReuseLastProvider() {
91         return reuseLastProvider;
92     }
93
94     /**
95      * Enables or disables caching of the last successful credentials provider
96      * in this chain. Reusing the last successful credentials provider will
97      * typically return credentials faster than searching through the chain.
98      *
99      * @param b
100      *            Whether to enable or disable reusing the last successful
101      *            credentials provider for future credentials requests instead
102      *            of searching through the whole chain.
103      */

104     public void setReuseLastProvider(boolean b) {
105         this.reuseLastProvider = b;
106     }
107
108     @Override
109     public AWSCredentials getCredentials() {
110         if (reuseLastProvider && lastUsedProvider != null) {
111             return lastUsedProvider.getCredentials();
112         }
113
114         List<String> exceptionMessages = null;
115         for (AWSCredentialsProvider provider : credentialsProviders) {
116             try {
117                 AWSCredentials credentials = provider.getCredentials();
118
119                 if (credentials.getAWSAccessKeyId() != null &&
120                     credentials.getAWSSecretKey() != null) {
121                     log.debug("Loading credentials from " + provider.toString());
122
123                     lastUsedProvider = provider;
124                     return credentials;
125                 }
126             } catch (Exception e) {
127                 // Ignore any exceptions and move onto the next provider
128                 String message = provider + ": " + e.getMessage();
129                 log.debug("Unable to load credentials from " + message);
130                 if (exceptionMessages == null) {
131                     exceptionMessages = new LinkedList<String>();
132                 }
133                 exceptionMessages.add(message);
134             }
135         }
136         throw new SdkClientException("Unable to load AWS credentials from any provider in the chain: "
137                                      + exceptionMessages);
138     }
139
140     @Override
141     public void refresh() {
142         for (AWSCredentialsProvider provider : credentialsProviders) {
143             provider.refresh();
144         }
145     }
146 }
147