Skip to content

Commit c1505b4

Browse files
committed
feat(auth): Introduce Regional Access Boundaries for GoogleCredentials (#12787)
Migrates RAB changes from the older repo -> https://github.com/googleapis/google-auth-library-java/tree/feat-tb-sa
1 parent 2d9d01c commit c1505b4

30 files changed

Lines changed: 2470 additions & 40 deletions

google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.google.api.client.http.HttpStatusCodes;
4242
import com.google.api.client.json.JsonObjectParser;
4343
import com.google.api.client.util.GenericData;
44+
import com.google.api.core.InternalApi;
4445
import com.google.auth.CredentialTypeForMetrics;
4546
import com.google.auth.Credentials;
4647
import com.google.auth.Retryable;
@@ -80,7 +81,7 @@
8081
* <p>These credentials use the IAM API to sign data. See {@link #sign(byte[])} for more details.
8182
*/
8283
public class ComputeEngineCredentials extends GoogleCredentials
83-
implements ServiceAccountSigner, IdTokenProvider {
84+
implements ServiceAccountSigner, IdTokenProvider, RegionalAccessBoundaryProvider {
8485

8586
static final String METADATA_RESPONSE_EMPTY_CONTENT_ERROR_MESSAGE =
8687
"Empty content from metadata token server request.";
@@ -454,7 +455,6 @@ public AccessToken refreshAccessToken() throws IOException {
454455
int expiresInSeconds =
455456
OAuth2Utils.validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX);
456457
long expiresAtMilliseconds = clock.currentTimeMillis() + expiresInSeconds * 1000;
457-
458458
return new AccessToken(accessToken, new Date(expiresAtMilliseconds));
459459
}
460460

@@ -779,6 +779,11 @@ public static Builder newBuilder() {
779779
*
780780
* @throws RuntimeException if the default service account cannot be read
781781
*/
782+
@Override
783+
HttpTransportFactory getTransportFactory() {
784+
return transportFactory;
785+
}
786+
782787
@Override
783788
// todo(#314) getAccount should not throw a RuntimeException
784789
public String getAccount() {
@@ -792,6 +797,13 @@ public String getAccount() {
792797
return principal;
793798
}
794799

800+
@InternalApi
801+
@Override
802+
public String getRegionalAccessBoundaryUrl() throws IOException {
803+
return String.format(
804+
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT, getAccount());
805+
}
806+
795807
/**
796808
* Signs the provided bytes using the private key associated with the service account.
797809
*

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131

3232
package com.google.auth.oauth2;
3333

34+
import static com.google.auth.oauth2.OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL;
3435
import static com.google.auth.oauth2.OAuth2Utils.JSON_FACTORY;
36+
import static com.google.auth.oauth2.OAuth2Utils.WORKFORCE_AUDIENCE_PATTERN;
3537

3638
import com.google.api.client.http.GenericUrl;
3739
import com.google.api.client.http.HttpHeaders;
@@ -43,6 +45,7 @@
4345
import com.google.api.client.json.JsonObjectParser;
4446
import com.google.api.client.util.GenericData;
4547
import com.google.api.client.util.Preconditions;
48+
import com.google.api.core.InternalApi;
4649
import com.google.auth.http.HttpTransportFactory;
4750
import com.google.common.base.MoreObjects;
4851
import com.google.common.io.BaseEncoding;
@@ -54,6 +57,7 @@
5457
import java.util.Date;
5558
import java.util.Map;
5659
import java.util.Objects;
60+
import java.util.regex.Matcher;
5761
import javax.annotation.Nullable;
5862

5963
/**
@@ -74,7 +78,8 @@
7478
* }
7579
* </pre>
7680
*/
77-
public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials {
81+
public class ExternalAccountAuthorizedUserCredentials extends GoogleCredentials
82+
implements RegionalAccessBoundaryProvider {
7883
private static final LoggerProvider LOGGER_PROVIDER =
7984
LoggerProvider.forClazz(ExternalAccountAuthorizedUserCredentials.class);
8085

@@ -229,6 +234,24 @@ public AccessToken refreshAccessToken() throws IOException {
229234
.build();
230235
}
231236

237+
@InternalApi
238+
@Override
239+
public String getRegionalAccessBoundaryUrl() throws IOException {
240+
Matcher matcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
241+
if (!matcher.matches()) {
242+
throw new IllegalStateException(
243+
"The provided audience is not in the correct format for a workforce pool. "
244+
+ "Refer: https://docs.cloud.google.com/iam/docs/principal-identifiers");
245+
}
246+
String poolId = matcher.group("pool");
247+
return String.format(IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, poolId);
248+
}
249+
250+
@Override
251+
HttpTransportFactory getTransportFactory() {
252+
return transportFactory;
253+
}
254+
232255
@Nullable
233256
public String getAudience() {
234257
return audience;

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

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
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;
@@ -54,6 +56,7 @@
5456
import java.util.Locale;
5557
import java.util.Map;
5658
import java.util.concurrent.Executor;
59+
import java.util.regex.Matcher;
5760
import java.util.regex.Pattern;
5861
import javax.annotation.Nullable;
5962

@@ -63,7 +66,8 @@
6366
* <p>Handles initializing external credentials, calls to the Security Token Service, and service
6467
* account impersonation.
6568
*/
66-
public abstract class ExternalAccountCredentials extends GoogleCredentials {
69+
public abstract class ExternalAccountCredentials extends GoogleCredentials
70+
implements RegionalAccessBoundaryProvider {
6771

6872
private static final long serialVersionUID = 8049126194174465023L;
6973

@@ -577,6 +581,11 @@ protected AccessToken exchangeExternalCredentialForAccessToken(
577581
*/
578582
public abstract String retrieveSubjectToken() throws IOException;
579583

584+
@Override
585+
HttpTransportFactory getTransportFactory() {
586+
return transportFactory;
587+
}
588+
580589
public String getAudience() {
581590
return audience;
582591
}
@@ -620,6 +629,37 @@ public String getServiceAccountEmail() {
620629
return ImpersonatedCredentials.extractTargetPrincipal(serviceAccountImpersonationUrl);
621630
}
622631

632+
@InternalApi
633+
@Override
634+
public String getRegionalAccessBoundaryUrl() throws IOException {
635+
if (getServiceAccountEmail() != null) {
636+
return String.format(
637+
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_SERVICE_ACCOUNT,
638+
getServiceAccountEmail());
639+
}
640+
641+
Matcher workforceMatcher = WORKFORCE_AUDIENCE_PATTERN.matcher(getAudience());
642+
if (workforceMatcher.matches()) {
643+
String poolId = workforceMatcher.group("pool");
644+
return String.format(
645+
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKFORCE_POOL, poolId);
646+
}
647+
648+
Matcher workloadMatcher = WORKLOAD_AUDIENCE_PATTERN.matcher(getAudience());
649+
if (workloadMatcher.matches()) {
650+
String projectNumber = workloadMatcher.group("project");
651+
String poolId = workloadMatcher.group("pool");
652+
return String.format(
653+
OAuth2Utils.IAM_CREDENTIALS_ALLOWED_LOCATIONS_URL_FORMAT_WORKLOAD_POOL,
654+
projectNumber,
655+
poolId);
656+
}
657+
658+
throw new IllegalStateException(
659+
"The provided audience is not in a valid format for either a workload identity pool or a workforce pool."
660+
+ " Refer: https://docs.cloud.google.com/iam/docs/principal-identifiers");
661+
}
662+
623663
@Nullable
624664
public String getClientId() {
625665
return clientId;

0 commit comments

Comments
 (0)