Skip to content

Conversation

@scezen
Copy link
Member

@scezen scezen commented Nov 19, 2025

Ticket :
DERCBOT-1691

Summary
Adds optional GSA impersonation to the Google Chat connector.
If gsaToImpersonate is set, the connector uses ImpersonatedCredentials with source creds (JSON or ADC).
If not set, behavior remains unchanged.

@scezen scezen marked this pull request as ready for review November 19, 2025 17:18
@vsct-jburet vsct-jburet requested a review from Copilot November 21, 2025 16:41
Copilot finished reviewing on behalf of vsct-jburet November 21, 2025 16:45
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Google Service Account (GSA) impersonation support to the Google Chat connector, allowing more flexible authentication patterns in Google Cloud environments. When gsaToImpersonate is configured, the connector uses ImpersonatedCredentials with either explicit JSON credentials or Application Default Credentials (ADC) as the source. When not set, the connector maintains backward compatibility with the existing direct authentication approach.

Key changes:

  • Added optional GSA impersonation with automatic fallback to ADC when explicit credentials are unavailable
  • Enhanced logging throughout the authentication flow and message sending process for better observability
  • Updated documentation to explain the new impersonation feature and required IAM permissions

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.

File Description
bot/connector-google-chat/src/main/kotlin/GoogleChatConnectorProvider.kt Core implementation of GSA impersonation logic, including credential loading, scope management, and configuration field addition
bot/connector-google-chat/src/main/kotlin/GoogleChatConnector.kt Enhanced error handling and logging for message sending operations
bot/connector-google-chat/README.md Documentation updates explaining impersonation setup, permissions, and configuration examples

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


val sourceCredentials = getSourceCredentials(connectorConfiguration)

logger.info { "Source credentials: ${(sourceCredentials as? ServiceAccountCredentials)?.clientEmail}" }
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the source service account email could expose sensitive configuration information. Consider removing this log statement or redacting the email address.

Suggested change
logger.info { "Source credentials: ${(sourceCredentials as? ServiceAccountCredentials)?.clientEmail}" }

Copilot uses AI. Check for mistakes.
val sourceCredentials = getSourceCredentials(connectorConfiguration)

logger.info { "Source credentials: ${(sourceCredentials as? ServiceAccountCredentials)?.clientEmail}" }
logger.info { "Impersonating target GSA = $targetServiceAccount with scopes = $CHAT_SCOPE" }
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the target service account email could expose sensitive configuration information. Consider removing this log statement or redacting the email address.

Suggested change
logger.info { "Impersonating target GSA = $targetServiceAccount with scopes = $CHAT_SCOPE" }
logger.info { "Impersonating target GSA with scopes = $CHAT_SCOPE" }

Copilot uses AI. Check for mistakes.
val creds = ServiceAccountCredentials.fromStream(credentialInputStream)
.createScoped("https://www.googleapis.com/auth/cloud-platform")

logger.info { "Loaded explicit service account: ${(creds as ServiceAccountCredentials).clientEmail}" }
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the service account email could expose sensitive configuration information. Consider removing this log statement or redacting the email address.

Suggested change
logger.info { "Loaded explicit service account: ${(creds as ServiceAccountCredentials).clientEmail}" }

Copilot uses AI. Check for mistakes.
Comment on lines +132 to +136
} catch (e: Exception) {
logger.info { "No explicit credentials found, using Application Default Credentials" }
GoogleCredentials.getApplicationDefault()
.createScoped("https://www.googleapis.com/auth/cloud-platform")
}
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch block handles all exceptions generically with catch (e: Exception), which might hide configuration errors (e.g., invalid JSON format, missing files). Consider catching only specific exceptions related to missing credentials (e.g., FileNotFoundException, IOException) and let other exceptions propagate with more context. This will help distinguish between "credentials not provided" vs "credentials provided but invalid".

Copilot uses AI. Check for mistakes.
targetServiceAccount,
null,
listOf(CHAT_SCOPE),
3600,
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded lifetime value of 3600 seconds (1 hour) should be extracted to a named constant for better maintainability. Consider adding private const val IMPERSONATION_TOKEN_LIFETIME_SECONDS = 3600 at the top of the file alongside other constants.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also use a property to make it configurable

.execute()
try {
logger.info {
"Sending to Google Chat: space=${callback.spaceName}, thread=${callback.threadName}, message=${message.toGoogleMessage()}"
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the complete message content with message=${message.toGoogleMessage()} could inadvertently log sensitive user data or PII. Consider logging only metadata (space, thread) or use a sanitized representation of the message.

Suggested change
"Sending to Google Chat: space=${callback.spaceName}, thread=${callback.threadName}, message=${message.toGoogleMessage()}"
"Sending to Google Chat: space=${callback.spaceName}, thread=${callback.threadName}, messageType=${message::class.simpleName}"

Copilot uses AI. Check for mistakes.
val credentialInputStream = getCredentialInputStream(connectorConfiguration)
loadCredentials(credentialInputStream)
} else {
logger.info { "Using impersonation mode with GSA: $gsaToImpersonate" }
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging the service account email to impersonate could expose sensitive configuration information. Consider using a more generic log message or redacting part of the email (e.g., showing only the domain). For example: "Using impersonation mode with GSA: ${gsaToImpersonate.substringAfter('@')}" or just "Using impersonation mode".

Suggested change
logger.info { "Using impersonation mode with GSA: $gsaToImpersonate" }
logger.info { "Using impersonation mode with GSA domain: ${gsaToImpersonate.substringAfter('@')}" }

Copilot uses AI. Check for mistakes.
true
),
ConnectorTypeConfigurationField(
"Service account email to impersonate (if provided, priority over JSON credentials)",
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description "Service account email to impersonate (if provided, priority over JSON credentials)" is misleading. Impersonation doesn't replace JSON credentials; it uses them (or ADC) as the source for impersonation. Consider revising to: "Service account email to impersonate (optional, uses JSON credentials or ADC as source)" to clarify that both can work together.

Suggested change
"Service account email to impersonate (if provided, priority over JSON credentials)",
"Service account email to impersonate (optional, uses JSON credentials or ADC as source)",

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants