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 default, this 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