Skip to content

Commit d235d74

Browse files
committed
more permissions stuff
1 parent 3769da8 commit d235d74

File tree

4 files changed

+94
-8
lines changed

4 files changed

+94
-8
lines changed

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,18 @@ interface RouteContext {
1313
params: Promise<{ id: string }>
1414
}
1515

16-
async function requireAdminMembership(credentialId: string, userId: string) {
16+
async function requireWorkspaceAdminMembership(credentialId: string, userId: string) {
17+
const [cred] = await db
18+
.select({ id: credential.id, workspaceId: credential.workspaceId })
19+
.from(credential)
20+
.where(eq(credential.id, credentialId))
21+
.limit(1)
22+
23+
if (!cred) return null
24+
25+
const perm = await getUserEntityPermissions(userId, 'workspace', cred.workspaceId)
26+
if (perm === null) return null
27+
1728
const [membership] = await db
1829
.select({ role: credentialMember.role, status: credentialMember.status })
1930
.from(credentialMember)
@@ -91,7 +102,7 @@ export async function POST(request: NextRequest, context: RouteContext) {
91102

92103
const { id: credentialId } = await context.params
93104

94-
const admin = await requireAdminMembership(credentialId, session.user.id)
105+
const admin = await requireWorkspaceAdminMembership(credentialId, session.user.id)
95106
if (!admin) {
96107
return NextResponse.json({ error: 'Admin access required' }, { status: 403 })
97108
}
@@ -153,7 +164,7 @@ export async function DELETE(request: NextRequest, context: RouteContext) {
153164
return NextResponse.json({ error: 'userId query parameter required' }, { status: 400 })
154165
}
155166

156-
const admin = await requireAdminMembership(credentialId, session.user.id)
167+
const admin = await requireWorkspaceAdminMembership(credentialId, session.user.id)
157168
if (!admin) {
158169
return NextResponse.json({ error: 'Admin access required' }, { status: 403 })
159170
}

apps/sim/app/api/credentials/draft/route.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { db } from '@sim/db'
2-
import { pendingCredentialDraft } from '@sim/db/schema'
2+
import { credential, credentialMember, pendingCredentialDraft } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { and, eq, lt } from 'drizzle-orm'
55
import { NextResponse } from 'next/server'
66
import { z } from 'zod'
77
import { getSession } from '@/lib/auth'
8+
import { checkWorkspaceAccess } from '@/lib/workspaces/permissions/utils'
89

910
const logger = createLogger('CredentialDraftAPI')
1011

@@ -33,6 +34,36 @@ export async function POST(request: Request) {
3334

3435
const { workspaceId, providerId, displayName, description, credentialId } = parsed.data
3536
const userId = session.user.id
37+
38+
const workspaceAccess = await checkWorkspaceAccess(workspaceId, userId)
39+
if (!workspaceAccess.canWrite) {
40+
return NextResponse.json({ error: 'Write permission required' }, { status: 403 })
41+
}
42+
43+
if (credentialId) {
44+
const [membership] = await db
45+
.select({ role: credentialMember.role, status: credentialMember.status })
46+
.from(credentialMember)
47+
.innerJoin(credential, eq(credential.id, credentialMember.credentialId))
48+
.where(
49+
and(
50+
eq(credentialMember.credentialId, credentialId),
51+
eq(credentialMember.userId, userId),
52+
eq(credentialMember.status, 'active'),
53+
eq(credentialMember.role, 'admin'),
54+
eq(credential.workspaceId, workspaceId)
55+
)
56+
)
57+
.limit(1)
58+
59+
if (!membership) {
60+
return NextResponse.json(
61+
{ error: 'Admin access required on the target credential' },
62+
{ status: 403 }
63+
)
64+
}
65+
}
66+
3667
const now = new Date()
3768

3869
await db

apps/sim/app/api/v1/admin/workspaces/[id]/members/[memberId]/route.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
*/
2323

2424
import { db } from '@sim/db'
25-
import { permissions, user, workspace } from '@sim/db/schema'
25+
import { credential, credentialMember, permissions, user, workspace } from '@sim/db/schema'
2626
import { createLogger } from '@sim/logger'
27-
import { and, eq } from 'drizzle-orm'
27+
import { and, eq, inArray } from 'drizzle-orm'
2828
import { withAdminAuthParams } from '@/app/api/v1/admin/middleware'
2929
import {
3030
badRequestResponse,
@@ -215,6 +215,28 @@ export const DELETE = withAdminAuthParams<RouteParams>(async (_, context) => {
215215

216216
await db.delete(permissions).where(eq(permissions.id, memberId))
217217

218+
// Revoke credential memberships for all credentials in this workspace
219+
const workspaceCredentialIds = await db
220+
.select({ id: credential.id })
221+
.from(credential)
222+
.where(eq(credential.workspaceId, workspaceId))
223+
224+
if (workspaceCredentialIds.length > 0) {
225+
await db
226+
.update(credentialMember)
227+
.set({ status: 'revoked', updatedAt: new Date() })
228+
.where(
229+
and(
230+
eq(credentialMember.userId, existingMember.userId),
231+
eq(credentialMember.status, 'active'),
232+
inArray(
233+
credentialMember.credentialId,
234+
workspaceCredentialIds.map((c) => c.id)
235+
)
236+
)
237+
)
238+
}
239+
218240
logger.info(`Admin API: Removed member ${memberId} from workspace ${workspaceId}`, {
219241
userId: existingMember.userId,
220242
})

apps/sim/app/api/workspaces/members/[id]/route.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '@sim/db'
2-
import { permissions, workspace } from '@sim/db/schema'
2+
import { credential, credentialMember, permissions, workspace } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
4-
import { and, eq } from 'drizzle-orm'
4+
import { and, eq, inArray } from 'drizzle-orm'
55
import { type NextRequest, NextResponse } from 'next/server'
66
import { z } from 'zod'
77
import { getSession } from '@/lib/auth'
@@ -101,6 +101,28 @@ export async function DELETE(req: NextRequest, { params }: { params: Promise<{ i
101101
)
102102
)
103103

104+
// Revoke credential memberships for all credentials in this workspace
105+
const workspaceCredentialIds = await db
106+
.select({ id: credential.id })
107+
.from(credential)
108+
.where(eq(credential.workspaceId, workspaceId))
109+
110+
if (workspaceCredentialIds.length > 0) {
111+
await db
112+
.update(credentialMember)
113+
.set({ status: 'revoked', updatedAt: new Date() })
114+
.where(
115+
and(
116+
eq(credentialMember.userId, userId),
117+
eq(credentialMember.status, 'active'),
118+
inArray(
119+
credentialMember.credentialId,
120+
workspaceCredentialIds.map((c) => c.id)
121+
)
122+
)
123+
)
124+
}
125+
104126
return NextResponse.json({ success: true })
105127
} catch (error) {
106128
logger.error('Error removing workspace member:', error)

0 commit comments

Comments
 (0)