Allow configuration of service account id and project id values GCP IAM Authentication.
Original pull request: gh-287. Closes gh-261.
This commit is contained in:
committed by
Mark Paluch
parent
722368d59e
commit
41645f1034
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
/**
|
||||
* Default implementation to obtain a GCP project id for GCP IAM authentication.
|
||||
* Used by {@link GcpIamAuthentication}.
|
||||
*
|
||||
* @author Magnus Jungsbluth
|
||||
* @since 2.1
|
||||
* @see GcpIamAuthentication
|
||||
*/
|
||||
public class DefaultGcpProjectIdProvider implements GcpProjectIdProvider {
|
||||
|
||||
@Override
|
||||
public String getProjectId(GoogleCredential credential) {
|
||||
if (StringUtils.isEmpty(credential.getServiceAccountProjectId())) {
|
||||
return "-";
|
||||
} else {
|
||||
return credential.getServiceAccountProjectId();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
/**
|
||||
* Default implementation to obtain a service account id for GCP IAM authentication.
|
||||
* Used by {@link GcpIamAuthentication}.
|
||||
*
|
||||
* @author Magnus Jungsbluth
|
||||
* @since 2.1
|
||||
* @see GcpIamAuthentication
|
||||
*/
|
||||
public class DefaultGcpServiceAccountIdProvider implements GcpServiceAccountIdProvider {
|
||||
|
||||
@Override
|
||||
public String getServiceAccountId(GoogleCredential credential) {
|
||||
Assert.notNull(credential.getServiceAccountId(), "The configured GoogleCredential does not represent a service account. Configure the service account id manually");
|
||||
|
||||
return credential.getServiceAccountId();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,8 +125,8 @@ public class GcpIamAuthentication extends GcpJwtAuthenticationSupport implements
|
||||
|
||||
protected String signJwt() {
|
||||
|
||||
String projectId = credential.getServiceAccountProjectId();
|
||||
String serviceAccount = credential.getServiceAccountId();
|
||||
String projectId = options.getProjectIdProvider().getProjectId(credential);
|
||||
String serviceAccount = options.getServiceAccountIdProvider().getServiceAccountId(credential);
|
||||
Map<String, Object> jwtPayload = getJwtPayload(options, serviceAccount);
|
||||
|
||||
Iam iam = new Builder(httpTransport, JSON_FACTORY, credential)
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.vault.authentication;
|
||||
import java.time.Clock;
|
||||
import java.time.Duration;
|
||||
|
||||
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||
import com.google.api.client.auth.oauth2.Credential;
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
@@ -65,15 +64,27 @@ public class GcpIamAuthenticationOptions {
|
||||
*/
|
||||
private final Clock clock;
|
||||
|
||||
/**
|
||||
* Provide the service account id to use as sub/iss claims
|
||||
*/
|
||||
private final GcpServiceAccountIdProvider serviceAccountIdSupplier;
|
||||
|
||||
/**
|
||||
* The GCP project id to use in GCP IAM API calls
|
||||
*/
|
||||
private final GcpProjectIdProvider projectIdSupplier;
|
||||
|
||||
private GcpIamAuthenticationOptions(String path,
|
||||
GcpCredentialSupplier credentialSupplier, String role, Duration jwtValidity,
|
||||
Clock clock) {
|
||||
Clock clock, GcpServiceAccountIdProvider serviceAccountIdSupplier, GcpProjectIdProvider projectIdSupplier) {
|
||||
|
||||
this.path = path;
|
||||
this.credentialSupplier = credentialSupplier;
|
||||
this.role = role;
|
||||
this.jwtValidity = jwtValidity;
|
||||
this.clock = clock;
|
||||
this.serviceAccountIdSupplier = serviceAccountIdSupplier;
|
||||
this.projectIdSupplier = projectIdSupplier;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,6 +129,20 @@ public class GcpIamAuthenticationOptions {
|
||||
return clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide the service account id to use as sub/iss claims
|
||||
*/
|
||||
public GcpServiceAccountIdProvider getServiceAccountIdProvider() {
|
||||
return serviceAccountIdSupplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* The GCP project id to use in GCP IAM API calls
|
||||
*/
|
||||
public GcpProjectIdProvider getProjectIdProvider() {
|
||||
return projectIdSupplier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link GcpIamAuthenticationOptions}.
|
||||
*/
|
||||
@@ -135,6 +160,10 @@ public class GcpIamAuthenticationOptions {
|
||||
|
||||
private Clock clock = Clock.systemDefaultZone();
|
||||
|
||||
private GcpServiceAccountIdProvider serviceAccountIdProvider = new DefaultGcpServiceAccountIdProvider();
|
||||
|
||||
private GcpProjectIdProvider projectIdProvider = new DefaultGcpProjectIdProvider();
|
||||
|
||||
GcpIamAuthenticationOptionsBuilder() {
|
||||
}
|
||||
|
||||
@@ -169,7 +198,7 @@ public class GcpIamAuthenticationOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link AWSCredentialsProvider}, required to create a signed JWT.
|
||||
* Configure an {@link GcpCredentialSupplier}, required to create a signed JWT.
|
||||
* Alternatively, configure static {@link #credential(GoogleCredential)
|
||||
* credentials}.
|
||||
*
|
||||
@@ -186,6 +215,61 @@ public class GcpIamAuthenticationOptions {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an explicit service account id to use in GCP IAM calls. If none is configured, falls back to using
|
||||
* {@link GoogleCredential#getServiceAccountId()}.
|
||||
*
|
||||
* @param serviceAccountId the service account id (email) to use
|
||||
* @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
|
||||
*/
|
||||
public GcpIamAuthenticationOptionsBuilder serviceAccountId(String serviceAccountId) {
|
||||
Assert.notNull(serviceAccountId, "Service account id may not be null");
|
||||
|
||||
return serviceAccountIdProvider((GoogleCredential credential) -> serviceAccountId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link GcpServiceAccountIdProvider} to obtain the service account id used in GCP IAM calls.
|
||||
* If none is configured, falls back to using {@link GoogleCredential#getServiceAccountId()}.
|
||||
*
|
||||
* @param serviceAccountIdProvider the service account id provider to use
|
||||
* @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
|
||||
* @see GcpServiceAccountIdProvider
|
||||
*/
|
||||
public GcpIamAuthenticationOptionsBuilder serviceAccountIdProvider(GcpServiceAccountIdProvider serviceAccountIdProvider) {
|
||||
Assert.notNull(serviceAccountIdProvider, "GcpServiceAccountIdProvider must not be null");
|
||||
|
||||
this.serviceAccountIdProvider = serviceAccountIdProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an explicit GCP project id to use in GCP IAM API calls. If none is configured, falls back using
|
||||
* {@link GoogleCredential#getServiceAccountProjectId()}.
|
||||
*
|
||||
* @param projectId the GCP project id to use in GCP IAM API calls
|
||||
* @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
|
||||
*/
|
||||
public GcpIamAuthenticationOptionsBuilder projectId(String projectId) {
|
||||
Assert.notNull(projectId, "GCP project id must not be null");
|
||||
|
||||
return projectIdProvider((GoogleCredential credential) -> projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure an {@link GcpProjectIdProvider} to use in GCP IAM API calls. If none is configured, falls back using
|
||||
* {@link GoogleCredential#getServiceAccountProjectId()}.
|
||||
*
|
||||
* @param projectIdProvider the GCP project id supplier to use in GCP IAM API calls
|
||||
* @return {@code this} {@link GcpIamAuthenticationOptionsBuilder}.
|
||||
*/
|
||||
public GcpIamAuthenticationOptionsBuilder projectIdProvider(GcpProjectIdProvider projectIdProvider) {
|
||||
Assert.notNull(projectIdProvider, "GcpProjectIdProvider must not be null");
|
||||
|
||||
this.projectIdProvider = projectIdProvider;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the name of the role against which the login is being attempted.
|
||||
*
|
||||
@@ -241,7 +325,7 @@ public class GcpIamAuthenticationOptions {
|
||||
Assert.notNull(role, "Role must not be null");
|
||||
|
||||
return new GcpIamAuthenticationOptions(path, credentialSupplier, role,
|
||||
jwtValidity, clock);
|
||||
jwtValidity, clock, serviceAccountIdProvider, projectIdProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
/**
|
||||
* Interface to obtain a GCP project id for GCP IAM authentication.
|
||||
* Implementations are used by {@link GcpIamAuthentication}.
|
||||
*
|
||||
* @author Magnus Jungsbluth
|
||||
* @since 2.1
|
||||
* @see GcpIamAuthentication
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GcpProjectIdProvider {
|
||||
|
||||
/**
|
||||
* Get a the GCP project id to used in Google Cloud IAM API calls.
|
||||
*
|
||||
* @return the service account id to use.
|
||||
*/
|
||||
String getProjectId(GoogleCredential credential);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
/**
|
||||
* Interface to obtain a service account id for GCP IAM authentication.
|
||||
* Implementations are used by {@link GcpIamAuthentication}.
|
||||
*
|
||||
* @author Magnus Jungsbluth
|
||||
* @since 2.1
|
||||
* @see GcpIamAuthentication
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface GcpServiceAccountIdProvider {
|
||||
|
||||
/**
|
||||
* Get a the service account id (email) to be placed in the signed JWT.
|
||||
*
|
||||
* @return the service account id to use.
|
||||
*/
|
||||
String getServiceAccountId(GoogleCredential credential);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package org.springframework.vault.authentication;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GcpIamAuthenticationOptions.GcpIamAuthenticationOptionsBuilder}
|
||||
*/
|
||||
public class GcpIamAuthenticationOptionsBuilderUnitTests {
|
||||
|
||||
private GoogleCredential createGoogleCredential() {
|
||||
GoogleCredential credential = new GoogleCredential.Builder().setServiceAccountId("hello@world")
|
||||
.setServiceAccountProjectId("foobar")
|
||||
.setServiceAccountPrivateKey(mock(PrivateKey.class))
|
||||
.setServiceAccountPrivateKeyId("key-id").build();
|
||||
credential.setAccessToken("foobar");
|
||||
return credential;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDefaultToCredentialServiceAccountId() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getServiceAccountIdProvider().getServiceAccountId(credential)).isEqualTo("hello@world");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowServiceAccountIdOverride() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.serviceAccountId("override@foo.com")
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getServiceAccountIdProvider().getServiceAccountId(credential)).isEqualTo("override@foo.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowServiceAccountIdProviderOverride() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.serviceAccountIdProvider((GoogleCredential googleCredential) -> "override@foo.com")
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getServiceAccountIdProvider().getServiceAccountId(credential)).isEqualTo("override@foo.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldDefaultToCredentialProjectId() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getProjectIdProvider().getProjectId(credential)).isEqualTo("foobar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowProjectIdOverride() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.projectId("my-project")
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getProjectIdProvider().getProjectId(credential)).isEqualTo("my-project");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAllowProjectIdProviderOverride() {
|
||||
GoogleCredential credential = createGoogleCredential();
|
||||
GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
|
||||
.credential(credential)
|
||||
.projectIdProvider((GoogleCredential googleCredential) -> "my-project")
|
||||
.role("foo")
|
||||
.build();
|
||||
|
||||
assertThat(options.getProjectIdProvider().getProjectId(credential)).isEqualTo("my-project");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user