Skip to content

Commit 6eb249c

Browse files
committed
Error thrown when audience doesn't match pattern is IllegalStateException and format changes.
1 parent 1efce2d commit 6eb249c

9 files changed

+54
-50
lines changed

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

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
package com.google.auth.oauth2;
3333

34-
import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY;
34+
import static com.google.auth.oauth2.OAuth2Utils.*;
3535
import static com.google.common.base.Preconditions.checkNotNull;
3636

3737
import com.google.api.client.http.GenericUrl;
@@ -56,7 +56,6 @@
5656
import java.util.Map;
5757
import java.util.Objects;
5858
import java.util.regex.Matcher;
59-
import java.util.regex.Pattern;
6059
import javax.annotation.Nullable;
6160

6261
/**
@@ -83,13 +82,6 @@ public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials
8382
private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. ";
8483

8584
private static final long serialVersionUID = -2181779590486283287L;
86-
87-
private static final String WORKFORCE_POOL_URL_FORMAT =
88-
"https://iamcredentials.googleapis.com/v1/locations/global/workforcePools/%s/allowedLocations";
89-
private static final Pattern WORKFORCE_PATTERN =
90-
Pattern.compile(
91-
"^//iam.googleapis.com/locations/(?<location>[^/]+)/workforcePools/(?<pool>[^/]+)/providers/(?<provider>[^/]+)$");
92-
9385
private final String transportFactoryClassName;
9486
private final String audience;
9587
private final String tokenUrl;
@@ -231,13 +223,14 @@ public AccessToken refreshAccessToken() throws IOException {
231223

232224
@Override
233225
public String getTrustBoundaryUrl() throws IOException {
234-
Matcher matcher = WORKFORCE_PATTERN.matcher(getAudience());
226+
Matcher matcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
235227
if (!matcher.matches()) {
236-
throw new IOException(
228+
throw new IllegalStateException(
237229
"The provided audience is not in the correct format for a workforce pool.");
238230
}
239231
String poolId = matcher.group("pool");
240-
return String.format(WORKFORCE_POOL_URL_FORMAT, poolId);
232+
return String.format(
233+
IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, getUniverseDomain(), poolId);
241234
}
242235

243236
@Nullable

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

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@
3131

3232
package com.google.auth.oauth2;
3333

34+
import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN;
35+
import static com.google.auth.oauth2.OAuth2Utils.WORKLOAD_AUDIENCE_PATTERN;
3436
import static com.google.common.base.Preconditions.checkNotNull;
3537

3638
import com.google.api.client.http.HttpHeaders;
3739
import com.google.api.client.json.GenericJson;
3840
import com.google.api.client.json.JsonObjectParser;
3941
import com.google.api.client.util.Data;
42+
import com.google.api.core.InternalApi;
4043
import com.google.auth.RequestMetadataCallback;
4144
import com.google.auth.http.HttpTransportFactory;
4245
import com.google.common.base.MoreObjects;
@@ -100,13 +103,6 @@ public abstract class ExternalAccountCredentials extends GoogleCredentials
100103

101104
private EnvironmentProvider environmentProvider;
102105

103-
private static final Pattern WORKFORCE_PATTERN =
104-
Pattern.compile(
105-
"^//iam.googleapis.com/locations/(?<location>[^/]+)/workforcePools/(?<pool>[^/]+)/providers/(?<provider>[^/]+)$");
106-
private static final Pattern WORKLOAD_PATTERN =
107-
Pattern.compile(
108-
"^//iam.googleapis.com/projects/(?<project>[^/]+)/locations/(?<location>[^/]+)/workloadIdentityPools/(?<pool>[^/]+)/providers/(?<provider>[^/]+)$");
109-
110106
/**
111107
* Constructor with minimum identifying information and custom HTTP transport. Does not support
112108
* workforce credentials.
@@ -537,8 +533,9 @@ protected AccessToken exchangeExternalCredentialForAccessToken(
537533
}
538534
if (this.impersonatedCredentials != null) {
539535
AccessToken accessToken = this.impersonatedCredentials.refreshAccessToken();
540-
// After the impersonated credential refreshes, its trust boundary is
541-
// also refreshed. That is the trust boundary we will use.
536+
// We use the impersonated account's credential as the trust boundary
537+
// since the regional restriction is bounded by the access that the
538+
// impersonated account has.
542539
setTrustBoundary(this.impersonatedCredentials.getTrustBoundary());
543540
return accessToken;
544541
}
@@ -628,29 +625,31 @@ public String getServiceAccountEmail() {
628625
return ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl);
629626
}
630627

628+
@InternalApi
631629
@Override
632-
public String getTrustBoundaryUrl() throws IOException {
633-
Matcher workforceMatcher = WORKFORCE_PATTERN.matcher(getAudience());
634-
Matcher workloadMatcher = WORKLOAD_PATTERN.matcher(getAudience());
635-
630+
public String getTrustBoundaryUrl() {
631+
Matcher workforceMatcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
636632
if (workforceMatcher.matches()) {
637633
String poolId = workforceMatcher.group("pool");
638634
return String.format(
639635
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL,
640636
getUniverseDomain(),
641637
poolId);
642-
} else if (workloadMatcher.matches()) {
638+
}
639+
640+
Matcher workloadMatcher = WORKLOAD_AUDIENCE_PATTERN.matcher(getAudience());
641+
if (workloadMatcher.matches()) {
643642
String projectNumber = workloadMatcher.group("project");
644643
String poolId = workloadMatcher.group("pool");
645644
return String.format(
646645
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL,
647646
getUniverseDomain(),
648647
projectNumber,
649648
poolId);
650-
} else {
651-
throw new IOException(
652-
"The provided audience is not in a valid format for either a workload identity pool or a workforce pool.");
653649
}
650+
651+
throw new IllegalStateException(
652+
"The provided audience is not in a valid format for either a workload identity pool or a workforce pool.");
654653
}
655654

656655
@Nullable

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
import java.util.List;
6969
import java.util.Map;
7070
import java.util.Set;
71+
import java.util.regex.Pattern;
7172

7273
/**
7374
* Internal utilities for the com.google.auth.oauth2 namespace.
@@ -93,13 +94,6 @@ public class OAuth2Utils {
9394
static final URI TOKEN_SERVER_URI = URI.create("https://oauth2.googleapis.com/token");
9495

9596
static final URI TOKEN_REVOKE_URI = URI.create("https://oauth2.googleapis.com/revoke");
96-
97-
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT =
98-
"https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations";
99-
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL =
100-
"https://iamcredentials.%s/v1/locations/global/workforcePools/%s/allowedLocations";
101-
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL =
102-
"https://iamcredentials.%s/v1/projects/%s/locations/global/workloadIdentityPools/%s/allowedLocations";
10397
static final URI USER_AUTH_URI = URI.create("https://accounts.google.com/o/oauth2/auth");
10498

10599
public static final String CLOUD_PLATFORM_SCOPE =
@@ -124,6 +118,22 @@ public class OAuth2Utils {
124118
static final double RETRY_MULTIPLIER = 2;
125119
static final int DEFAULT_NUMBER_OF_RETRIES = 3;
126120

121+
static final Pattern WORKFORCE_AUDIENCE_PATTERN =
122+
Pattern.compile(
123+
"^//iam.googleapis.com/locations/(?<location>[^/]+)/workforcePools/(?<pool>[^/]+)/providers/(?<provider>[^/]+)$");
124+
static final Pattern WORKLOAD_AUDIENCE_PATTERN =
125+
Pattern.compile(
126+
"^//iam.googleapis.com/projects/(?<project>[^/]+)/locations/(?<location>[^/]+)/workloadIdentityPools/(?<pool>[^/]+)/providers/(?<provider>[^/]+)$");
127+
128+
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT =
129+
"https://iamcredentials.%s/v1/projects/-/serviceAccounts/%s/allowedLocations";
130+
131+
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL =
132+
"https://iamcredentials.%s/v1/locations/global/workforcePools/%s/allowedLocations";
133+
134+
static final String IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL =
135+
"https://iamcredentials.%s/v1/projects/%s/locations/global/workloadIdentityPools/%s/allowedLocations";
136+
127137
// Includes expected server errors from Google token endpoint
128138
// Other 5xx codes are either not used or retries are unlikely to succeed
129139
public static final Set<Integer> TOKEN_ENDPOINT_RETRYABLE_STATUS_CODES =

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,7 +1247,7 @@ public void testRefresh_trustBoundarySuccess() throws IOException {
12471247
}
12481248

12491249
@Test
1250-
public void testRefresh_trustBoundaryFails_incorrectAudience() throws IOException {
1250+
public void testRefresh_trustBoundaryFails_incorrectAudience() {
12511251
TestEnvironmentProvider environmentProvider = new TestEnvironmentProvider();
12521252
TrustBoundary.setEnvironmentProviderForTest(environmentProvider);
12531253
environmentProvider.setEnv("GOOGLE_AUTH_TRUST_BOUNDARY_ENABLE_EXPERIMENT", "1");
@@ -1262,9 +1262,9 @@ public void testRefresh_trustBoundaryFails_incorrectAudience() throws IOExceptio
12621262
.setTokenUrl(TOKEN_URL)
12631263
.build();
12641264

1265-
IOException exception =
1265+
IllegalStateException exception =
12661266
assertThrows(
1267-
IOException.class,
1267+
IllegalStateException.class,
12681268
() -> {
12691269
credentials.refresh();
12701270
});

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,9 +1297,9 @@ public void getTrustBoundaryUrl_invalidAudience_throws() {
12971297
.setCredentialSource(new TestCredentialSource(FILE_CREDENTIAL_SOURCE_MAP))
12981298
.build();
12991299

1300-
IOException exception =
1300+
IllegalStateException exception =
13011301
assertThrows(
1302-
IOException.class,
1302+
IllegalStateException.class,
13031303
() -> {
13041304
credentials.getTrustBoundaryUrl();
13051305
});

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ public LowLevelHttpResponse execute() throws IOException {
199199
if (url.contains(IAM_ENDPOINT)) {
200200

201201
if (url.endsWith(TRUST_BOUNDARY_URL_END)) {
202-
// Mocking call to refresh trust boundaries.
203-
// The lookup endpoint is located in the IAM server which is different from the
204-
// token server.
205202
GenericJson responseJson = new GenericJson();
206203
responseJson.setFactory(OAuth2Utils.JSON_FACTORY);
207204
responseJson.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -388,9 +388,10 @@ public LowLevelHttpResponse execute() throws IOException {
388388
}
389389

390390
protected boolean isIamLookupUrl(String url) {
391-
// Mocking call to refresh trust boundaries.
392-
// The lookup endpoint is located in the IAM server which is different from the
393-
// metadata server.
391+
// Mocking call to the /allowedLocations endpoint for trust boundary refresh.
392+
// For testing convenience, this mock transport handles
393+
// the /allowedLocations endpoint. The actual server for this endpoint
394+
// will be the IAM Credentials API.
394395
return url.endsWith("/allowedLocations");
395396
}
396397
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ public LowLevelHttpResponse execute() throws IOException {
106106
Matcher trustBoundaryMatcher =
107107
Pattern.compile(VALID_TRUST_BOUNDARY_PATTERN).matcher(url);
108108
if (trustBoundaryMatcher.matches()) {
109+
// Mocking call to the /allowedLocations endpoint for trust boundary refresh.
110+
// For testing convenience, this mock transport handles
111+
// the /allowedLocations endpoint.
109112
GenericJson response = new GenericJson();
110113
response.put("locations", TestUtils.TRUST_BOUNDARY_LOCATIONS);
111114
response.put("encodedLocations", TestUtils.TRUST_BOUNDARY_ENCODED_LOCATION);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,10 @@ public LowLevelHttpResponse execute() throws IOException {
327327
};
328328
return request;
329329
} else if (urlWithoutQuery.endsWith("/allowedLocations")) {
330-
// Mocking call to refresh trust boundaries.
331-
// The lookup endpoint is located in the IAM server which is different from the
332-
// token server.
330+
// Mocking call to the /allowedLocations endpoint for trust boundary refresh.
331+
// For testing convenience, this mock transport handles
332+
// the /allowedLocations endpoint. The actual server for this endpoint
333+
// will be the IAM Credentials API.
333334
request =
334335
new MockLowLevelHttpRequest(url) {
335336
@Override

0 commit comments

Comments
 (0)