fix(credentials): authorize shared credentials without requiring a wo… · simstudioai/sim@3595fa2 · GitHub
Skip to content

Commit 3595fa2

Browse files
authored
fix(credentials): authorize shared credentials without requiring a workflow (#6571)
`authorizeCredentialUse` could only reach its member-based sharing branch when a `workflowId` was supplied, so non-workflow surfaces — knowledge base connectors, credential management — fell through to an owner-only path and rejected everyone but the user who ran the OAuth flow. A workflow now only pins which workspace a legacy account id resolves through; it never grants access on its own. Access itself is decided by one rule everywhere: active credential member, or derived credential admin. - resolve legacy account ids through whichever workspace credential rows the caller can reach, instead of an owner-only fallback - extract `canUseCredential` and replace the predicate hand-inlined at five sites - reuse `resolveCredentialTokenIdentity` for owner resolution instead of a second local copy of the same invariant - keep the workflow-pinned path from crossing a workspace boundary
1 parent be5db68 commit 3595fa2

9 files changed

Lines changed: 381 additions & 190 deletions

File tree

‎apps/sim/app/api/auth/oauth/credentials/route.ts‎

Lines changed: 3 additions & 3 deletions

‎apps/sim/app/api/credentials/[id]/route.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ import { updateWorkspaceCredentialContract } from '@/lib/api/contracts/credentia
44
import { getValidationErrorMessage, parseRequest } from '@/lib/api/server'
55
import { getSession } from '@/lib/auth'
66
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
7-
import { type CredentialActorContext, getCredentialActorContext } from '@/lib/credentials/access'
7+
import {
8+
type CredentialActorContext,
9+
canUseCredential,
10+
getCredentialActorContext,
11+
} from '@/lib/credentials/access'
812
import {
913
isProviderOutageCode,
1014
performDeleteCredential,
@@ -49,7 +53,7 @@ export const GET = withRouteHandler(
4953
if (!access.credential) {
5054
return NextResponse.json({ error: 'Credential not found' }, { status: 404 })
5155
}
52-
if (!access.hasWorkspaceAccess || (!access.member && !access.isAdmin)) {
56+
if (!canUseCredential(access)) {
5357
return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
5458
}
5559

‎apps/sim/executor/handlers/evaluator/evaluator-handler.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const { mockResolveAutoModel } = vi.hoisted(() => ({
1111
vi.mock('@/lib/oauth/credential-service', () => authOAuthUtilsMock)
1212

1313
vi.mock('@/lib/credentials/access', () => ({
14+
canUseCredential: (access: { hasWorkspaceAccess: boolean; member: unknown; isAdmin: boolean }) =>
15+
access.hasWorkspaceAccess && (Boolean(access.member) || access.isAdmin),
1416
getCredentialActorContext: vi.fn().mockResolvedValue({
1517
credential: {
1618
id: 'test-vertex-credential-id',

‎apps/sim/executor/handlers/router/router-handler.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ vi.mock('@/lib/oauth/credential-service', () => authOAuthUtilsMock)
1717
vi.mock('@/lib/core/security/encryption', () => encryptionMock)
1818

1919
vi.mock('@/lib/credentials/access', () => ({
20+
canUseCredential: (access: { hasWorkspaceAccess: boolean; member: unknown; isAdmin: boolean }) =>
21+
access.hasWorkspaceAccess && (Boolean(access.member) || access.isAdmin),
2022
getCredentialActorContext: vi.fn().mockResolvedValue({
2123
credential: {
2224
id: 'test-vertex-credential',

‎apps/sim/executor/utils/vertex-credential.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const { mockGetCredentialActorContext, mockGetServiceAccountToken, mockRefreshTo
1212

1313
vi.mock('@/lib/credentials/access', () => ({
1414
getCredentialActorContext: mockGetCredentialActorContext,
15+
canUseCredential: (access: { hasWorkspaceAccess: boolean; member: unknown; isAdmin: boolean }) =>
16+
access.hasWorkspaceAccess && (Boolean(access.member) || access.isAdmin),
1517
}))
1618
vi.mock('@/lib/oauth/credential-service', () => ({
1719
getServiceAccountToken: mockGetServiceAccountToken,

‎apps/sim/executor/utils/vertex-credential.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { db } from '@sim/db'
22
import { account } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { eq } from 'drizzle-orm'
5-
import { getCredentialActorContext } from '@/lib/credentials/access'
5+
import { canUseCredential, getCredentialActorContext } from '@/lib/credentials/access'
66
import { getServiceAccountToken, refreshTokenIfNeeded } from '@/lib/oauth/credential-service'
77

88
const logger = createLogger('VertexCredential')
@@ -48,7 +48,7 @@ export async function resolveVertexCredential({
4848
})
4949
throw new Error('Credential is not accessible from this workflow workspace')
5050
}
51-
if (!access.hasWorkspaceAccess || (!access.member && !access.isAdmin)) {
51+
if (!canUseCredential(access)) {
5252
throw new Error('Not authorized to use this Vertex AI credential')
5353
}
5454

Lines changed: 238 additions & 0 deletions

0 commit comments

Comments
 (0)