Skip to content

Commit 2c60aa2

Browse files
committed
fix(credentials): offer keyless Google delegation only for services that can be delegated
1 parent 9bf50c9 commit 2c60aa2

2 files changed

Lines changed: 54 additions & 26 deletions

File tree

‎apps/sim/lib/credentials/google-workload-identity.server.test.ts‎

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -415,27 +415,41 @@ describe('getting a keyless Google Cloud token for a run', () => {
415415
expect(google.iam[0]?.body).toEqual({ scope: [BIGQUERY], lifetime: '3600s' })
416416
})
417417

418-
it('refuses domain-wide delegation before contacting Google', async () => {
419-
fakeGoogle(fetch)
418+
it.each([
419+
['google-drive', 'google_keyless_delegation'],
420+
['google-bigquery', 'google_keyless_delegation_unavailable'],
421+
['google-ads', 'google_keyless_delegation_unavailable'],
422+
])(
423+
'refuses impersonation for %s before contacting Google, offering delegation only where it exists',
424+
async (service, step) => {
425+
fakeGoogle(fetch)
420426

421-
await expect(
422-
resolve({ credentialId: 'cred-dwd', impersonateEmail: 'alice@example.com' })
423-
).rejects.toMatchObject({
424-
code: 'unsupported_use',
425-
logDetail: { step: 'google_keyless_delegation' },
426-
userMessage: expect.any(String),
427-
})
428-
expect(fetch).not.toHaveBeenCalled()
429-
})
427+
await expect(
428+
resolve({ credentialId: 'cred-dwd', service, impersonateEmail: 'alice@example.com' })
429+
).rejects.toMatchObject({
430+
code: 'unsupported_use',
431+
logDetail: { step },
432+
userMessage: expect.any(String),
433+
})
434+
expect(fetch).not.toHaveBeenCalled()
435+
}
436+
)
430437

431-
it.each(['google-drive', 'gmail', 'google-sheets', 'google-ads', undefined])(
432-
'refuses %s, which keyless credentials do not serve, before contacting Google',
433-
async (service) => {
438+
it.each([
439+
['google-drive', 'google_keyless_service'],
440+
['gmail', 'google_keyless_service'],
441+
['google-sheets', 'google_keyless_service'],
442+
['google-email', 'google_keyless_service'],
443+
['google-ads', 'google_keyless_service_unsupported'],
444+
[undefined, 'google_keyless_service_unsupported'],
445+
])(
446+
'refuses %s, which keyless credentials do not serve, offering delegation only where it exists',
447+
async (service, step) => {
434448
fakeGoogle(fetch)
435449

436450
await expect(resolve({ credentialId: 'cred-workspace', service })).rejects.toMatchObject({
437451
code: 'unsupported_use',
438-
logDetail: { step: 'google_keyless_service' },
452+
logDetail: { step },
439453
userMessage: expect.any(String),
440454
})
441455
expect(fetch).not.toHaveBeenCalled()

‎apps/sim/lib/credentials/google-workload-identity.server.ts‎

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -930,7 +930,9 @@ export async function resolveGoogleWorkloadIdentityToken(
930930
input: ResolveGoogleWorkloadIdentityTokenInput
931931
): Promise<string> {
932932
const config = readStoredConfig(input.decrypted)
933-
const delegation = googleDelegationServiceFor(input.service) ? config.delegation : undefined
933+
// Reconnecting can add delegation only for the Workspace services keyless delegation covers.
934+
const delegable = googleDelegationServiceFor(input.service) !== undefined
935+
const delegation = delegable ? config.delegation : undefined
934936

935937
if (delegation) {
936938
if (input.impersonateEmail && input.impersonateEmail.trim().toLowerCase() !== delegation.user) {
@@ -942,18 +944,30 @@ export async function resolveGoogleWorkloadIdentityToken(
942944
}
943945
} else {
944946
if (input.impersonateEmail) {
945-
throw unsupportedUse(
946-
'google_keyless_delegation',
947-
'this keyless Google credential has no domain-wide delegation for this service',
948-
'This keyless Google credential has no domain-wide delegation for this service, so it cannot act as a Workspace user. Clear the Impersonated Account, reconnect the credential to delegate this service, or use a JSON key credential instead'
949-
)
947+
throw delegable
948+
? unsupportedUse(
949+
'google_keyless_delegation',
950+
'this keyless Google credential has no domain-wide delegation for this service',
951+
'This keyless Google credential has no domain-wide delegation for this service, so it cannot act as a Workspace user. Clear the Impersonated Account, reconnect the credential to delegate this service, or use a JSON key credential instead'
952+
)
953+
: unsupportedUse(
954+
'google_keyless_delegation_unavailable',
955+
'keyless Google credentials cannot act as a Workspace user for this service',
956+
'A keyless Google credential cannot act as a Workspace user for this service. Clear the Impersonated Account, or use a JSON key credential instead'
957+
)
950958
}
951959
if (!input.service || !GOOGLE_KEYLESS_SERVICES.has(input.service)) {
952-
throw unsupportedUse(
953-
'google_keyless_service',
954-
'this keyless Google credential works only with Google Cloud APIs and the Workspace services it was delegated',
955-
'This keyless Google credential works only with Google Cloud tools such as BigQuery and, through domain-wide delegation, with Gmail, Google Drive, Google Docs, Google Sheets, and Google Calendar. Reconnect it to delegate this service, or use a JSON key credential instead'
956-
)
960+
throw delegable
961+
? unsupportedUse(
962+
'google_keyless_service',
963+
'this keyless Google credential works only with Google Cloud APIs and the Workspace services it was delegated',
964+
'This keyless Google credential works only with Google Cloud tools such as BigQuery and, through domain-wide delegation, with Gmail, Google Drive, Google Docs, Google Sheets, and Google Calendar. Reconnect it to delegate this service, or use a JSON key credential instead'
965+
)
966+
: unsupportedUse(
967+
'google_keyless_service_unsupported',
968+
'keyless Google credentials cannot serve this service',
969+
'Keyless Google credentials work only with Google Cloud tools such as BigQuery and, through domain-wide delegation, with Gmail, Google Drive, Google Docs, Google Sheets, and Google Calendar. Use a JSON key credential for this service'
970+
)
957971
}
958972
}
959973
if (input.scopes.length === 0) {

0 commit comments

Comments
 (0)