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.
This commit is contained in:
gbrodman 2023-03-15 10:20:58 -04:00 committed by GitHub
parent 8ab2252272
commit cd0703c815
5 changed files with 30 additions and 12 deletions

View file

@ -107,9 +107,9 @@ public final class RegistryConfig {
} }
@Provides @Provides
@Config("cloudSchedulerServiceAccountEmail") @Config("serviceAccountEmails")
public static String provideCloudSchedulerServiceAccountEmail(RegistryConfigSettings config) { public static ImmutableList<String> provideServiceAccountEmails(RegistryConfigSettings config) {
return config.gcpProject.cloudSchedulerServiceAccountEmail; return ImmutableList.copyOf(config.gcpProject.serviceAccountEmails);
} }
@Provides @Provides

View file

@ -54,7 +54,7 @@ public class RegistryConfigSettings {
public String backendServiceUrl; public String backendServiceUrl;
public String toolsServiceUrl; public String toolsServiceUrl;
public String pubapiServiceUrl; public String pubapiServiceUrl;
public String cloudSchedulerServiceAccountEmail; public List<String> serviceAccountEmails;
} }
/** Configuration options for OAuth settings for authenticating users. */ /** Configuration options for OAuth settings for authenticating users. */

View file

@ -22,8 +22,11 @@ gcpProject:
backendServiceUrl: https://localhost backendServiceUrl: https://localhost
toolsServiceUrl: https://localhost toolsServiceUrl: https://localhost
pubapiServiceUrl: https://localhost pubapiServiceUrl: https://localhost
# Service account used by Cloud Scheduler to send authenticated requests. # Service accounts eligible for authorization (e.g. default service account,
cloudSchedulerServiceAccountEmail: cloud-scheduler-email@email.com # account used by Cloud Scheduler) to send authenticated requests.
serviceAccountEmails:
- default-service-account-email@email.com
- cloud-scheduler-email@email.com
gSuite: gSuite:
# Publicly accessible domain name of the running G Suite instance. # Publicly accessible domain name of the running G Suite instance.

View file

@ -18,6 +18,7 @@ import static com.google.common.net.HttpHeaders.AUTHORIZATION;
import static google.registry.request.auth.AuthLevel.APP; import static google.registry.request.auth.AuthLevel.APP;
import com.google.auth.oauth2.TokenVerifier; import com.google.auth.oauth2.TokenVerifier;
import com.google.common.collect.ImmutableList;
import google.registry.config.RegistryConfig.Config; import google.registry.config.RegistryConfig.Config;
import google.registry.request.auth.AuthModule.ServiceAccount; import google.registry.request.auth.AuthModule.ServiceAccount;
import javax.inject.Inject; import javax.inject.Inject;
@ -30,16 +31,16 @@ import javax.servlet.http.HttpServletRequest;
*/ */
public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase { public class ServiceAccountAuthenticationMechanism extends IdTokenAuthenticationBase {
private final String cloudSchedulerEmailPrefix;
private static final String BEARER_PREFIX = "Bearer "; private static final String BEARER_PREFIX = "Bearer ";
private final ImmutableList<String> serviceAccountEmails;
@Inject @Inject
public ServiceAccountAuthenticationMechanism( public ServiceAccountAuthenticationMechanism(
@ServiceAccount TokenVerifier tokenVerifier, @ServiceAccount TokenVerifier tokenVerifier,
@Config("cloudSchedulerServiceAccountEmail") String cloudSchedulerEmailPrefix) { @Config("serviceAccountEmails") ImmutableList<String> serviceAccountEmails) {
super(tokenVerifier); super(tokenVerifier);
this.cloudSchedulerEmailPrefix = cloudSchedulerEmailPrefix; this.serviceAccountEmails = serviceAccountEmails;
} }
@Override @Override
@ -53,7 +54,7 @@ public class ServiceAccountAuthenticationMechanism extends IdTokenAuthentication
@Override @Override
AuthResult authResultFromEmail(String emailAddress) { AuthResult authResultFromEmail(String emailAddress) {
if (emailAddress.equals(cloudSchedulerEmailPrefix)) { if (serviceAccountEmails.stream().anyMatch(e -> e.equals(emailAddress))) {
return AuthResult.create(APP); return AuthResult.create(APP);
} else { } else {
return AuthResult.NOT_AUTHENTICATED; return AuthResult.NOT_AUTHENTICATED;

View file

@ -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;
import com.google.api.client.json.webtoken.JsonWebSignature.Header; import com.google.api.client.json.webtoken.JsonWebSignature.Header;
import com.google.auth.oauth2.TokenVerifier; import com.google.auth.oauth2.TokenVerifier;
import com.google.common.collect.ImmutableList;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -44,7 +45,8 @@ class ServiceAccountAuthenticationMechanismTest {
@BeforeEach @BeforeEach
void beforeEach() throws Exception { void beforeEach() throws Exception {
serviceAccountAuthenticationMechanism = 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"); when(request.getHeader(AUTHORIZATION)).thenReturn("Bearer jwtValue");
Payload payload = new Payload(); Payload payload = new Payload();
payload.setEmail("sa-prefix@email.com"); payload.setEmail("sa-prefix@email.com");
@ -59,6 +61,18 @@ class ServiceAccountAuthenticationMechanismTest {
assertThat(authResult.authLevel()).isEqualTo(AuthLevel.APP); 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 @Test
void testFails_authenticateWrongEmail() throws Exception { void testFails_authenticateWrongEmail() throws Exception {
token.getPayload().set("email", "not-service-account-email@email.com"); token.getPayload().set("email", "not-service-account-email@email.com");