1 /* 2 * Copyright 2015, Google Inc. All rights reserved. 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 Inc. 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.appengine; 33 34 import com.google.appengine.api.appidentity.AppIdentityService; 35 import com.google.appengine.api.appidentity.AppIdentityService.GetAccessTokenResult; 36 import com.google.appengine.api.appidentity.AppIdentityServiceFactory; 37 import com.google.auth.ServiceAccountSigner; 38 import com.google.auth.oauth2.AccessToken; 39 import com.google.auth.oauth2.GoogleCredentials; 40 import com.google.common.base.MoreObjects; 41 import com.google.common.collect.ImmutableList; 42 import com.google.common.collect.ImmutableSet; 43 import com.google.errorprone.annotations.CanIgnoreReturnValue; 44 import java.io.IOException; 45 import java.io.ObjectInputStream; 46 import java.util.Collection; 47 import java.util.Date; 48 import java.util.Objects; 49 50 /** 51 * OAuth2 credentials representing the built-in service account for Google App Engine. You should 52 * only use this class if you are running on AppEngine and are using urlfetch. 53 * 54 * <p>Fetches access tokens from the App Identity service. 55 */ 56 public class AppEngineCredentials extends GoogleCredentials implements ServiceAccountSigner { 57 58 private static final long serialVersionUID = -2627708355455064660L; 59 60 private final String appIdentityServiceClassName; 61 private final Collection<String> scopes; 62 private final boolean scopesRequired; 63 64 private transient AppIdentityService appIdentityService; 65 AppEngineCredentials(Collection<String> scopes, AppIdentityService appIdentityService)66 private AppEngineCredentials(Collection<String> scopes, AppIdentityService appIdentityService) { 67 this.scopes = scopes == null ? ImmutableSet.<String>of() : ImmutableList.copyOf(scopes); 68 this.appIdentityService = 69 appIdentityService != null 70 ? appIdentityService 71 : AppIdentityServiceFactory.getAppIdentityService(); 72 this.appIdentityServiceClassName = this.appIdentityService.getClass().getName(); 73 scopesRequired = this.scopes.isEmpty(); 74 } 75 76 /** Refresh the access token by getting it from the App Identity service */ 77 @Override refreshAccessToken()78 public AccessToken refreshAccessToken() throws IOException { 79 if (createScopedRequired()) { 80 throw new IOException("AppEngineCredentials requires createScoped call before use."); 81 } 82 GetAccessTokenResult accessTokenResponse = appIdentityService.getAccessToken(scopes); 83 String accessToken = accessTokenResponse.getAccessToken(); 84 Date expirationTime = accessTokenResponse.getExpirationTime(); 85 return new AccessToken(accessToken, expirationTime); 86 } 87 88 @Override createScopedRequired()89 public boolean createScopedRequired() { 90 return scopesRequired; 91 } 92 93 @Override createScoped(Collection<String> scopes)94 public GoogleCredentials createScoped(Collection<String> scopes) { 95 return new AppEngineCredentials(scopes, appIdentityService); 96 } 97 98 @Override getAccount()99 public String getAccount() { 100 return appIdentityService.getServiceAccountName(); 101 } 102 103 @Override sign(byte[] toSign)104 public byte[] sign(byte[] toSign) { 105 return appIdentityService.signForApp(toSign).getSignature(); 106 } 107 108 @Override hashCode()109 public int hashCode() { 110 return Objects.hash(scopes, scopesRequired, appIdentityServiceClassName); 111 } 112 113 @Override toString()114 public String toString() { 115 return MoreObjects.toStringHelper(this) 116 .add("scopes", scopes) 117 .add("scopesRequired", scopesRequired) 118 .add("appIdentityServiceClassName", appIdentityServiceClassName) 119 .toString(); 120 } 121 122 @Override equals(Object obj)123 public boolean equals(Object obj) { 124 if (!(obj instanceof AppEngineCredentials)) { 125 return false; 126 } 127 AppEngineCredentials other = (AppEngineCredentials) obj; 128 return this.scopesRequired == other.scopesRequired 129 && Objects.equals(this.scopes, other.scopes) 130 && Objects.equals(this.appIdentityServiceClassName, other.appIdentityServiceClassName); 131 } 132 readObject(ObjectInputStream input)133 private void readObject(ObjectInputStream input) throws IOException, ClassNotFoundException { 134 input.defaultReadObject(); 135 appIdentityService = newInstance(appIdentityServiceClassName); 136 } 137 newBuilder()138 public static Builder newBuilder() { 139 return new Builder(); 140 } 141 142 @Override toBuilder()143 public Builder toBuilder() { 144 return new Builder(this); 145 } 146 147 public static class Builder extends GoogleCredentials.Builder { 148 149 private Collection<String> scopes; 150 private AppIdentityService appIdentityService; 151 Builder()152 protected Builder() {} 153 Builder(AppEngineCredentials credentials)154 protected Builder(AppEngineCredentials credentials) { 155 this.scopes = credentials.scopes; 156 this.appIdentityService = credentials.appIdentityService; 157 } 158 159 @CanIgnoreReturnValue setScopes(Collection<String> scopes)160 public Builder setScopes(Collection<String> scopes) { 161 this.scopes = scopes; 162 return this; 163 } 164 165 @CanIgnoreReturnValue setAppIdentityService(AppIdentityService appIdentityService)166 public Builder setAppIdentityService(AppIdentityService appIdentityService) { 167 this.appIdentityService = appIdentityService; 168 return this; 169 } 170 getScopes()171 public Collection<String> getScopes() { 172 return scopes; 173 } 174 getAppIdentityService()175 public AppIdentityService getAppIdentityService() { 176 return appIdentityService; 177 } 178 179 @Override build()180 public AppEngineCredentials build() { 181 return new AppEngineCredentials(scopes, appIdentityService); 182 } 183 } 184 } 185