1 /* 2 * Copyright 2021 Google LLC 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * 15 * * Neither the name of Google LLC nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package com.google.auth.oauth2; 33 34 import com.google.auth.http.HttpTransportFactory; 35 import com.google.common.annotations.VisibleForTesting; 36 import com.google.errorprone.annotations.CanIgnoreReturnValue; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Collection; 40 import java.util.Map; 41 42 /** 43 * Url-sourced, file-sourced, or user provided supplier method-sourced external account credentials. 44 * 45 * <p>By default, attempts to exchange the external credential for a GCP access token. 46 */ 47 public class IdentityPoolCredentials extends ExternalAccountCredentials { 48 49 static final String FILE_METRICS_HEADER_VALUE = "file"; 50 static final String URL_METRICS_HEADER_VALUE = "url"; 51 private static final long serialVersionUID = 2471046175477275881L; 52 private final IdentityPoolSubjectTokenSupplier subjectTokenSupplier; 53 private final ExternalAccountSupplierContext supplierContext; 54 private final String metricsHeaderValue; 55 56 /** Internal constructor. See {@link Builder}. */ IdentityPoolCredentials(Builder builder)57 IdentityPoolCredentials(Builder builder) { 58 super(builder); 59 IdentityPoolCredentialSource credentialSource = 60 (IdentityPoolCredentialSource) builder.credentialSource; 61 this.supplierContext = 62 ExternalAccountSupplierContext.newBuilder() 63 .setAudience(this.getAudience()) 64 .setSubjectTokenType(this.getSubjectTokenType()) 65 .build(); 66 // Check that one and only one of supplier or credential source are provided. 67 if (builder.subjectTokenSupplier != null && credentialSource != null) { 68 throw new IllegalArgumentException( 69 "IdentityPoolCredentials cannot have both a subjectTokenSupplier and a credentialSource."); 70 } 71 if (builder.subjectTokenSupplier == null && credentialSource == null) { 72 throw new IllegalArgumentException( 73 "A subjectTokenSupplier or a credentialSource must be provided."); 74 } 75 if (builder.subjectTokenSupplier != null) { 76 this.subjectTokenSupplier = builder.subjectTokenSupplier; 77 this.metricsHeaderValue = PROGRAMMATIC_METRICS_HEADER_VALUE; 78 } else if (credentialSource.credentialSourceType 79 == IdentityPoolCredentialSource.IdentityPoolCredentialSourceType.FILE) { 80 this.subjectTokenSupplier = new FileIdentityPoolSubjectTokenSupplier(credentialSource); 81 this.metricsHeaderValue = FILE_METRICS_HEADER_VALUE; 82 } else { 83 this.subjectTokenSupplier = 84 new UrlIdentityPoolSubjectTokenSupplier(credentialSource, this.transportFactory); 85 this.metricsHeaderValue = URL_METRICS_HEADER_VALUE; 86 } 87 } 88 89 @Override refreshAccessToken()90 public AccessToken refreshAccessToken() throws IOException { 91 String credential = retrieveSubjectToken(); 92 StsTokenExchangeRequest.Builder stsTokenExchangeRequest = 93 StsTokenExchangeRequest.newBuilder(credential, getSubjectTokenType()) 94 .setAudience(getAudience()); 95 96 Collection<String> scopes = getScopes(); 97 if (scopes != null && !scopes.isEmpty()) { 98 stsTokenExchangeRequest.setScopes(new ArrayList<>(scopes)); 99 } 100 101 return exchangeExternalCredentialForAccessToken(stsTokenExchangeRequest.build()); 102 } 103 104 @Override retrieveSubjectToken()105 public String retrieveSubjectToken() throws IOException { 106 return this.subjectTokenSupplier.getSubjectToken(supplierContext); 107 } 108 109 @Override getCredentialSourceType()110 String getCredentialSourceType() { 111 return this.metricsHeaderValue; 112 } 113 114 @VisibleForTesting getIdentityPoolSubjectTokenSupplier()115 IdentityPoolSubjectTokenSupplier getIdentityPoolSubjectTokenSupplier() { 116 return this.subjectTokenSupplier; 117 } 118 119 /** Clones the IdentityPoolCredentials with the specified scopes. */ 120 @Override createScoped(Collection<String> newScopes)121 public IdentityPoolCredentials createScoped(Collection<String> newScopes) { 122 return new IdentityPoolCredentials( 123 (IdentityPoolCredentials.Builder) newBuilder(this).setScopes(newScopes)); 124 } 125 newBuilder()126 public static Builder newBuilder() { 127 return new Builder(); 128 } 129 newBuilder(IdentityPoolCredentials identityPoolCredentials)130 public static Builder newBuilder(IdentityPoolCredentials identityPoolCredentials) { 131 return new Builder(identityPoolCredentials); 132 } 133 134 public static class Builder extends ExternalAccountCredentials.Builder { 135 136 private IdentityPoolSubjectTokenSupplier subjectTokenSupplier; 137 Builder()138 Builder() {} 139 Builder(IdentityPoolCredentials credentials)140 Builder(IdentityPoolCredentials credentials) { 141 super(credentials); 142 if (this.credentialSource == null) { 143 this.subjectTokenSupplier = credentials.subjectTokenSupplier; 144 } 145 } 146 147 /** 148 * Sets the subject token supplier. The supplier should return a valid subject token string. 149 * 150 * @param subjectTokenSupplier the supplier to use. 151 * @return this {@code Builder} object 152 */ 153 @CanIgnoreReturnValue setSubjectTokenSupplier(IdentityPoolSubjectTokenSupplier subjectTokenSupplier)154 public Builder setSubjectTokenSupplier(IdentityPoolSubjectTokenSupplier subjectTokenSupplier) { 155 this.subjectTokenSupplier = subjectTokenSupplier; 156 return this; 157 } 158 159 @CanIgnoreReturnValue setHttpTransportFactory(HttpTransportFactory transportFactory)160 public Builder setHttpTransportFactory(HttpTransportFactory transportFactory) { 161 super.setHttpTransportFactory(transportFactory); 162 return this; 163 } 164 165 @CanIgnoreReturnValue setAudience(String audience)166 public Builder setAudience(String audience) { 167 super.setAudience(audience); 168 return this; 169 } 170 171 @CanIgnoreReturnValue setSubjectTokenType(String subjectTokenType)172 public Builder setSubjectTokenType(String subjectTokenType) { 173 super.setSubjectTokenType(subjectTokenType); 174 return this; 175 } 176 177 @CanIgnoreReturnValue setSubjectTokenType(SubjectTokenTypes subjectTokenType)178 public Builder setSubjectTokenType(SubjectTokenTypes subjectTokenType) { 179 super.setSubjectTokenType(subjectTokenType); 180 return this; 181 } 182 183 @CanIgnoreReturnValue setTokenUrl(String tokenUrl)184 public Builder setTokenUrl(String tokenUrl) { 185 super.setTokenUrl(tokenUrl); 186 return this; 187 } 188 189 @CanIgnoreReturnValue setCredentialSource(IdentityPoolCredentialSource credentialSource)190 public Builder setCredentialSource(IdentityPoolCredentialSource credentialSource) { 191 super.setCredentialSource(credentialSource); 192 return this; 193 } 194 195 @CanIgnoreReturnValue setServiceAccountImpersonationUrl(String serviceAccountImpersonationUrl)196 public Builder setServiceAccountImpersonationUrl(String serviceAccountImpersonationUrl) { 197 super.setServiceAccountImpersonationUrl(serviceAccountImpersonationUrl); 198 return this; 199 } 200 201 @CanIgnoreReturnValue setTokenInfoUrl(String tokenInfoUrl)202 public Builder setTokenInfoUrl(String tokenInfoUrl) { 203 super.setTokenInfoUrl(tokenInfoUrl); 204 return this; 205 } 206 207 @CanIgnoreReturnValue setQuotaProjectId(String quotaProjectId)208 public Builder setQuotaProjectId(String quotaProjectId) { 209 super.setQuotaProjectId(quotaProjectId); 210 return this; 211 } 212 213 @CanIgnoreReturnValue setClientId(String clientId)214 public Builder setClientId(String clientId) { 215 super.setClientId(clientId); 216 return this; 217 } 218 219 @CanIgnoreReturnValue setClientSecret(String clientSecret)220 public Builder setClientSecret(String clientSecret) { 221 super.setClientSecret(clientSecret); 222 return this; 223 } 224 225 @CanIgnoreReturnValue setScopes(Collection<String> scopes)226 public Builder setScopes(Collection<String> scopes) { 227 super.setScopes(scopes); 228 return this; 229 } 230 231 @Override 232 @CanIgnoreReturnValue setWorkforcePoolUserProject(String workforcePoolUserProject)233 public Builder setWorkforcePoolUserProject(String workforcePoolUserProject) { 234 super.setWorkforcePoolUserProject(workforcePoolUserProject); 235 return this; 236 } 237 238 @CanIgnoreReturnValue setServiceAccountImpersonationOptions(Map<String, Object> optionsMap)239 public Builder setServiceAccountImpersonationOptions(Map<String, Object> optionsMap) { 240 super.setServiceAccountImpersonationOptions(optionsMap); 241 return this; 242 } 243 244 @CanIgnoreReturnValue setUniverseDomain(String universeDomain)245 public Builder setUniverseDomain(String universeDomain) { 246 super.setUniverseDomain(universeDomain); 247 return this; 248 } 249 250 @CanIgnoreReturnValue setEnvironmentProvider(EnvironmentProvider environmentProvider)251 Builder setEnvironmentProvider(EnvironmentProvider environmentProvider) { 252 super.setEnvironmentProvider(environmentProvider); 253 return this; 254 } 255 256 @Override build()257 public IdentityPoolCredentials build() { 258 return new IdentityPoolCredentials(this); 259 } 260 } 261 } 262