001/** 002 * Copyright 2004-2014 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.kuali.common.aws.auth; 017 018import java.util.ArrayList; 019import java.util.List; 020 021import org.kuali.common.aws.model.ImmutableAWSCredentials; 022import org.kuali.common.aws.model.ImmutableAWSSessionCredentials; 023import org.kuali.common.util.Assert; 024import org.kuali.common.util.enc.EncUtils; 025import org.kuali.common.util.enc.EncryptionService; 026import org.kuali.common.util.spring.env.EnvironmentService; 027 028import com.amazonaws.auth.AWSCredentials; 029import com.amazonaws.auth.AWSCredentialsProvider; 030import com.amazonaws.auth.AWSCredentialsProviderChain; 031import com.amazonaws.auth.AWSSessionCredentials; 032import com.amazonaws.auth.InstanceProfileCredentialsProvider; 033import com.google.common.base.Optional; 034import com.google.common.collect.ImmutableList; 035 036/** 037 * This chain searches for AWS credentials in system properties -> environment variables -> Amazon's EC2 Instance Metadata Service 038 */ 039public final class DefaultProviderChain extends AWSCredentialsProviderChain { 040 041 private final Optional<AWSCredentials> optionalCredentials; 042 private final Optional<EncryptionService> enc; 043 private final Optional<EnvironmentService> env; 044 private final boolean instanceCredentialsOverride; 045 private final List<AWSCredentialsProvider> providers; 046 047 public static class Builder { 048 049 // Optional 050 private Optional<EncryptionService> enc = Optional.absent(); 051 private Optional<EnvironmentService> env = Optional.absent(); 052 private Optional<AWSCredentials> optionalCredentials = Optional.absent(); 053 private boolean instanceCredentialsOverride = false; // If true, EC2 instance credentials take precedence over optionalCredentials 054 055 // Filled in by the build() method 056 private List<AWSCredentialsProvider> providers; 057 058 private static final String INSTANCE_CREDENTIALS_OVERRIDE_KEY = "aws.instanceCredentialsOverride"; 059 060 public Builder enc(EncryptionService enc) { 061 this.enc = Optional.of(enc); 062 return this; 063 } 064 065 public Builder env(EnvironmentService env) { 066 this.env = Optional.of(env); 067 return this; 068 } 069 070 public Builder credentials(AWSCredentials credentials) { 071 this.optionalCredentials = Optional.of(credentials); 072 return this; 073 } 074 075 public Builder instanceCredentialsOverride(boolean instanceCredentialsOverride) { 076 this.instanceCredentialsOverride = instanceCredentialsOverride; 077 return this; 078 } 079 080 private void override() { 081 if (env.isPresent()) { 082 instanceCredentialsOverride(env.get().getBoolean(INSTANCE_CREDENTIALS_OVERRIDE_KEY, instanceCredentialsOverride)); 083 } 084 } 085 086 private void validate(DefaultProviderChain provider) { 087 Assert.noNulls(provider.getCredentials(), provider.getEnc(), provider.getEnv(), provider.getProviders()); 088 Assert.isTrue(provider.getProviders().size() > 0, "Must supply at least one provider"); 089 } 090 091 public DefaultProviderChain build() { 092 override(); 093 this.providers = getProviders(); 094 DefaultProviderChain provider = new DefaultProviderChain(this); 095 validate(provider); 096 return provider; 097 } 098 099 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}