From cd0703c81503392be9f7f661f67eeddf521b08ca Mon Sep 17 00:00:00 2001 From: gbrodman Date: Wed, 15 Mar 2023 10:20:58 -0400 Subject: [PATCH] Allow for multiple service accounts in authentication (#1963) When submitting tasks to Cloud Tasks, we will use the built-in OIDC authentication which runs under the default service account (not the cloud scheduler service account). We want either to work for app-level auth. --- .../google/registry/config/RegistryConfig.java | 6 +++--- .../registry/config/RegistryConfigSettings.java | 2 +- .../registry/config/files/default-config.yaml | 7 +++++-- .../ServiceAccountAuthenticationMechanism.java | 11 ++++++----- ...erviceAccountAuthenticationMechanismTest.java | 16 +++++++++++++++- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/google/registry/config/RegistryConfig.java b/core/src/main/java/google/registry/config/RegistryConfig.java index d6b824b90..4fb9bb7f1 100644 --- a/core/src/main/java/google/registry/config/RegistryConfig.java +++ b/core/src/main/java/google/registry/config/RegistryConfig.java @@ -107,9 +107,9 @@ public final class RegistryConfig { } @Provides - @Config("cloudSchedulerServiceAccountEmail") - public static String provideCloudSchedulerServiceAccountEmail(RegistryConfigSettings config) { - return config.gcpProject.cloudSchedulerServiceAccountEmail; + @Config("serviceAccountEmails") + public static ImmutableList provideServiceAccountEmails(RegistryConfigSettings config) { + return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails); } @Provides diff --git a/core/src/main/java/google/registry/config/RegistryConfigSettings.java b/core/src/main/java/google/registry/config/RegistryConfigSettings.java index 878206c83..f3701d660 100644 --- a/core/src/main/java/google/registry/config/RegistryConfigSettings.java +++ b/core/src/main/java/google/registry/config/RegistryConfigSettings.java @@ -54,7 +54,7 @@ public class RegistryConfigSettings { public String backendServiceUrl; public String toolsServiceUrl; public String pubapiServiceUrl; - public String cloudSchedulerServiceAccountEmail; + public List serviceAccountEmails; } /** Configuration options for OAuth settings for authenticating users. */ diff --git a/core/src/main/java/google/registry/config/files/default-config.yaml b/core/src/main/java/google/registry/config/files/default-config.yaml index d9111c271..dedb9826c 100644 --- a/core/src/main/java/google/registry/config/files/default-config.yaml +++ b/core/src/main/java/google/registry/config/files/default-config.yaml @@ -22,8 +22,11 @@ gcpProject: backendServiceUrl: https://localhost toolsServiceUrl: https://localhost pubapiServiceUrl: https://localhost - # Service account used by Cloud Scheduler to send authenticated requests. - cloudSchedulerServiceAccountEmail: cloud-scheduler-email@email.com + # Service accounts eligible for authorization (e.g. default service account, + # account used by Cloud Scheduler) to send authenticated requests. + serviceAccountEmails: + - default-service-account-email@email.com + - cloud-scheduler-email@email.com gSuite: # Publicly accessible domain name of the running G Suite instance. diff --git a/core/src/main/java/google/registry/request/auth/ServiceAccountAuthenticationMechanism.java b/core/src/main/java/google/registry/request/auth/ServiceAccountAuthenticationMechanism.java index cd0963656..bf20d49c3 100644 --- a/core/src/main/java/google/registry/request/auth/ServiceAccountAuthenticationMechanism.java +++ b/core/src/main/java/google/registry/request/auth/ServiceAccountAuthenticationMechanism.java @@ -18,6 +18,7 @@ import static com.google.common.net.HttpHeaders.AUTHORIZATION; import static google.registry.request.auth.AuthLevel.APP; import com.google.auth.oauth2.TokenVerifier; +import com.google.common.collect.ImmutableList; import google.registry.config.RegistryConfig.Config; import google.registry.request.auth.AuthModule.ServiceAccount; import javax.inject.Inject; @@ -30,16 +31,16 @@ import javax.servlet.http.HttpServletRequest; */ public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase { - private final String cloudSchedulerEmailPrefix; private static final String BEARER_PREFIX = "Bearer "; + private final ImmutableList serviceAccountEmails; + @Inject public ServiceAccountAuthenticationMechanism( @ServiceAccount TokenVerifier tokenVerifier, - @Config("cloudSchedulerServiceAccountEmail") String cloudSchedulerEmailPrefix) { - + @Config("serviceAccountEmails") ImmutableList serviceAccountEmails) { super(tokenVerifier); - this.cloudSchedulerEmailPrefix = cloudSchedulerEmailPrefix; + this.serviceAccountEmails = serviceAccountEmails; } @Override @@ -53,7 +54,7 @@ public class ServiceAccountAuthenticationMechanism extends IdTokenAuthentication @Override AuthResult authResultFromEmail(String emailAddress) { - if (emailAddress.equals(cloudSchedulerEmailPrefix)) { + if (serviceAccountEmails.stream().anyMatch(e -> e.equals(emailAddress))) { return AuthResult.create(APP); } else { return AuthResult.NOT_AUTHENTICATED; diff --git a/core/src/test/java/google/registry/request/auth/ServiceAccountAuthenticationMechanismTest.java b/core/src/test/java/google/registry/request/auth/ServiceAccountAuthenticationMechanismTest.java index db6a34c32..ef0aa925e 100644 --- a/core/src/test/java/google/registry/request/auth/ServiceAccountAuthenticationMechanismTest.java +++ b/core/src/test/java/google/registry/request/auth/ServiceAccountAuthenticationMechanismTest.java @@ -22,6 +22,7 @@ import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload; import com.google.api.client.json.webtoken.JsonWebSignature; import com.google.api.client.json.webtoken.JsonWebSignature.Header; import com.google.auth.oauth2.TokenVerifier; +import com.google.common.collect.ImmutableList; import javax.servlet.http.HttpServletRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -44,7 +45,8 @@ class ServiceAccountAuthenticationMechanismTest { @BeforeEach void beforeEach() throws Exception { serviceAccountAuthenticationMechanism = - new ServiceAccountAuthenticationMechanism(tokenVerifier, "sa-prefix@email.com"); + new ServiceAccountAuthenticationMechanism( + tokenVerifier, ImmutableList.of("sa-prefix@email.com", "cloud-tasks@email.com")); when(request.getHeader(AUTHORIZATION)).thenReturn("Bearer jwtValue"); Payload payload = new Payload(); payload.setEmail("sa-prefix@email.com"); @@ -59,6 +61,18 @@ class ServiceAccountAuthenticationMechanismTest { assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP); } + @Test + void testSuccess_secondEmail() throws Exception { + Payload payload = new Payload(); + payload.setEmail("cloud-tasks@email.com"); + token = new JsonWebSignature(new Header(), payload, new byte[0], new byte[0]); + when(tokenVerifier.verify("jwtValue")).thenReturn(token); + + AuthResult authResult = serviceAccountAuthenticationMechanism.authenticate(request); + assertThat(authResult.isAuthenticated()).isTrue(); + assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP); + } + @Test void testFails_authenticateWrongEmail() throws Exception { token.getPayload().set("email", "not-service-account-email@email.com");