Skip to content

Commit c21c663

Browse files
committed
Format changes
1 parent 302038f commit c21c663

File tree

4 files changed

+1621
-1622
lines changed

4 files changed

+1621
-1622
lines changed

oauth2_http/java/com/google/auth/oauth2/AgentIdentityUtils.java

Lines changed: 167 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -57,189 +57,186 @@
5757

5858
/** Internal utility class for handling Agent Identity certificate-bound access tokens. */
5959
final class AgentIdentityUtils {
60-
private static final Logger LOGGER = Logger.getLogger(AgentIdentityUtils.class.getName());
61-
62-
static final String GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG";
63-
static final String GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES =
64-
"GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES";
65-
66-
private static final List<Pattern> AGENT_IDENTITY_SPIFFE_PATTERNS =
67-
ImmutableList.of(
68-
Pattern.compile("^agents\\.global\\.org-\\d+\\.system\\.id\\.goog$"),
69-
Pattern.compile("^agents\\.global\\.proj-\\d+\\.system\\.id\\.goog$"));
70-
71-
// Polling configuration
72-
static long TOTAL_TIMEOUT_MS = 30000; // 30 seconds
73-
static long FAST_POLL_DURATION_MS = 5000; // 5 seconds
74-
static long FAST_POLL_INTERVAL_MS = 100; // 0.1 seconds
75-
static long SLOW_POLL_INTERVAL_MS = 500; // 0.5 seconds
76-
77-
private static final int SAN_URI_TYPE = 6;
78-
private static final String SPIFFE_SCHEME_PREFIX = "spiffe://";
79-
80-
// Interface to allow mocking System.getenv for tests without exposing it publicly.
81-
interface EnvReader {
82-
String getEnv(String name);
83-
}
84-
85-
private static EnvReader envReader = System::getenv;
86-
87-
private AgentIdentityUtils() {}
88-
89-
/**
90-
* Gets the Agent Identity certificate if available and enabled.
91-
*
92-
* @return The X509Certificate if found and Agent Identities are enabled, null otherwise.
93-
* @throws IOException If there is an error reading the certificate file after retries.
94-
*/
95-
static X509Certificate getAgentIdentityCertificate() throws IOException {
96-
if (isOptedOut()) {
97-
return null;
98-
}
99-
100-
String certConfigPath = envReader.getEnv(GOOGLE_API_CERTIFICATE_CONFIG);
101-
if (Strings.isNullOrEmpty(certConfigPath)) {
102-
return null;
103-
}
104-
105-
String certPath = getCertificatePathWithRetry(certConfigPath);
106-
return parseCertificate(certPath);
60+
private static final Logger LOGGER = Logger.getLogger(AgentIdentityUtils.class.getName());
61+
62+
static final String GOOGLE_API_CERTIFICATE_CONFIG = "GOOGLE_API_CERTIFICATE_CONFIG";
63+
static final String GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES =
64+
"GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES";
65+
66+
private static final List<Pattern> AGENT_IDENTITY_SPIFFE_PATTERNS =
67+
ImmutableList.of(
68+
Pattern.compile("^agents\\.global\\.org-\\d+\\.system\\.id\\.goog$"),
69+
Pattern.compile("^agents\\.global\\.proj-\\d+\\.system\\.id\\.goog$"));
70+
71+
// Polling configuration
72+
static long TOTAL_TIMEOUT_MS = 30000; // 30 seconds
73+
static long FAST_POLL_DURATION_MS = 5000; // 5 seconds
74+
static long FAST_POLL_INTERVAL_MS = 100; // 0.1 seconds
75+
static long SLOW_POLL_INTERVAL_MS = 500; // 0.5 seconds
76+
77+
private static final int SAN_URI_TYPE = 6;
78+
private static final String SPIFFE_SCHEME_PREFIX = "spiffe://";
79+
80+
// Interface to allow mocking System.getenv for tests without exposing it publicly.
81+
interface EnvReader {
82+
String getEnv(String name);
83+
}
84+
85+
private static EnvReader envReader = System::getenv;
86+
87+
private AgentIdentityUtils() {}
88+
89+
/**
90+
* Gets the Agent Identity certificate if available and enabled.
91+
*
92+
* @return The X509Certificate if found and Agent Identities are enabled, null otherwise.
93+
* @throws IOException If there is an error reading the certificate file after retries.
94+
*/
95+
static X509Certificate getAgentIdentityCertificate() throws IOException {
96+
if (isOptedOut()) {
97+
return null;
10798
}
10899

109-
/** Checks if the user has opted out of Agent Token sharing. */
110-
private static boolean isOptedOut() {
111-
String optOut = envReader.getEnv(GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES);
112-
return optOut != null && "false".equalsIgnoreCase(optOut);
100+
String certConfigPath = envReader.getEnv(GOOGLE_API_CERTIFICATE_CONFIG);
101+
if (Strings.isNullOrEmpty(certConfigPath)) {
102+
return null;
113103
}
114104

115-
/** Polls for the certificate config file and the certificate file it references. */
116-
private static String getCertificatePathWithRetry(String certConfigPath) throws IOException {
117-
long startTime = System.currentTimeMillis();
118-
boolean warned = false;
119-
120-
while (true) {
121-
try {
122-
if (Files.exists(Paths.get(certConfigPath))) {
123-
String certPath = extractCertPathFromConfig(certConfigPath);
124-
if (!Strings.isNullOrEmpty(certPath) && Files.exists(Paths.get(certPath))) {
125-
return certPath;
126-
}
127-
}
128-
} catch (Exception e) {
129-
// Ignore exceptions during polling and retry
130-
LOGGER.log(Level.FINE, "Error while polling for certificate files", e);
131-
}
132-
133-
long elapsedTime = System.currentTimeMillis() - startTime;
134-
if (elapsedTime >= TOTAL_TIMEOUT_MS) {
135-
throw new IOException(
136-
"Certificate config or certificate file not found after multiple retries. "
137-
+ "Token binding protection is failing. You can turn off this protection by setting "
138-
+ GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
139-
+ " to false to fall back to unbound tokens.");
140-
}
141-
142-
if (!warned) {
143-
LOGGER.warning(
144-
String.format(
145-
"Certificate config file not found at %s (from %s environment variable). "
146-
+ "Retrying for up to %d seconds.",
147-
certConfigPath,
148-
GOOGLE_API_CERTIFICATE_CONFIG,
149-
TOTAL_TIMEOUT_MS / 1000));
150-
warned = true;
151-
}
152-
153-
try {
154-
long sleepTime =
155-
elapsedTime < FAST_POLL_DURATION_MS ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
156-
Thread.sleep(sleepTime);
157-
} catch (InterruptedException e) {
158-
Thread.currentThread().interrupt();
159-
throw new IOException("Interrupted while waiting for certificate files", e);
160-
}
105+
String certPath = getCertificatePathWithRetry(certConfigPath);
106+
return parseCertificate(certPath);
107+
}
108+
109+
/** Checks if the user has opted out of Agent Token sharing. */
110+
private static boolean isOptedOut() {
111+
String optOut = envReader.getEnv(GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES);
112+
return optOut != null && "false".equalsIgnoreCase(optOut);
113+
}
114+
115+
/** Polls for the certificate config file and the certificate file it references. */
116+
private static String getCertificatePathWithRetry(String certConfigPath) throws IOException {
117+
long startTime = System.currentTimeMillis();
118+
boolean warned = false;
119+
120+
while (true) {
121+
try {
122+
if (Files.exists(Paths.get(certConfigPath))) {
123+
String certPath = extractCertPathFromConfig(certConfigPath);
124+
if (!Strings.isNullOrEmpty(certPath) && Files.exists(Paths.get(certPath))) {
125+
return certPath;
126+
}
161127
}
128+
} catch (Exception e) {
129+
// Ignore exceptions during polling and retry
130+
LOGGER.log(Level.FINE, "Error while polling for certificate files", e);
131+
}
132+
133+
long elapsedTime = System.currentTimeMillis() - startTime;
134+
if (elapsedTime >= TOTAL_TIMEOUT_MS) {
135+
throw new IOException(
136+
"Certificate config or certificate file not found after multiple retries. "
137+
+ "Token binding protection is failing. You can turn off this protection by setting "
138+
+ GOOGLE_API_PREVENT_AGENT_TOKEN_SHARING_FOR_GCP_SERVICES
139+
+ " to false to fall back to unbound tokens.");
140+
}
141+
142+
if (!warned) {
143+
LOGGER.warning(
144+
String.format(
145+
"Certificate config file not found at %s (from %s environment variable). "
146+
+ "Retrying for up to %d seconds.",
147+
certConfigPath, GOOGLE_API_CERTIFICATE_CONFIG, TOTAL_TIMEOUT_MS / 1000));
148+
warned = true;
149+
}
150+
151+
try {
152+
long sleepTime =
153+
elapsedTime < FAST_POLL_DURATION_MS ? FAST_POLL_INTERVAL_MS : SLOW_POLL_INTERVAL_MS;
154+
Thread.sleep(sleepTime);
155+
} catch (InterruptedException e) {
156+
Thread.currentThread().interrupt();
157+
throw new IOException("Interrupted while waiting for certificate files", e);
158+
}
162159
}
163-
164-
@SuppressWarnings("unchecked")
165-
private static String extractCertPathFromConfig(String certConfigPath) throws IOException {
166-
try (InputStream stream = new FileInputStream(certConfigPath)) {
167-
JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY);
168-
GenericJson config =
169-
parser.parseAndClose(stream, StandardCharsets.UTF_8, GenericJson.class);
170-
Map<String, Object> certConfigs = (Map<String, Object>) config.get("cert_configs");
171-
if (certConfigs != null) {
172-
Map<String, Object> workload = (Map<String, Object>) certConfigs.get("workload");
173-
if (workload != null) {
174-
return (String) workload.get("cert_path");
175-
}
176-
}
160+
}
161+
162+
@SuppressWarnings("unchecked")
163+
private static String extractCertPathFromConfig(String certConfigPath) throws IOException {
164+
try (InputStream stream = new FileInputStream(certConfigPath)) {
165+
JsonObjectParser parser = new JsonObjectParser(OAuth2Utils.JSON_FACTORY);
166+
GenericJson config = parser.parseAndClose(stream, StandardCharsets.UTF_8, GenericJson.class);
167+
Map<String, Object> certConfigs = (Map<String, Object>) config.get("cert_configs");
168+
if (certConfigs != null) {
169+
Map<String, Object> workload = (Map<String, Object>) certConfigs.get("workload");
170+
if (workload != null) {
171+
return (String) workload.get("cert_path");
177172
}
178-
return null;
173+
}
179174
}
180-
181-
private static X509Certificate parseCertificate(String certPath) throws IOException {
182-
try (InputStream stream = new FileInputStream(certPath)) {
183-
CertificateFactory cf = CertificateFactory.getInstance("X.509");
184-
return (X509Certificate) cf.generateCertificate(stream);
185-
} catch (GeneralSecurityException e) {
186-
throw new IOException("Failed to parse certificate", e);
187-
}
175+
return null;
176+
}
177+
178+
private static X509Certificate parseCertificate(String certPath) throws IOException {
179+
try (InputStream stream = new FileInputStream(certPath)) {
180+
CertificateFactory cf = CertificateFactory.getInstance("X.509");
181+
return (X509Certificate) cf.generateCertificate(stream);
182+
} catch (GeneralSecurityException e) {
183+
throw new IOException("Failed to parse certificate", e);
188184
}
185+
}
189186

190-
/** Checks if the certificate belongs to an Agent Identity by inspecting SANs. */
191-
static boolean shouldRequestBoundToken(X509Certificate cert) {
192-
try {
193-
Collection<List<?>> sans = cert.getSubjectAlternativeNames();
194-
if (sans == null) {
195-
return false;
196-
}
197-
for (List<?> san : sans) {
198-
// SAN entry is a list where first element is the type (Integer) and second is value (mostly
199-
// String)
200-
if (san.size() >= 2
201-
&& san.get(0) instanceof Integer
202-
&& (Integer) san.get(0) == SAN_URI_TYPE) {
203-
Object value = san.get(1);
204-
if (value instanceof String) {
205-
String uri = (String) value;
206-
if (uri.startsWith(SPIFFE_SCHEME_PREFIX)) {
207-
// Extract trust domain: spiffe://<trust_domain>/...
208-
String withoutScheme = uri.substring(SPIFFE_SCHEME_PREFIX.length());
209-
int slashIndex = withoutScheme.indexOf('/');
210-
String trustDomain =
211-
(slashIndex == -1) ? withoutScheme : withoutScheme.substring(0, slashIndex);
212-
213-
for (Pattern pattern : AGENT_IDENTITY_SPIFFE_PATTERNS) {
214-
if (pattern.matcher(trustDomain).matches()) {
215-
return true;
216-
}
217-
}
218-
}
219-
}
187+
/** Checks if the certificate belongs to an Agent Identity by inspecting SANs. */
188+
static boolean shouldRequestBoundToken(X509Certificate cert) {
189+
try {
190+
Collection<List<?>> sans = cert.getSubjectAlternativeNames();
191+
if (sans == null) {
192+
return false;
193+
}
194+
for (List<?> san : sans) {
195+
// SAN entry is a list where first element is the type (Integer) and second is value (mostly
196+
// String)
197+
if (san.size() >= 2
198+
&& san.get(0) instanceof Integer
199+
&& (Integer) san.get(0) == SAN_URI_TYPE) {
200+
Object value = san.get(1);
201+
if (value instanceof String) {
202+
String uri = (String) value;
203+
if (uri.startsWith(SPIFFE_SCHEME_PREFIX)) {
204+
// Extract trust domain: spiffe://<trust_domain>/...
205+
String withoutScheme = uri.substring(SPIFFE_SCHEME_PREFIX.length());
206+
int slashIndex = withoutScheme.indexOf('/');
207+
String trustDomain =
208+
(slashIndex == -1) ? withoutScheme : withoutScheme.substring(0, slashIndex);
209+
210+
for (Pattern pattern : AGENT_IDENTITY_SPIFFE_PATTERNS) {
211+
if (pattern.matcher(trustDomain).matches()) {
212+
return true;
220213
}
214+
}
221215
}
222-
} catch (CertificateParsingException e) {
223-
LOGGER.log(Level.WARNING, "Failed to parse Subject Alternative Names from certificate", e);
216+
}
224217
}
225-
return false;
218+
}
219+
} catch (CertificateParsingException e) {
220+
LOGGER.log(Level.WARNING, "Failed to parse Subject Alternative Names from certificate", e);
226221
}
227-
228-
/** Calculates the SHA-256 fingerprint of the certificate, Base64Url encoded without padding. */
229-
static String calculateCertificateFingerprint(X509Certificate cert) throws IOException {
230-
try {
231-
MessageDigest md = MessageDigest.getInstance("SHA-256");
232-
byte[] der = cert.getEncoded();
233-
md.update(der);
234-
byte[] digest = md.digest();
235-
return BaseEncoding.base64Url().omitPadding().encode(digest);
236-
} catch (GeneralSecurityException e) {
237-
throw new IOException("Failed to calculate certificate fingerprint", e);
238-
}
222+
return false;
223+
}
224+
225+
/** Calculates the SHA-256 fingerprint of the certificate, Base64Url encoded without padding. */
226+
static String calculateCertificateFingerprint(X509Certificate cert) throws IOException {
227+
try {
228+
MessageDigest md = MessageDigest.getInstance("SHA-256");
229+
byte[] der = cert.getEncoded();
230+
md.update(der);
231+
byte[] digest = md.digest();
232+
return BaseEncoding.base64Url().omitPadding().encode(digest);
233+
} catch (GeneralSecurityException e) {
234+
throw new IOException("Failed to calculate certificate fingerprint", e);
239235
}
236+
}
240237

241-
@VisibleForTesting
242-
static void setEnvReader(EnvReader reader) {
243-
envReader = reader;
244-
}
245-
}
238+
@VisibleForTesting
239+
static void setEnvReader(EnvReader reader) {
240+
envReader = reader;
241+
}
242+
}

oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,8 +348,7 @@ public AccessToken refreshAccessToken() throws IOException {
348348
String tokenUrl = createTokenUrlWithScopes();
349349

350350
try {
351-
X509Certificate cert =
352-
AgentIdentityUtils.getAgentIdentityCertificate();
351+
X509Certificate cert = AgentIdentityUtils.getAgentIdentityCertificate();
353352
if (cert != null && AgentIdentityUtils.shouldRequestBoundToken(cert)) {
354353
String fingerprint = AgentIdentityUtils.calculateCertificateFingerprint(cert);
355354
GenericUrl url = new GenericUrl(tokenUrl);

0 commit comments

Comments
 (0)