Skip to content

Commit 8225b92

Browse files
authored
Cleanup ImpersonatedCredentials (#212)
* Cleanup * Fix tests * No need for singular ERROR_PREFIX that doesn't provide extra information * Fix spelling error
1 parent b037146 commit 8225b92

File tree

2 files changed

+74
-117
lines changed

2 files changed

+74
-117
lines changed

oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java

Lines changed: 70 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,10 @@
5959
import com.google.common.collect.ImmutableMap;
6060

6161
/**
62-
* ImpersonatedCredentials allowing credentials issued to a user or
63-
* service account to impersonate another.
64-
* <br/>
65-
* The source project using ImpersonatedCredentials must enable the
66-
* "IAMCredentials" API.<br/>
67-
* Also, the target service account must grant the orginating principal the
68-
* "Service Account Token Creator" IAM role.
69-
* <br/>
70-
* Usage:<br/>
62+
* ImpersonatedCredentials allowing credentials issued to a user or service account to impersonate
63+
* another. <br> The source project using ImpersonatedCredentials must enable the "IAMCredentials"
64+
* API.<br> Also, the target service account must grant the orginating principal the "Service
65+
* Account Token Creator" IAM role. <br> Usage:<br>
7166
* <pre>
7267
* String credPath = "/path/to/svc_account.json";
7368
* ServiceAccountCredentials sourceCredentials = ServiceAccountCredentials
@@ -92,7 +87,6 @@ public class ImpersonatedCredentials extends GoogleCredentials {
9287
private static final String RFC3339 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
9388
private static final int ONE_HOUR_IN_SECONDS = 3600;
9489
private static final String CLOUD_PLATFORM_SCOPE = "https://www.googleapis.com/auth/cloud-platform";
95-
private static final String ERROR_PREFIX = "Error processng IamCredentials generateAccessToken: ";
9690
private static final String IAM_ENDPOINT = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken";
9791

9892
private static final String SCOPE_EMPTY_ERROR = "Scopes cannot be null";
@@ -108,74 +102,64 @@ public class ImpersonatedCredentials extends GoogleCredentials {
108102
private transient HttpTransportFactory transportFactory;
109103

110104
/**
111-
* @param sourceCredentials The source credential used as to acquire the
112-
* impersonated credentials
113-
* @param targetPrincipal The service account to impersonate.
114-
* @param delegates The chained list of delegates required to grant
115-
* the final access_token. <br/>If set, the sequence of identities must
116-
* have "Service Account Token Creator" capability granted to the
117-
* prceeding identity. <br/>For example, if set to
118-
* [serviceAccountB, serviceAccountC], the sourceCredential
119-
* must have the Token Creator role on serviceAccountB. serviceAccountB must
120-
* have the Token Creator on serviceAccountC. <br/>Finally, C must have
121-
* Token Creator on target_principal. If left unset, sourceCredential
122-
* must have that role on targetPrincipal.
123-
* @param scopes Scopes to request during the authorization grant.
124-
* @param lifetime Number of seconds the delegated credential should
125-
* be valid for (upto 3600).
126-
* @param transportFactory HTTP transport factory, creates the transport used
127-
* to get access tokens.
105+
* @param sourceCredentials The source credential used as to acquire the impersonated credentials
106+
* @param targetPrincipal The service account to impersonate.
107+
* @param delegates The chained list of delegates required to grant the final access_token. If
108+
* set, the sequence of identities must have "Service Account Token Creator" capability granted to
109+
* the preceding identity. For example, if set to [serviceAccountB, serviceAccountC], the
110+
* sourceCredential must have the Token Creator role on serviceAccountB. serviceAccountB must have
111+
* the Token Creator on serviceAccountC. Finally, C must have Token Creator on target_principal.
112+
* If left unset, sourceCredential must have that role on targetPrincipal.
113+
* @param scopes Scopes to request during the authorization grant.
114+
* @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
115+
* @param transportFactory HTTP transport factory, creates the transport used to get access
116+
* tokens.
128117
*/
129-
public static ImpersonatedCredentials create(GoogleCredentials sourceCredentials, String targetPrincipal,
130-
List<String> delegates, List<String> scopes, int lifetime, HttpTransportFactory transportFactory) {
131-
return ImpersonatedCredentials.newBuilder().setSourceCredentials(sourceCredentials)
132-
.setTargetPrincipal(targetPrincipal).setDelegates(delegates).setScopes(scopes).setLifetime(lifetime)
133-
.setHttpTransportFactory(transportFactory).build();
118+
public static ImpersonatedCredentials create(GoogleCredentials sourceCredentials,
119+
String targetPrincipal,
120+
List<String> delegates, List<String> scopes, int lifetime,
121+
HttpTransportFactory transportFactory) {
122+
return ImpersonatedCredentials.newBuilder()
123+
.setSourceCredentials(sourceCredentials)
124+
.setTargetPrincipal(targetPrincipal)
125+
.setDelegates(delegates)
126+
.setScopes(scopes)
127+
.setLifetime(lifetime)
128+
.setHttpTransportFactory(transportFactory)
129+
.build();
134130
}
135131

136132
/**
137-
* @param sourceCredentials The source credential used as to acquire the
138-
* impersonated credentials
139-
* @param targetPrincipal The service account to impersonate.
140-
* @param delegates The chained list of delegates required to grant
141-
* the final access_token. <br/>If set, the sequence of identities must
142-
* have "Service Account Token Creator" capability granted to the
143-
* prceeding identity. <br/>For example, if set to
144-
* [serviceAccountB, serviceAccountC], the sourceCredential
145-
* must have the Token Creator role on serviceAccountB. serviceAccountB must
146-
* have the Token Creator on serviceAccountC. <br/>Finally, C must have
147-
* Token Creator on target_principal. If left unset, sourceCredential
148-
* must have that role on targetPrincipal.
149-
* @param scopes Scopes to request during the authorization grant.
150-
* @param lifetime Number of seconds the delegated credential should
151-
* be valid for (upto 3600).
152-
*/
153-
public static ImpersonatedCredentials create(GoogleCredentials sourceCredentials, String targetPrincipal,
133+
* @param sourceCredentials The source credential used as to acquire the impersonated credentials
134+
* @param targetPrincipal The service account to impersonate.
135+
* @param delegates The chained list of delegates required to grant the final access_token. If
136+
* set, the sequence of identities must have "Service Account Token Creator" capability granted to
137+
* the preceding identity. For example, if set to [serviceAccountB, serviceAccountC], the
138+
* sourceCredential must have the Token Creator role on serviceAccountB. serviceAccountB must have
139+
* the Token Creator on serviceAccountC. Finally, C must have Token Creator on target_principal.
140+
* If left unset, sourceCredential must have that role on targetPrincipal.
141+
* @param scopes Scopes to request during the authorization grant.
142+
* @param lifetime Number of seconds the delegated credential should be valid for (up to 3600).
143+
*/
144+
public static ImpersonatedCredentials create(GoogleCredentials sourceCredentials,
145+
String targetPrincipal,
154146
List<String> delegates, List<String> scopes, int lifetime) {
155-
return ImpersonatedCredentials.newBuilder().setSourceCredentials(sourceCredentials)
156-
.setTargetPrincipal(targetPrincipal).setDelegates(delegates).setScopes(scopes).setLifetime(lifetime).build();
147+
return ImpersonatedCredentials.newBuilder()
148+
.setSourceCredentials(sourceCredentials)
149+
.setTargetPrincipal(targetPrincipal)
150+
.setDelegates(delegates)
151+
.setScopes(scopes)
152+
.setLifetime(lifetime)
153+
.build();
157154
}
158155

159-
/**
160-
* @param sourceCredentials = Source Credentials.
161-
* @param targetPrincipal = targetPrincipal;
162-
* @param delegates = delegates;
163-
* @param scopes = scopes;
164-
* @param lifetime = lifetime;
165-
* @param transportFactory = HTTP transport factory, creates the transport used
166-
* to get access tokens.
167-
* @deprecated Use {@link #create(ImpersonatedCredentials)} instead. This constructor
168-
* will either be deleted or made private in a later version.
169-
*/
170-
@Deprecated
171-
private ImpersonatedCredentials(GoogleCredentials sourceCredentials, String targetPrincipal, List<String> delegates,
172-
List<String> scopes, int lifetime, HttpTransportFactory transportFactory) {
173-
this.sourceCredentials = sourceCredentials;
174-
this.targetPrincipal = targetPrincipal;
175-
this.delegates = delegates;
176-
this.scopes = scopes;
177-
this.lifetime = lifetime;
178-
this.transportFactory = firstNonNull(transportFactory,
156+
private ImpersonatedCredentials(Builder builder) {
157+
this.sourceCredentials = builder.getSourceCredentials();
158+
this.targetPrincipal = builder.getTargetPrincipal();
159+
this.delegates = builder.getDelegates();
160+
this.scopes = builder.getScopes();
161+
this.lifetime = builder.getLifetime();
162+
this.transportFactory = firstNonNull(builder.getHttpTransportFactory(),
179163
getFromServiceLoader(HttpTransportFactory.class, OAuth2Utils.HTTP_TRANSPORT_FACTORY));
180164
this.transportFactoryClassName = this.transportFactory.getClass().getName();
181165
if (this.delegates == null) {
@@ -189,47 +173,17 @@ private ImpersonatedCredentials(GoogleCredentials sourceCredentials, String targ
189173
}
190174
}
191175

192-
/**
193-
* @param sourceCredentials = Source Credentials.
194-
* @param targetPrincipal = targetPrincipal;
195-
* @param scopes = scopes;
196-
* @param lifetime = lifetime;
197-
* @param transportFactory = HTTP transport factory, creates the transport used
198-
* to get access tokens.
199-
* @deprecated Use {@link #create(ImpersonatedCredentials)} instead. This constructor
200-
* will either be deleted or made private in a later version.
201-
*/
202-
@Deprecated
203-
private ImpersonatedCredentials(GoogleCredentials sourceCredentials, String targetPrincipal, List<String> scopes,
204-
int lifetime, HttpTransportFactory transportFactory) {
205-
this(sourceCredentials, targetPrincipal, new ArrayList<String>(), scopes, lifetime, transportFactory);
206-
}
207-
208-
/**
209-
* @param sourceCredentials = Source Credentials.
210-
* @param targetPrincipal = targetPrincipal;
211-
* @param delegates = delegates;
212-
* @param scopes = scopes;
213-
* @param lifetime = lifetime;
214-
* @deprecated Use {@link #create(ImpersonatedCredentials)} instead. This constructor
215-
* will either be deleted or made private in a later version.
216-
*/
217-
@Deprecated
218-
private ImpersonatedCredentials(GoogleCredentials sourceCredentials, String targetPrincipal, List<String> scopes,
219-
int lifetime) {
220-
this(sourceCredentials, targetPrincipal, new ArrayList<String>(), scopes, lifetime, null);
221-
}
222-
223176
@Override
224177
public AccessToken refreshAccessToken() throws IOException {
225-
178+
if (this.sourceCredentials.getAccessToken() == null) {
179+
this.sourceCredentials = this.sourceCredentials
180+
.createScoped(Arrays.asList(CLOUD_PLATFORM_SCOPE));
181+
}
182+
226183
try {
227-
if (this.sourceCredentials.getAccessToken() == null) {
228-
this.sourceCredentials = this.sourceCredentials.createScoped(Arrays.asList(CLOUD_PLATFORM_SCOPE));
229-
}
230184
this.sourceCredentials.refreshIfExpired();
231185
} catch (IOException e) {
232-
throw new IOException(ERROR_PREFIX + "Unable to refresh sourceCredentials " + e.toString());
186+
throw new IOException("Unable to refresh sourceCredentials", e);
233187
}
234188

235189
HttpTransport httpTransport = this.transportFactory.create();
@@ -241,7 +195,8 @@ public AccessToken refreshAccessToken() throws IOException {
241195
String endpointUrl = String.format(IAM_ENDPOINT, this.targetPrincipal);
242196
GenericUrl url = new GenericUrl(endpointUrl);
243197

244-
Map<String, Object> body = ImmutableMap.<String, Object>of("delegates", this.delegates, "scope", this.scopes,
198+
Map<String, Object> body = ImmutableMap.<String, Object>of("delegates", this.delegates, "scope",
199+
this.scopes,
245200
"lifetime", this.lifetime + "s");
246201

247202
HttpContent requestContent = new JsonHttpContent(parser.getJsonFactory(), body);
@@ -253,21 +208,23 @@ public AccessToken refreshAccessToken() throws IOException {
253208
try {
254209
response = request.execute();
255210
} catch (IOException e) {
256-
throw new IOException(ERROR_PREFIX + e.toString());
211+
throw new IOException("Error requesting access token", e);
257212
}
258213

259214
GenericData responseData = response.parseAs(GenericData.class);
260215
response.disconnect();
261216

262-
String accessToken = OAuth2Utils.validateString(responseData, "accessToken", ERROR_PREFIX);
263-
String expireTime = OAuth2Utils.validateString(responseData, "expireTime", ERROR_PREFIX);
217+
String accessToken = OAuth2Utils
218+
.validateString(responseData, "accessToken", "Expected to find an accessToken");
219+
String expireTime = OAuth2Utils
220+
.validateString(responseData, "expireTime", "Expected to find an expireTime");
264221

265222
DateFormat format = new SimpleDateFormat(RFC3339);
266223
Date date;
267224
try {
268225
date = format.parse(expireTime);
269226
} catch (ParseException pe) {
270-
throw new IOException(ERROR_PREFIX + pe.getMessage());
227+
throw new IOException("Error parsing expireTime: " + pe.getMessage());
271228
}
272229
return new AccessToken(accessToken, date);
273230
}
@@ -383,8 +340,7 @@ public HttpTransportFactory getHttpTransportFactory() {
383340
}
384341

385342
public ImpersonatedCredentials build() {
386-
return new ImpersonatedCredentials(this.sourceCredentials, this.targetPrincipal, this.delegates, this.scopes,
387-
this.lifetime, this.transportFactory);
343+
return new ImpersonatedCredentials(this);
388344
}
389345

390346
}

oauth2_http/javatests/com/google/auth/oauth2/ImpersonatedCredentialsTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343
import java.util.Arrays;
4444
import java.util.List;
4545

46-
import com.google.api.client.http.HttpResponseException;
4746
import com.google.api.client.http.HttpStatusCodes;
4847
import com.google.api.client.http.HttpTransport;
4948
import com.google.api.client.json.JsonFactory;
@@ -134,7 +133,8 @@ public void refreshAccessToken_unauthorized() throws IOException {
134133
targetCredentials.refreshAccessToken().getTokenValue();
135134
fail(String.format("Should throw exception with message containing '%s'", expectedMessage));
136135
} catch (IOException expected) {
137-
assertTrue(expected.getMessage().contains(expectedMessage));
136+
assertEquals("Error requesting access token", expected.getMessage());
137+
assertTrue(expected.getCause().getMessage().contains(expectedMessage));
138138
}
139139
}
140140

@@ -158,7 +158,8 @@ public void refreshAccessToken_malformedTarget() throws IOException {
158158
targetCredentials.refreshAccessToken().getTokenValue();
159159
fail(String.format("Should throw exception with message containing '%s'", expectedMessage));
160160
} catch (IOException expected) {
161-
assertTrue(expected.getMessage().contains(expectedMessage));
161+
assertEquals("Error requesting access token", expected.getMessage());
162+
assertTrue(expected.getCause().getMessage().contains(expectedMessage));
162163
}
163164
}
164165

0 commit comments

Comments
 (0)