Authenticator { GoogleIdToken authenticate(String token) { final GoogleIdToken googleIdToken; try { googleIdToken = verifyIdToken(token); } catch (IOException e) { logger.warn("Rejecting auth token: Failed to verify token", e); return null; } if (googleIdToken == null) { logger.warn("Rejecting auth token: verifyIdToken returned null"); return null; } final String email = googleIdToken.getPayload().getEmail(); if (email == null) { logger.warn("Rejecting auth token: No email in id token"); return null; } final String domain = getDomain(email); if (domain == null) { logger.warn("Rejecting auth token: Invalid email address {}", email); return null; } else if (domainWhitelist.contains(domain)) { logger.debug("Validating auth token: Domain {} in whitelist", domain); return googleIdToken; } if (validatedEmailCache.getIfPresent(email) != null) { logger.debug("Validating auth token: Cache hit for {}", email); return googleIdToken; } if (!SERVICE_ACCOUNT_PATTERN.matcher(email).matches()) { logger.warn("Rejecting auth token: Not a service account: {}", email); return null; } if (!allowedAudiences.isEmpty() && googleIdToken.getPayload().getAudience() != null) { if (!googleIdToken.verifyAudience(allowedAudiences)) { logger.warn("Rejecting auth token: ID token wasn't intended for Styx"); return null; } } final String projectId; try { projectId = checkServiceAccountProject(email); } catch (IOException e) { logger.warn("Rejecting auth token: Cannot authenticate {}", email); return null; } if (projectId != null) { validatedEmailCache.put(email, projectId); return googleIdToken; } logger.warn("Rejecting auth token"); return null; } Authenticator(GoogleIdTokenVerifier googleIdTokenVerifier,
                CloudResourceManager cloudResourceManager,
                Iam iam,
                AuthenticatorConfiguration configuration);   Authenticator(GoogleIdTokenVerifier googleIdTokenVerifier,
                CloudResourceManager cloudResourceManager,
                Iam iam,
                AuthenticatorConfiguration configuration,
                WaitStrategy retryWaitStrategy,
                StopStrategy retryStopStrategy);   }
@Test public void shouldFailExternalProject() { idTokenPayload.setEmail("foo@external.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); }
@Test public void shouldFailIfInvalidToken() throws GeneralSecurityException, IOException { when(verifier.verify(anyString())).thenThrow(new GeneralSecurityException()); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(idToken); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldFailToVerifyToken() throws GeneralSecurityException, IOException { when(verifier.verify(anyString())).thenThrow(new IOException()); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(idToken); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldBeWhitelisted() { idTokenPayload.setEmail("foo@example.com"); assertThat(validator.authenticate("token"), is(idToken)); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldFailIfInvalidEmailAddress() { idTokenPayload.setEmail("example.com"); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldHitProjectCache() throws IOException { idTokenPayload.setEmail("foo@foo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(idToken)); reset(cloudResourceManager.projects()); idTokenPayload.setEmail("bar@foo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(idToken)); verify(cloudResourceManager.projects(), never()).getAncestry(eq("foo"), any()); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldFailToGetProject() throws IOException { doThrow(new IOException()).when(projectsGetAncestry).execute(); idTokenPayload.setEmail("foo@barfoo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(iam); verify(projectsGetAncestry, times(3)).execute(); }
@Test public void shouldFailForNonExistProject() throws IOException { doThrow(NOT_FOUND).when(projectsGetAncestry).execute(); idTokenPayload.setEmail("foo@barfoo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verify(projectsGetAncestry).execute(); verifyZeroInteractions(iam); }
@Test public void shouldFailIfNoPermissionGettingProject() throws IOException { doThrow(PERMISSION_DENIED).when(projectsGetAncestry).execute(); idTokenPayload.setEmail("foo@barfoo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(iam); verify(projectsGetAncestry).execute(); }
@Test public void shouldFailToGetProjectFromIAM() throws IOException { doThrow(new IOException()).when(serviceAccountsGet).execute(); idTokenPayload.setEmail("foo@developer.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verify(serviceAccountsGet, times(3)).execute(); verifyZeroInteractions(projectsGetAncestry); }
@Test public void shouldFailForNonExistServiceAccount() throws IOException { doThrow(NOT_FOUND).when(serviceAccountsGet).execute(); idTokenPayload.setEmail("foo@developer.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verify(serviceAccountsGet).execute(); verifyZeroInteractions(projectsGetAncestry); }
@Test public void shouldFailIfNoPermissionGettingServiceAccountFromIAM() throws IOException { doThrow(PERMISSION_DENIED).when(serviceAccountsGet).execute(); idTokenPayload.setEmail("foo@developer.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verify(serviceAccountsGet).execute(); verifyZeroInteractions(projectsGetAncestry); }
@Test public void shouldFailTokenWithNoEmail() { idTokenPayload.setEmail(null); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldDenyWhitelistedEmailNonSA() { idTokenPayload.setEmail("foo@bar.com"); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldDenyIncorrectAudience() { idTokenPayload.setAudience("https: idTokenPayload.setEmail("foo@foo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(nullValue())); verifyZeroInteractions(projectsGetAncestry); verifyZeroInteractions(iam); }
@Test public void shouldAllowIncorrectAudienceIfNoAllowedAudiencesConfigured() { var configurationWithNoAllowedAudiences = AuthenticatorConfigurationBuilder.from(CONFIGURATION) .allowedAudiences(Set.of()) .build(); validator = new Authenticator(verifier, cloudResourceManager, iam, configurationWithNoAllowedAudiences); idTokenPayload.setAudience("https: idTokenPayload.setEmail("foo@foo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(idToken)); }
@Test public void shouldAllowNoAudience() { idTokenPayload.setAudience(null); idTokenPayload.setEmail("foo@foo.iam.gserviceaccount.com"); assertThat(validator.authenticate("token"), is(idToken)); }