Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
Expand Down Expand Up @@ -99,6 +101,8 @@ final class ITWorkloadIdentityFederationTest {
private static final String DEFAULT_MTLS_CERT_CONFIG_PATH =
"testresources/mtls/certificate_config.json";

private static volatile String syncedOidcJwtKeyId;

private String clientEmail;
private String clientId;
private String privateKeyId;
Expand Down Expand Up @@ -648,6 +652,8 @@ private String computeCertThumbprint(KeyStore keyStore) throws IOException {

private String generateSignedOidcJwt(String audience, String x5tS256, boolean includeMayAct)
throws IOException {
ensureOidcJwtProviderJwksSynced();

JsonWebSignature.Header header = new JsonWebSignature.Header();
header.setAlgorithm("RS256");
header.setType("JWT");
Expand Down Expand Up @@ -680,6 +686,88 @@ private String generateSignedOidcJwt(String audience, String x5tS256, boolean in
}
}

/**
* Ensures the {@code oidc-jwt-1} Workload Identity Pool Provider's static {@code jwksJson}
* includes the current service account {@code private_key_id}. When the integration test service
* account rotates keys bi-monthly, the first test run automatically fetches the live public JWK
* set for the service account and patches {@code oidc-jwt-1} using the service account's own
* {@code roles/iam.workloadIdentityPoolAdmin} permission.
*/
private void ensureOidcJwtProviderJwksSynced() throws IOException {
if (privateKeyId.equals(syncedOidcJwtKeyId)) {
return;
}
synchronized (ITWorkloadIdentityFederationTest.class) {
if (privateKeyId.equals(syncedOidcJwtKeyId)) {
return;
}
GoogleCredentials adminCredentials =
GoogleCredentials.getApplicationDefault().createScoped(OAuth2Utils.CLOUD_PLATFORM_SCOPE);
HttpTransport transport = new NetHttpTransport();
HttpRequestFactory requestFactory =
transport.createRequestFactory(new HttpCredentialsAdapter(adminCredentials));
JsonObjectParser parser = new JsonObjectParser(GsonFactory.getDefaultInstance());

String providerResource = OIDC_JWT_AUDIENCE.substring("//iam.googleapis.com/".length());
String providerUrl = "https://iam.googleapis.com/v1/" + providerResource;

HttpRequest getRequest = requestFactory.buildGetRequest(new GenericUrl(providerUrl));
getRequest.setParser(parser);
GenericData providerData;
HttpResponse getResponse = getRequest.execute();
try {
providerData = getResponse.parseAs(GenericData.class);
} finally {
getResponse.disconnect();
}

Map<?, ?> oidc = (Map<?, ?>) providerData.get("oidc");
String currentJwksJson = oidc != null ? (String) oidc.get("jwksJson") : null;
if (currentJwksJson != null && currentJwksJson.contains(privateKeyId)) {
syncedOidcJwtKeyId = privateKeyId;
return;
}

HttpRequest jwksRequest =
transport
.createRequestFactory()
.buildGetRequest(
new GenericUrl(
"https://www.googleapis.com/service_accounts/v1/jwk/" + clientEmail));
String liveJwksJson;
HttpResponse jwksResponse = jwksRequest.execute();
try {
liveJwksJson = jwksResponse.parseAsString();
} finally {
jwksResponse.disconnect();
}

GenericJson oidcPatch = new GenericJson();
oidcPatch.setFactory(OAuth2Utils.JSON_FACTORY);
oidcPatch.put("jwksJson", liveJwksJson);

GenericJson patchBody = new GenericJson();
patchBody.setFactory(OAuth2Utils.JSON_FACTORY);
patchBody.put("oidc", oidcPatch);

HttpContent content =
new ByteArrayContent(
"application/json", patchBody.toPrettyString().getBytes(StandardCharsets.UTF_8));
HttpRequest patchRequest =
requestFactory.buildPostRequest(
new GenericUrl(providerUrl + "?updateMask=oidc.jwksJson"), content);
patchRequest.getHeaders().set("X-HTTP-Method-Override", "PATCH");
HttpResponse patchResponse = patchRequest.execute();
try {
assertTrue(patchResponse.isSuccessStatusCode());
} finally {
patchResponse.disconnect();
}

syncedOidcJwtKeyId = privateKeyId;
}
}

private void callGcs(GoogleCredentials credentials) throws IOException {
callGcs(credentials, new NetHttpTransport(), "https://storage.googleapis.com/storage/v1/b/");
}
Expand Down
Loading