View Javadoc
1   /**
2    * Copyright 2004-2014 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.common.aws.auth;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.kuali.common.aws.model.ImmutableAWSCredentials;
22  import org.kuali.common.aws.model.ImmutableAWSSessionCredentials;
23  import org.kuali.common.util.Assert;
24  import org.kuali.common.util.enc.EncUtils;
25  import org.kuali.common.util.enc.EncryptionService;
26  import org.kuali.common.util.spring.env.EnvironmentService;
27  
28  import com.amazonaws.auth.AWSCredentials;
29  import com.amazonaws.auth.AWSCredentialsProvider;
30  import com.amazonaws.auth.AWSCredentialsProviderChain;
31  import com.amazonaws.auth.AWSSessionCredentials;
32  import com.amazonaws.auth.InstanceProfileCredentialsProvider;
33  import com.google.common.base.Optional;
34  import com.google.common.collect.ImmutableList;
35  
36  /**
37   * This chain searches for AWS credentials in system properties -> environment variables -> Amazon's EC2 Instance Metadata Service
38   */
39  public final class DefaultProviderChain extends AWSCredentialsProviderChain {
40  
41  	private final Optional<AWSCredentials> optionalCredentials;
42  	private final Optional<EncryptionService> enc;
43  	private final Optional<EnvironmentService> env;
44  	private final boolean instanceCredentialsOverride;
45  	private final List<AWSCredentialsProvider> providers;
46  
47  	public static class Builder {
48  
49  		// Optional
50  		private Optional<EncryptionService> enc = Optional.absent();
51  		private Optional<EnvironmentService> env = Optional.absent();
52  		private Optional<AWSCredentials> optionalCredentials = Optional.absent();
53  		private boolean instanceCredentialsOverride = false; // If true, EC2 instance credentials take precedence over optionalCredentials
54  
55  		// Filled in by the build() method
56  		private List<AWSCredentialsProvider> providers;
57  
58  		private static final String INSTANCE_CREDENTIALS_OVERRIDE_KEY = "aws.instanceCredentialsOverride";
59  
60  		public Builder enc(EncryptionService enc) {
61  			this.enc = Optional.of(enc);
62  			return this;
63  		}
64  
65  		public Builder env(EnvironmentService env) {
66  			this.env = Optional.of(env);
67  			return this;
68  		}
69  
70  		public Builder credentials(AWSCredentials credentials) {
71  			this.optionalCredentials = Optional.of(credentials);
72  			return this;
73  		}
74  
75  		public Builder instanceCredentialsOverride(boolean instanceCredentialsOverride) {
76  			this.instanceCredentialsOverride = instanceCredentialsOverride;
77  			return this;
78  		}
79  
80  		private void override() {
81  			if (env.isPresent()) {
82  				instanceCredentialsOverride(env.get().getBoolean(INSTANCE_CREDENTIALS_OVERRIDE_KEY, instanceCredentialsOverride));
83  			}
84  		}
85  
86  		private void validate(DefaultProviderChain provider) {
87  			Assert.noNulls(provider.getCredentials(), provider.getEnc(), provider.getEnv(), provider.getProviders());
88  			Assert.isTrue(provider.getProviders().size() > 0, "Must supply at least one provider");
89  		}
90  
91  		public DefaultProviderChain build() {
92  			override();
93  			this.providers = getProviders();
94  			DefaultProviderChain provider = new DefaultProviderChain(this);
95  			validate(provider);
96  			return provider;
97  		}
98  
99  		private List<AWSCredentialsProvider> getProviders() {
100 			// Set up some storage
101 			List<AWSCredentialsProvider> providers = new ArrayList<AWSCredentialsProvider>();
102 
103 			// If we are examining the environment, system properties / environment variables always win
104 			if (env.isPresent()) {
105 				providers.add(new EnvCredentialsProvider.Builder(env.get()).build());
106 			}
107 
108 			// Then fall through to "other" providers
109 			providers.addAll(getOther());
110 
111 			// Make the list immutable
112 			return ImmutableList.copyOf(providers);
113 		}
114 
115 		protected List<AWSCredentialsProvider> getOther() {
116 			// Amazon's EC2 Instance Metadata Service
117 			// http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/java-dg-roles.html
118 			// AWS allows you to setup an IAM role and attach that role to an EC2 Instance at launch time
119 			// This allows you to automatically authorize java code running on an EC2 instance
120 			AWSCredentialsProvider ipcp = new InstanceProfileCredentialsProvider();
121 
122 			// No optional credentials
123 			if (!optionalCredentials.isPresent()) {
124 				return ImmutableList.of(ipcp);
125 			}
126 
127 			// Setup a provider that returns the optional credentials they supplied
128 			AWSCredentialsProvider simple = new SimpleCredentialsProvider(optionalCredentials.get());
129 
130 			if (instanceCredentialsOverride) {
131 				// EC2 instance credentials first, then the credentials they supplied
132 				return ImmutableList.of(ipcp, simple);
133 			} else {
134 				// Credentials they supplied first, then the EC2 instance credentials
135 				return ImmutableList.of(simple, ipcp);
136 			}
137 		}
138 	}
139 
140 	public DefaultProviderChain(Builder builder) {
141 		super(toArray(builder.providers));
142 		this.optionalCredentials = builder.optionalCredentials;
143 		this.enc = builder.enc;
144 		this.env = builder.env;
145 		this.instanceCredentialsOverride = builder.instanceCredentialsOverride;
146 		this.providers = builder.providers;
147 	}
148 
149 	private static AWSCredentialsProvider[] toArray(List<AWSCredentialsProvider> providers) {
150 		return providers.toArray(new AWSCredentialsProvider[providers.size()]);
151 	}
152 
153 	public Optional<AWSCredentials> getOptionalCredentials() {
154 		return optionalCredentials;
155 	}
156 
157 	public boolean isInstanceCredentialsOverride() {
158 		return instanceCredentialsOverride;
159 	}
160 
161 	@Override
162 	public AWSCredentials getCredentials() {
163 		AWSCredentials creds = super.getCredentials();
164 		String accessKey = creds.getAWSAccessKeyId();
165 		String secretKey = EncUtils.decrypt(enc, creds.getAWSSecretKey());
166 		if (creds instanceof AWSSessionCredentials) {
167 			AWSSessionCredentials sessionCreds = (AWSSessionCredentials) creds;
168 			return ImmutableAWSSessionCredentials.build(accessKey, secretKey, sessionCreds.getSessionToken());
169 		} else {
170 			return ImmutableAWSCredentials.build(accessKey, secretKey);
171 		}
172 	}
173 
174 	public List<AWSCredentialsProvider> getProviders() {
175 		return providers;
176 	}
177 
178 	public Optional<EncryptionService> getEnc() {
179 		return enc;
180 	}
181 
182 	public Optional<EnvironmentService> getEnv() {
183 		return env;
184 	}
185 
186 }