From c38282bcf5fae4f062914a42c2424385b2bd8d0c Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 4 Dec 2024 21:14:16 -0500 Subject: [PATCH 01/53] add slf4j and other test dep. add logging utils. add logging to req/resp to auth endpoints. --- .../auth/oauth2/ComputeEngineCredentials.java | 12 + .../java/com/google/auth/oauth2/IamUtils.java | 9 + .../auth/oauth2/ImpersonatedCredentials.java | 5 + .../auth/oauth2/LoggingInterceptor.java | 65 ++ .../com/google/auth/oauth2/LoggingUtils.java | 572 ++++++++++++++++++ .../oauth2/ServiceAccountCredentials.java | 20 +- .../google/auth/oauth2/UserCredentials.java | 23 +- .../com/google/auth/TestAppender.java | 19 + .../google/auth/oauth2/LoggingUtilsTest.java | 124 ++++ .../oauth2/ServiceAccountCredentialsTest.java | 61 ++ .../auth/oauth2/UserCredentialsTest.java | 2 +- oauth2_http/pom.xml | 36 ++ oauth2_http/testresources/logback-test.xml | 27 + 13 files changed, 970 insertions(+), 5 deletions(-) create mode 100644 oauth2_http/java/com/google/auth/oauth2/LoggingInterceptor.java create mode 100644 oauth2_http/java/com/google/auth/oauth2/LoggingUtils.java create mode 100644 oauth2_http/javatests/com/google/auth/TestAppender.java create mode 100644 oauth2_http/javatests/com/google/auth/oauth2/LoggingUtilsTest.java create mode 100644 oauth2_http/testresources/logback-test.xml diff --git a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java index 15ce8947d..3e67d327f 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ComputeEngineCredentials.java @@ -92,6 +92,8 @@ public class ComputeEngineCredentials extends GoogleCredentials static final Duration COMPUTE_REFRESH_MARGIN = Duration.ofMinutes(3).plusSeconds(45); private static final Logger LOGGER = Logger.getLogger(ComputeEngineCredentials.class.getName()); + private static final org.slf4j.Logger SLF4JLOGGER = + LoggingUtils.getLogger(ComputeEngineCredentials.class); static final String DEFAULT_METADATA_SERVER_URL = "http://metadata.google.internal"; @@ -296,11 +298,14 @@ public AccessToken refreshAccessToken() throws IOException { throw new IOException("Empty content from metadata token server request."); } GenericData responseData = response.parseAs(GenericData.class); + LoggingUtils.logGenericData( + responseData, SLF4JLOGGER, "Auth response from refresh access token payload."); String accessToken = OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX); int expiresInSeconds = OAuth2Utils.validateInt32(responseData, "expires_in", PARSE_ERROR_PREFIX); long expiresAtMilliseconds = clock.currentTimeMillis() + expiresInSeconds * 1000; + return new AccessToken(accessToken, new Date(expiresAtMilliseconds)); } @@ -361,7 +366,9 @@ private HttpResponse getMetadataResponse( request.setThrowExceptionOnExecuteError(false); HttpResponse response; try { + LoggingUtils.logRequest(request, SLF4JLOGGER, "auth get metadata sending request."); response = request.execute(); + LoggingUtils.logResponse(response, SLF4JLOGGER, "auth get metadata received response."); } catch (UnknownHostException exception) { throw new IOException( "ComputeEngineCredentials cannot find the metadata server. This is" @@ -461,7 +468,10 @@ private static boolean pingComputeEngineMetadata( request, MetricsUtils.getGoogleCredentialsMetricsHeader( RequestType.METADATA_SERVER_PING, CredentialTypeForMetrics.DO_NOT_SEND)); + + LoggingUtils.logRequest(request, SLF4JLOGGER, "auth pin MDS."); HttpResponse response = request.execute(); + LoggingUtils.logResponse(response, SLF4JLOGGER, "auth received response from MDS."); try { // Internet providers can return a generic response to all requests, so it is necessary // to check that metadata header is present also. @@ -633,6 +643,8 @@ private String getDefaultServiceAccount() throws IOException { throw new IOException("Empty content from metadata token server request."); } GenericData responseData = response.parseAs(GenericData.class); + LoggingUtils.logGenericData( + responseData, SLF4JLOGGER, "Auth get default service account payload."); Map defaultAccount = OAuth2Utils.validateMap(responseData, "default", PARSE_ERROR_ACCOUNT); return OAuth2Utils.validateString(defaultAccount, "email", PARSE_ERROR_ACCOUNT); diff --git a/oauth2_http/java/com/google/auth/oauth2/IamUtils.java b/oauth2_http/java/com/google/auth/oauth2/IamUtils.java index 4a2a00870..4c08f2850 100644 --- a/oauth2_http/java/com/google/auth/oauth2/IamUtils.java +++ b/oauth2_http/java/com/google/auth/oauth2/IamUtils.java @@ -56,6 +56,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; +import org.slf4j.Logger; /** * This internal class provides shared utilities for interacting with the IAM API for common @@ -68,6 +69,7 @@ class IamUtils { "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateIdToken"; private static final String PARSE_ERROR_MESSAGE = "Error parsing error message response. "; private static final String PARSE_ERROR_SIGNATURE = "Error parsing signature response. "; + private static final Logger LOGGER = LoggingUtils.getLogger(IamUtils.class); // Following guidance for IAM retries: // https://cloud.google.com/iam/docs/retry-strategy#errors-to-retry @@ -142,7 +144,9 @@ private static String getSignature( IamUtils.IAM_RETRYABLE_STATUS_CODES.contains(response.getStatusCode()))); request.setIOExceptionHandler(new HttpBackOffIOExceptionHandler(backoff)); + LoggingUtils.logRequest(request, LOGGER, "auth sending request to get signature."); HttpResponse response = request.execute(); + LoggingUtils.logResponse(response, LOGGER, "auth received response for signature."); int statusCode = response.getStatusCode(); if (statusCode >= 400 && statusCode < HttpStatusCodes.STATUS_CODE_SERVER_ERROR) { GenericData responseError = response.parseAs(GenericData.class); @@ -169,6 +173,7 @@ private static String getSignature( } GenericData responseData = response.parseAs(GenericData.class); + LoggingUtils.logGenericData(responseData, LOGGER, "Auth response payload."); return OAuth2Utils.validateString(responseData, "signedBlob", PARSE_ERROR_SIGNATURE); } @@ -220,7 +225,10 @@ static IdToken getIdToken( MetricsUtils.getGoogleCredentialsMetricsHeader( RequestType.ID_TOKEN_REQUEST, credentialTypeForMetrics)); + LoggingUtils.logRequest(request, LOGGER, "auth sending request to get id token."); HttpResponse response = request.execute(); + + LoggingUtils.logResponse(response, LOGGER, "auth received response for id token."); int statusCode = response.getStatusCode(); if (statusCode >= 400 && statusCode < HttpStatusCodes.STATUS_CODE_SERVER_ERROR) { GenericData responseError = response.parseAs(GenericData.class); @@ -245,6 +253,7 @@ static IdToken getIdToken( } GenericJson responseData = response.parseAs(GenericJson.class); + LoggingUtils.logGenericData(responseData, LOGGER, "response data payload."); String rawToken = OAuth2Utils.validateString(responseData, "token", PARSE_ERROR_MESSAGE); return IdToken.create(rawToken); } diff --git a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java index 8ce922538..c0ce8745b 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java @@ -65,6 +65,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import org.slf4j.Logger; /** * ImpersonatedCredentials allowing credentials issued to a user or service account to impersonate @@ -110,6 +111,7 @@ public class ImpersonatedCredentials extends GoogleCredentials private int lifetime; private String iamEndpointOverride; private final String transportFactoryClassName; + private static final Logger LOGGER = LoggingUtils.getLogger(ImpersonatedCredentials.class); private transient HttpTransportFactory transportFactory; @@ -546,12 +548,15 @@ public AccessToken refreshAccessToken() throws IOException { HttpResponse response = null; try { + LoggingUtils.logRequest(request, LOGGER, "auth sending refresh access token request."); response = request.execute(); + LoggingUtils.logResponse(response, LOGGER, "auth received response."); } catch (IOException e) { throw new IOException("Error requesting access token", e); } GenericData responseData = response.parseAs(GenericData.class); + LoggingUtils.logGenericData(responseData, LOGGER, "Auth response payload."); response.disconnect(); String accessToken = diff --git a/oauth2_http/java/com/google/auth/oauth2/LoggingInterceptor.java b/oauth2_http/java/com/google/auth/oauth2/LoggingInterceptor.java new file mode 100644 index 000000000..94b301a28 --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/LoggingInterceptor.java @@ -0,0 +1,65 @@ +package com.google.auth.oauth2; + +import com.google.api.client.http.HttpExecuteInterceptor; +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpRequestInitializer; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.HttpResponseInterceptor; +import com.google.api.client.http.UrlEncodedContent; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Logger; +import org.slf4j.event.Level; + +public class LoggingInterceptor + implements HttpExecuteInterceptor, HttpRequestInitializer, HttpResponseInterceptor { + + private static final Logger logger = LoggingUtils.getLogger(LoggingInterceptor.class); + + @Override + public void intercept(HttpRequest request) throws IOException { + // Log the request + // StringBuilder requestLog = new StringBuilder("Sending request."); + Map loggingDataMap = new HashMap<>(); + loggingDataMap.put("request.method", request.getRequestMethod()); + loggingDataMap.put("request.url", request.getUrl().toString()); + // requestLog.append(request.getRequestMethod()).append(" ").append(request.getUrl()); + + Map headers = new HashMap<>(); + request.getHeaders().forEach((key, val) -> headers.put(key, val)); + loggingDataMap.put("request.headers", headers.toString()); + if (request.getContent() != null && logger.isDebugEnabled()) { + loggingDataMap.put( + "request.payload", ((UrlEncodedContent) request.getContent()).getData().toString()); + + LoggingUtils.logWithMDC(logger, Level.DEBUG, loggingDataMap, "Sending auth request"); + } else { + + LoggingUtils.logWithMDC(logger, Level.INFO, loggingDataMap, "Sending auth request"); + } + } + + @Override + public void interceptResponse(HttpResponse response) throws IOException { + // Log the response + // StringBuilder responseLog = new StringBuilder("Received response: "); + // responseLog.append(response.getStatusCode()).append(" ").append(response.getStatusMessage()); + + Map responseLogDataMap = new HashMap<>(); + responseLogDataMap.put("response.status", String.valueOf(response.getStatusCode())); + responseLogDataMap.put("response.status.message", response.getStatusMessage()); + + Map headers = new HashMap<>(); + response.getHeaders().forEach((key, val) -> headers.put(key, val)); + responseLogDataMap.put("response.headers", headers.toString()); + + LoggingUtils.logWithMDC(logger, Level.INFO, responseLogDataMap, "Auth response."); + } + + @Override + public void initialize(HttpRequest request) throws IOException { + request.setInterceptor(this); + request.setResponseInterceptor(this); + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/LoggingUtils.java b/oauth2_http/java/com/google/auth/oauth2/LoggingUtils.java new file mode 100644 index 000000000..8008e28f4 --- /dev/null +++ b/oauth2_http/java/com/google/auth/oauth2/LoggingUtils.java @@ -0,0 +1,572 @@ +package com.google.auth.oauth2; + +import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponse; +import com.google.api.client.http.UrlEncodedContent; +import com.google.api.client.util.GenericData; +import com.google.gson.JsonObject; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import org.slf4j.ILoggerFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.helpers.FormattingTuple; +import org.slf4j.helpers.MessageFormatter; + +public class LoggingUtils { + + private static final java.util.logging.Logger LOGGER = + java.util.logging.Logger.getLogger(LoggingUtils.class.getName()); + private static EnvironmentProvider environmentProvider = SystemEnvironmentProvider.getInstance(); + + static void setEnvironmentProvider(EnvironmentProvider provider) { + environmentProvider = provider; + } + + private LoggingUtils() {} + + interface LoggerFactoryProvider { + ILoggerFactory getLoggerFactory(); + } + + static class DefaultLoggerFactoryProvider implements LoggerFactoryProvider { + @Override + public ILoggerFactory getLoggerFactory() { + return LoggerFactory.getILoggerFactory(); + } + } + + public static Logger getLogger(Class clazz) { + return getLogger(clazz, new DefaultLoggerFactoryProvider()); + } + + public static Logger getLogger(Class clazz, LoggerFactoryProvider factoryProvider) { + if (!isLoggingEnabled()) { + // use SLF4j's NOP logger regardless of bindings + return org.slf4j.helpers.NOPLogger.NOP_LOGGER; + } + + ILoggerFactory loggerFactory = factoryProvider.getLoggerFactory(); + if (loggerFactory != null && !(loggerFactory instanceof org.slf4j.helpers.NOPLoggerFactory)) { + // Use SLF4j binding when present + return LoggerFactory.getLogger(clazz); + } + // No SLF4j binding found, use JUL as fallback + Logger logger = new JulWrapperLogger(clazz.getName()); + logger.info("No SLF4J providers were found, fall back to JUL."); + return logger; + } + + public static boolean isLoggingEnabled() { + String enableLogging = environmentProvider.getEnv("GOOGLE_SDK_JAVA_LOGGING"); + // String enableLogging = System.getenv("GOOGLE_SDK_JAVA_LOGGING"); + LOGGER.info("GOOGLE_SDK_JAVA_LOGGING=" + enableLogging); // log for debug now, remove it. + return "true".equalsIgnoreCase(enableLogging); + } + + public static JsonObject mergeJsonObject(JsonObject jsonObject1, JsonObject jsonObject2) { + JsonObject mergedObject = jsonObject1.deepCopy(); + jsonObject2.entrySet().forEach(entry -> mergedObject.add(entry.getKey(), entry.getValue())); + return mergedObject; + } + + public static Level mapToJulLevel(org.slf4j.event.Level slf4jLevel) { + switch (slf4jLevel) { + case ERROR: + return Level.SEVERE; + case WARN: + return Level.WARNING; + case INFO: + return Level.INFO; + case DEBUG: + return Level.FINE; + case TRACE: + return Level.FINEST; + default: + return Level.INFO; + } + } + + public static void logWithMDC( + Logger logger, org.slf4j.event.Level level, Map contextMap, String message) { + + if (logger instanceof JulWrapperLogger) { + // Simulate MDC behavior for JUL + LogRecord record = new LogRecord(mapToJulLevel(level), message); + // Add context map to the LogRecord + record.setParameters(new Object[] {contextMap}); + ((JulWrapperLogger) logger).getJulLogger().log(record); + return; + } + contextMap.forEach(MDC::put); + + switch (level) { + case TRACE: + logger.trace(message); + break; + case DEBUG: + logger.debug(message); + break; + case INFO: + logger.info(message); + break; + case WARN: + logger.warn(message); + break; + case ERROR: + logger.error(message); + break; + default: + logger.info(message); + // Default to INFO level + } + + MDC.clear(); + } + + static void logRequest(HttpRequest request, Logger logger, String message) { + if (logger.isInfoEnabled()) { + Map loggingDataMap = new HashMap<>(); + loggingDataMap.put("request.method", request.getRequestMethod()); + loggingDataMap.put("request.url", request.getUrl().toString()); + + Map headers = new HashMap<>(); + request + .getHeaders() + .forEach( + (key, val) -> { + if ("authorization".equals(key)) { + + String tokenString = String.valueOf(val); + + String maskedToken = + tokenString.substring(0, 5) + + "*****" + + tokenString.substring(tokenString.length() - 4); + // String maskedToken = calculateSHA256Hash(tokenString); + headers.put(key, String.valueOf(maskedToken)); + } else { + headers.put(key, val); + } + }); + loggingDataMap.put("request.headers", headers.toString()); + + if (request.getContent() != null && logger.isDebugEnabled()) { + // are payload always GenericData? If so, can parse and store in json + GenericData data = (GenericData) ((UrlEncodedContent) request.getContent()).getData(); + + Map contextMap = parseGenericData(data); + loggingDataMap.put("request.payload", contextMap.toString()); + + LoggingUtils.logWithMDC(logger, org.slf4j.event.Level.DEBUG, loggingDataMap, message); + } else { + + LoggingUtils.logWithMDC(logger, org.slf4j.event.Level.INFO, loggingDataMap, message); + } + } + } + + static void logResponse(HttpResponse response, Logger logger, String message) { + if (logger.isInfoEnabled()) { + Map responseLogDataMap = new HashMap<>(); + responseLogDataMap.put("response.status", String.valueOf(response.getStatusCode())); + responseLogDataMap.put("response.status.message", response.getStatusMessage()); + + Map headers = new HashMap<>(); + response.getHeaders().forEach((key, val) -> headers.put(key, val)); + responseLogDataMap.put("response.headers", headers.toString()); + LoggingUtils.logWithMDC(logger, org.slf4j.event.Level.INFO, responseLogDataMap, message); + } + } + + static void logGenericData(GenericData genericData, Logger logger, String message) { + if (logger.isDebugEnabled()) { + Map contextMap = parseGenericData(genericData); + LoggingUtils.logWithMDC(logger, org.slf4j.event.Level.DEBUG, contextMap, message); + } + } + + private static Map parseGenericData(GenericData genericData) { + Map contextMap = new HashMap<>(); + genericData.forEach( + (key, val) -> { + if ("token".equals(key) + || "assertion".equals(key) + || "access_token".equals(key) + || "client_secret".equals(key) + || "refresh_token".equals(key)) { + String tokenString = String.valueOf(val); + // String maskedToken = calculateSHA256Hash(tokenString); + String maskedToken = + tokenString.substring(0, 5) + + "*****" + + tokenString.substring(tokenString.length() - 4); + contextMap.put(key, String.valueOf(maskedToken)); + } else { + contextMap.put(key, val.toString()); + } + }); + return contextMap; + } + + private static String calculateSHA256Hash(String data) { + try { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + byte[] inputBytes = data.getBytes(StandardCharsets.UTF_8); + byte[] hashBytes = digest.digest(inputBytes); + return bytesToHex(hashBytes); + } catch (NoSuchAlgorithmException e) { + System.err.println("Error calculating SHA-256 hash: " + e.getMessage()); + return ""; // Or throw an exception, depending on your error handling strategy + } + } + + private static String bytesToHex(byte[] hash) { + StringBuilder hexString = new StringBuilder(2 * hash.length); + for (byte b : hash) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + return hexString.toString(); + } + + // JulWrapperLogger implementation + static class JulWrapperLogger implements Logger { + + private final java.util.logging.Logger julLogger; + + public JulWrapperLogger(String name) { + this.julLogger = java.util.logging.Logger.getLogger(name); + } + + public java.util.logging.Logger getJulLogger() { + return julLogger; + } + + @Override + public String getName() { + return julLogger.getName(); + } + + @Override + public boolean isTraceEnabled() { + return julLogger.isLoggable(java.util.logging.Level.FINEST); + } + + @Override + public void trace(String msg) { + julLogger.log(java.util.logging.Level.FINEST, msg); + } + + @Override + public void trace(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isTraceEnabled(Marker marker) { + return false; + } + + @Override + public void trace(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void trace(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isDebugEnabled() { + return julLogger.isLoggable(Level.FINE); + } + + @Override + public void debug(String msg) { + + if (isDebugEnabled()) { + julLogger.log(java.util.logging.Level.FINE, msg); + } + } + + @Override + public void debug(String format, Object arg) { + if (isDebugEnabled()) { + FormattingTuple ft = MessageFormatter.format(format, arg); + julLogger.log(Level.FINE, ft.getMessage()); + } + } + + @Override + public void debug(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isDebugEnabled(Marker marker) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void debug(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isInfoEnabled() { + return julLogger.isLoggable(Level.INFO); + } + + @Override + public void info(String msg) { + if (isInfoEnabled()) { + julLogger.log(java.util.logging.Level.INFO, msg); + } + } + + @Override + public void info(String format, Object arg) { + if (isInfoEnabled()) { + FormattingTuple ft = MessageFormatter.format(format, arg); + julLogger.log(java.util.logging.Level.INFO, ft.getMessage()); + } + } + + @Override + public void info(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isInfoEnabled(Marker marker) { + return true; + } + + @Override + public void info(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void info(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isWarnEnabled() { + return true; + } + + @Override + public void warn(String msg) { + julLogger.log(Level.WARNING, msg); + } + + @Override + public void warn(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isWarnEnabled(Marker marker) { + return false; + } + + @Override + public void warn(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void warn(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public void error(String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public boolean isErrorEnabled(Marker marker) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(Marker marker, String s) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(Marker marker, String s, Object o) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(Marker marker, String s, Object o, Object o1) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(Marker marker, String s, Object... objects) { + throw new UnsupportedOperationException("This method is not supported."); + } + + @Override + public void error(Marker marker, String s, Throwable throwable) { + throw new UnsupportedOperationException("This method is not supported."); + } + } +} diff --git a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java index d998ce671..89e8c9c1d 100644 --- a/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java @@ -64,6 +64,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.gson.Gson; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; @@ -82,6 +83,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.Executor; +import org.slf4j.Logger; /** * OAuth2 credentials representing a Service Account for calling Google APIs. @@ -96,6 +98,8 @@ public class ServiceAccountCredentials extends GoogleCredentials private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; private static final int TWELVE_HOURS_IN_SECONDS = 43200; private static final int DEFAULT_LIFETIME_IN_SECONDS = 3600; + private static final Logger LOGGER = LoggingUtils.getLogger(ServiceAccountCredentials.class); + private static final Gson gson = new Gson(); private final String clientId; private final String clientEmail; @@ -489,6 +493,11 @@ boolean isConfiguredForDomainWideDelegation() { return serviceAccountUser != null && serviceAccountUser.length() > 0; } + private GenericData parseResponseAs(HttpResponse response) throws IOException { + GenericData genericData = response.parseAs(GenericData.class); + LoggingUtils.logGenericData(genericData, LOGGER, "Auth response payload."); + return genericData; + } /** * Refreshes the OAuth2 access token by getting a new access token using a JSON Web Token (JWT). */ @@ -517,6 +526,7 @@ public AccessToken refreshAccessToken() throws IOException { } request.setParser(new JsonObjectParser(jsonFactory)); + LoggingUtils.logRequest(request, LOGGER, "Sending auth request to refresh access token."); ExponentialBackOff backoff = new ExponentialBackOff.Builder() .setInitialIntervalMillis(OAuth2Utils.INITIAL_RETRY_INTERVAL_MILLIS) @@ -539,6 +549,7 @@ public AccessToken refreshAccessToken() throws IOException { try { response = request.execute(); + LoggingUtils.logResponse(response, LOGGER, "Auth response received."); } catch (HttpResponseException re) { String message = String.format(errorTemplate, re.getMessage(), getIssuer()); throw GoogleAuthException.createWithTokenEndpointResponseException(re, message); @@ -547,7 +558,7 @@ public AccessToken refreshAccessToken() throws IOException { e, String.format(errorTemplate, e.getMessage(), getIssuer())); } - GenericData responseData = response.parseAs(GenericData.class); + GenericData responseData = parseResponseAs(response); String accessToken = OAuth2Utils.validateString(responseData, "access_token", PARSE_ERROR_PREFIX); int expiresInSeconds = @@ -597,9 +608,10 @@ private IdToken getIdTokenOauthEndpoint(String targetAudience) throws IOExceptio MetricsUtils.getGoogleCredentialsMetricsHeader( RequestType.ID_TOKEN_REQUEST, getMetricsCredentialType())); + LoggingUtils.logRequest(request, LOGGER, "Get IdToken via Oauth Endpoint."); HttpResponse httpResponse = executeRequest(request); - GenericData responseData = httpResponse.parseAs(GenericData.class); + GenericData responseData = parseResponseAs(httpResponse); String rawToken = OAuth2Utils.validateString(responseData, "id_token", PARSE_ERROR_PREFIX); return IdToken.create(rawToken); } @@ -641,9 +653,11 @@ private IdToken getIdTokenIamEndpoint(String targetAudience) throws IOException HttpRequest request = buildIdTokenRequest(iamIdTokenUri, transportFactory, content); // Use the Access Token from the SSJWT to request the ID Token from IAM Endpoint request.setHeaders(new HttpHeaders().set(AuthHttpConstants.AUTHORIZATION, accessToken)); + + LoggingUtils.logRequest(request, LOGGER, "Sending id token request to Iam Endpoint."); HttpResponse httpResponse = executeRequest(request); - GenericData responseData = httpResponse.parseAs(GenericData.class); + GenericData responseData = parseResponseAs(httpResponse); // IAM Endpoint returns `token` instead of `id_token` String rawToken = OAuth2Utils.validateString(responseData, "token", PARSE_ERROR_PREFIX); return IdToken.create(rawToken); diff --git a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java index a2ba5a52d..3f062369b 100644 --- a/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java +++ b/oauth2_http/java/com/google/auth/oauth2/UserCredentials.java @@ -50,6 +50,7 @@ import com.google.auth.oauth2.MetricsUtils.RequestType; import com.google.common.base.MoreObjects; import com.google.errorprone.annotations.CanIgnoreReturnValue; +import com.google.gson.Gson; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; @@ -61,6 +62,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import org.slf4j.Logger; /** OAuth2 Credentials representing a user's identity and consent. */ public class UserCredentials extends GoogleCredentials implements IdTokenProvider { @@ -68,6 +70,8 @@ public class UserCredentials extends GoogleCredentials implements IdTokenProvide private static final String GRANT_TYPE = "refresh_token"; private static final String PARSE_ERROR_PREFIX = "Error parsing token refresh response. "; private static final long serialVersionUID = -4800758775038679176L; + private static final Logger LOGGER = LoggingUtils.getLogger(UserCredentials.class); + private static final Gson gson = new Gson(); private final String clientId; private final String clientSecret; @@ -191,6 +195,13 @@ public AccessToken refreshAccessToken() throws IOException { String scopes = OAuth2Utils.validateOptionalString( responseData, OAuth2Utils.TOKEN_RESPONSE_SCOPE, PARSE_ERROR_PREFIX); + // if (LOGGER.isDebugEnabled()) { + // Map responseContentMap = new HashMap<>(); + // responseContentMap.put("access_token", accessToken); + // responseContentMap.put("expires_in", String.valueOf(expiresInSeconds)); + // responseContentMap.put(OAuth2Utils.TOKEN_RESPONSE_SCOPE, scopes); + // LoggingUtils.logWithMDC(LOGGER, Level.DEBUG, responseContentMap, "response payload."); + // } return AccessToken.newBuilder() .setExpirationTime(new Date(expiresAtMilliseconds)) .setTokenValue(accessToken) @@ -215,6 +226,11 @@ public IdToken idTokenWithAudience(String targetAudience, List