Skip to content

Commit 1b62796

Browse files
committed
fix(api): keep workspace member operations in the operations registry and append the CLI user column
1 parent ebdce71 commit 1b62796

9 files changed

Lines changed: 54 additions & 57 deletions

File tree

‎apps/sim/app/api/v2/workspaces/[workspaceId]/members/[userId]/route.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { presentWorkspaceMember } from '@/lib/api/server/workspace-presenters'
77
import { v2WorkspaceErrorPolicies } from '@/lib/workspaces/api/route-policies'
88
import { updateWorkspaceMember } from '@/lib/workspaces/application/manage-permissions'
99
import {
10-
removeWorkspaceMemberOperation,
10+
workspaceMemberOperations,
1111
workspacePermissionOperations,
12-
} from '@/lib/workspaces/application/member-operations'
12+
} from '@/lib/workspaces/application/operations'
1313
import { removeWorkspaceMember } from '@/lib/workspaces/application/remove-member'
1414

1515
/** PATCH /api/v2/workspaces/[workspaceId]/members/[userId] — Change one member's role. */
@@ -27,7 +27,7 @@ export const PATCH = defineV2JsonRoute({
2727
/** DELETE /api/v2/workspaces/[workspaceId]/members/[userId] — Remove a member, or leave. */
2828
export const DELETE = defineV2JsonRoute({
2929
contract: v2RemoveWorkspaceMemberContract,
30-
operation: removeWorkspaceMemberOperation,
30+
operation: workspaceMemberOperations.remove,
3131
auth: v2ApiKeyAuth,
3232
rateLimit: v2RateLimits.publicApi,
3333
errorPolicy: v2WorkspaceErrorPolicies.manageWorkspaceMembers,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import {
2323
readWorkspacePermissions,
2424
updateWorkspacePermissions,
2525
} from '@/lib/workspaces/application/manage-permissions'
26-
import { workspacePermissionOperations } from '@/lib/workspaces/application/member-operations'
26+
import { workspacePermissionOperations } from '@/lib/workspaces/application/operations'
2727
import { WorkspacePermissionError } from '@/lib/workspaces/permissions/management-store'
2828

2929
function permissionErrorResponse(error: unknown): NextResponse {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import {
1111
NoWorkspaceAccessError,
1212
} from '@/lib/core/application/workspace-authorization'
1313
import { captureServerEvent } from '@/lib/posthog/server'
14-
import { removeWorkspaceMemberOperation } from '@/lib/workspaces/application/member-operations'
14+
import { workspaceMemberOperations } from '@/lib/workspaces/application/operations'
1515
import { removeWorkspaceMember } from '@/lib/workspaces/application/remove-member'
1616
import { WorkspaceBillingAccountRemovalError } from '@/lib/workspaces/utils'
1717

1818
export const DELETE = defineInternalJsonRoute({
1919
contract: removeWorkspaceMemberContract,
2020
auth: internalSessionAuth,
21-
operation: removeWorkspaceMemberOperation,
21+
operation: workspaceMemberOperations.remove,
2222
rateLimit: internalRateLimits.none({
2323
reason: 'Preserve existing workspace member removal policy.',
2424
}),

‎apps/sim/lib/api/contracts/v2/openapi/resources.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ import { secretOperations } from '@/lib/secrets/application/operations'
108108
import { skillOperations } from '@/lib/skills/application/operations'
109109
import { toolExecutionOperations } from '@/lib/tool-execution/application/operations'
110110
import {
111-
removeWorkspaceMemberOperation,
111+
workspaceMemberOperations,
112+
workspaceOperations,
112113
workspacePermissionOperations,
113-
} from '@/lib/workspaces/application/member-operations'
114-
import { workspaceOperations } from '@/lib/workspaces/application/operations'
114+
} from '@/lib/workspaces/application/operations'
115115

116116
const BLOCK_SUMMARY_EXAMPLE = {
117117
id: 'slack',
@@ -795,7 +795,7 @@ const declaredRoutes = [
795795
defineOpenApiRoute(
796796
v2RemoveWorkspaceMemberContract,
797797
resourceOperation('Workspaces', {
798-
applicationOperation: removeWorkspaceMemberOperation,
798+
applicationOperation: workspaceMemberOperations.remove,
799799
operationId: 'removeWorkspaceMember',
800800
summary: 'Remove workspace member',
801801
description: `Remove a member’s workspace access, or leave the workspace by naming yourself. Removing someone else requires workspace Admin access; any member may leave. The last Admin cannot leave, organization Admins hold access through their organization role and cannot be removed here, and the workspace billing account cannot be removed until billing is reassigned. Removing the workspace owner transfers ownership to the billing account. When this was the member’s last workspace in its organization, their organization membership and seat are removed too and all of their sessions end, since this endpoint is called with an API key or OAuth token rather than from a signed-in browser session. ${WORKSPACE_API_KEY_DENIED}`,

‎apps/sim/lib/workspaces/application/manage-permissions.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { defineAuthorizedWorkspaceUseCase, ForbiddenOperationError } from '@/lib
66
import { OrchestrationError, type OrchestrationErrorCode } from '@/lib/core/orchestration/types'
77
import { HttpError } from '@/lib/core/utils/http-error'
88
import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/context'
9-
import { workspacePermissionOperations } from '@/lib/workspaces/application/member-operations'
9+
import { workspacePermissionOperations } from '@/lib/workspaces/application/operations'
1010
import {
1111
type WorkspacePermissionUpdate,
1212
workspacePermissionUpdatesSchema,

‎apps/sim/lib/workspaces/application/member-operations.ts‎

Lines changed: 0 additions & 42 deletions
This file was deleted.

‎apps/sim/lib/workspaces/application/operations.ts‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,41 @@ export const workspaceOperations = {
3939
delegatedServices: ['copilot'],
4040
}),
4141
} as const
42+
43+
export const workspacePermissionOperations = {
44+
/** permission-group-exempt: the roster describes the workspace the caller belongs to. */
45+
read: defineWorkspaceOperation({
46+
id: 'workspaces.permissions.read',
47+
minimumRole: 'read',
48+
workspaceApiKey: 'deny',
49+
principalKinds: ['session', 'delegated'],
50+
delegatedServices: ['copilot'],
51+
capability: 'none',
52+
contentDelivery: 'none',
53+
}),
54+
/** permission-group-exempt: workspace administrators manage existing collaborator roles. */
55+
update: defineWorkspaceOperation({
56+
id: 'workspaces.permissions.update',
57+
minimumRole: 'admin',
58+
workspaceApiKey: 'deny',
59+
oauthScope: 'api:write',
60+
principalKinds: ['session', 'personal_api_key', 'oauth_access_token', 'delegated'],
61+
delegatedServices: ['copilot'],
62+
capability: 'none',
63+
contentDelivery: 'none',
64+
}),
65+
} as const
66+
67+
export const workspaceMemberOperations = {
68+
/** permission-group-exempt: members may leave; workspace administrators may remove existing collaborators. */
69+
remove: defineWorkspaceOperation({
70+
id: 'workspaces.members.remove',
71+
minimumRole: 'read',
72+
workspaceApiKey: 'deny',
73+
capability: 'none',
74+
contentDelivery: 'none',
75+
oauthScope: 'api:write',
76+
principalKinds: ['session', 'personal_api_key', 'oauth_access_token', 'delegated'],
77+
delegatedServices: ['copilot'],
78+
}),
79+
} as const

‎apps/sim/lib/workspaces/application/remove-member.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { requirePrincipalSubjectUserId } from '@sim/auth/principal'
33
import { defineAuthorizedWorkspaceUseCase, requireCurrentHumanRole } from '@/lib/core/application'
44
import { OrchestrationError } from '@/lib/core/orchestration/types'
55
import { resolveActiveWorkspaceApplicationContext } from '@/lib/workspaces/application/context'
6-
import { removeWorkspaceMemberOperation } from '@/lib/workspaces/application/member-operations'
6+
import { workspaceMemberOperations } from '@/lib/workspaces/application/operations'
77
import { removeWorkspaceMemberRecord } from '@/lib/workspaces/permissions/member-removal-store'
88

99
export interface RemoveWorkspaceMemberInput {
@@ -12,7 +12,7 @@ export interface RemoveWorkspaceMemberInput {
1212
}
1313

1414
export const removeWorkspaceMember = defineAuthorizedWorkspaceUseCase({
15-
operation: removeWorkspaceMemberOperation,
15+
operation: workspaceMemberOperations.remove,
1616
resolveContext: ({ input }: { input: RemoveWorkspaceMemberInput }) =>
1717
resolveActiveWorkspaceApplicationContext(input.workspaceId),
1818
authorizationOptions: { delegation: { audience: 'sim:settings', isWithinScope: () => true } },

‎packages/sim-cli/src/contract/commands.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1525,14 +1525,15 @@ export const CLI_CONTRACT: CliContract = {
15251525
describe: 'List workspace members',
15261526
profileWorkspacePath: true,
15271527
// `members update` and `members remove` take the user id, and the listing
1528-
// is where a caller finds it.
1528+
// is where a caller finds it. Appended last so the shipped text columns keep
1529+
// their positions for scripts that read them by field.
15291530
columns: [
1530-
{ header: 'user', path: 'userId' },
15311531
{ header: 'email' },
15321532
{ header: 'name' },
15331533
{ header: 'role' },
15341534
{ header: 'external', path: 'isExternal', format: 'bool' },
15351535
{ header: 'joined', path: 'joinedAt', format: 'timestamp' },
1536+
{ header: 'user', path: 'userId' },
15361537
],
15371538
},
15381539
updateWorkspaceMember: {

0 commit comments

Comments
 (0)