Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: single workspace admin cant accept own invite #3197

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/frontend-2/lib/common/generated/gql/gql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ const documents = {
"\n subscription OnViewerUserActivityBroadcasted(\n $target: ViewerUpdateTrackingTarget!\n $sessionId: String!\n ) {\n viewerUserActivityBroadcasted(target: $target, sessionId: $sessionId) {\n userName\n userId\n user {\n ...LimitedUserAvatar\n }\n state\n status\n sessionId\n }\n }\n": types.OnViewerUserActivityBroadcastedDocument,
"\n subscription OnViewerCommentsUpdated($target: ViewerUpdateTrackingTarget!) {\n projectCommentsUpdated(target: $target) {\n id\n type\n comment {\n id\n parent {\n id\n }\n ...ViewerCommentThread\n }\n }\n }\n": types.OnViewerCommentsUpdatedDocument,
"\n fragment LinkableComment on Comment {\n id\n viewerResources {\n modelId\n versionId\n objectId\n }\n }\n": types.LinkableCommentFragmentDoc,
"\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n": types.UseWorkspaceInviteManager_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n workspaceSlug\n user {\n id\n }\n }\n": types.UseWorkspaceInviteManager_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n }\n": types.WorkspaceMixpanelUpdateGroup_WorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceMixpanelUpdateGroup_Workspace on Workspace {\n id\n name\n description\n domainBasedMembershipProtectionEnabled\n discoverabilityEnabled\n billing {\n cost {\n total\n }\n versionsCount {\n current\n max\n }\n }\n team {\n totalCount\n items {\n ...WorkspaceMixpanelUpdateGroup_WorkspaceCollaborator\n }\n }\n }\n": types.WorkspaceMixpanelUpdateGroup_WorkspaceFragmentDoc,
"\n mutation UpdateRole($input: WorkspaceRoleUpdateInput!) {\n workspaceMutations {\n updateRole(input: $input) {\n team {\n items {\n id\n role\n }\n }\n }\n }\n }\n": types.UpdateRoleDocument,
Expand Down Expand Up @@ -1541,7 +1541,7 @@ export function graphql(source: "\n fragment LinkableComment on Comment {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n"): (typeof documents)["\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n user {\n id\n }\n }\n"];
export function graphql(source: "\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n workspaceSlug\n user {\n id\n }\n }\n"): (typeof documents)["\n fragment UseWorkspaceInviteManager_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n token\n workspaceId\n workspaceSlug\n user {\n id\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
Expand Down
42 changes: 22 additions & 20 deletions packages/frontend-2/lib/common/generated/gql/graphql.ts

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions packages/frontend-2/lib/workspaces/composables/management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ graphql(`
id
token
workspaceId
workspaceSlug
user {
id
}
Expand Down Expand Up @@ -283,6 +284,7 @@ export const useWorkspaceInviteManager = <
if (!token.value || !invite.value) return false

const workspaceId = invite.value.workspaceId
const workspaceSlug = invite.value.workspaceSlug
const shouldAddNewEmail = canAddNewEmail.value && addNewEmail

loading.value = true
Expand All @@ -302,8 +304,8 @@ export const useWorkspaceInviteManager = <

// Redirect
if (accept) {
if (workspaceId) {
window.location.href = workspaceRoute(workspaceId)
if (workspaceSlug) {
window.location.href = workspaceRoute(workspaceSlug)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

noticed the post-accept redirect being busted after the slug feature merge

} else {
window.location.reload()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ type PendingWorkspaceCollaborator {
inviteId: String!
workspaceId: String!
workspaceName: String!
workspaceSlug: String!
"""
E-mail address if target is unregistered or primary e-mail of target registered user
if token was specified to retrieve this invite
Expand Down
2 changes: 2 additions & 0 deletions packages/server/modules/core/graph/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1785,6 +1785,7 @@ export type PendingWorkspaceCollaborator = {
user?: Maybe<LimitedUser>;
workspaceId: Scalars['String']['output'];
workspaceName: Scalars['String']['output'];
workspaceSlug: Scalars['String']['output'];
};

export type PendingWorkspaceCollaboratorsFilter = {
Expand Down Expand Up @@ -5478,6 +5479,7 @@ export type PendingWorkspaceCollaboratorResolvers<ContextType = GraphQLContext,
user?: Resolver<Maybe<ResolversTypes['LimitedUser']>, ParentType, ContextType>;
workspaceId?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
workspaceName?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
workspaceSlug?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,7 @@ export type PendingWorkspaceCollaborator = {
user?: Maybe<LimitedUser>;
workspaceId: Scalars['String']['output'];
workspaceName: Scalars['String']['output'];
workspaceSlug: Scalars['String']['output'];
};

export type PendingWorkspaceCollaboratorsFilter = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ describe('FileUploads @fileuploads', () => {
;({ token: userOneToken } = await createToken({
userId: userOneId,
name: 'test token',
scopes: [Scopes.Streams.Write],
lifespan: 3600
Copy link
Contributor Author

Choose a reason for hiding this comment

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

scopes: [Scopes.Streams.Write]
}))
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,12 @@ export = FF_WORKSPACES_MODULE_ENABLED
)
return workspace!.name
},
workspaceSlug: async (parent, _args, ctx) => {
const workspace = await ctx.loaders.workspaces!.getWorkspace.load(
parent.workspaceId
)
return workspace!.slug
},
invitedBy: async (parent, _args, ctx) => {
const { invitedById } = parent
if (!invitedById) return null
Expand Down
17 changes: 8 additions & 9 deletions packages/server/modules/workspaces/services/management.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,19 +389,10 @@ export const updateWorkspaceRoleFactory =

// Return early if no work required
const previousWorkspaceRole = workspaceRoles.find((acl) => acl.userId === userId)

if (previousWorkspaceRole?.role === nextWorkspaceRole) {
return
}

// Protect against removing last admin
if (
isUserLastWorkspaceAdmin(workspaceRoles, userId) &&
nextWorkspaceRole !== Roles.Workspace.Admin
) {
throw new WorkspaceAdminRequiredError()
}

// prevent role downgrades (used during invite flow)
if (preventRoleDowngrade) {
if (previousWorkspaceRole) {
Expand All @@ -416,6 +407,14 @@ export const updateWorkspaceRoleFactory =
}
}

// Protect against removing last admin
if (
isUserLastWorkspaceAdmin(workspaceRoles, userId) &&
nextWorkspaceRole !== Roles.Workspace.Admin
) {
throw new WorkspaceAdminRequiredError()
}

// ensure domain compliance
if (nextWorkspaceRole !== Roles.Workspace.Guest) {
const workspace = await getWorkspaceWithDomains({ id: workspaceId })
Expand Down
1 change: 1 addition & 0 deletions packages/server/test/graphql/generated/graphql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1770,6 +1770,7 @@ export type PendingWorkspaceCollaborator = {
user?: Maybe<LimitedUser>;
workspaceId: Scalars['String']['output'];
workspaceName: Scalars['String']['output'];
workspaceSlug: Scalars['String']['output'];
};

export type PendingWorkspaceCollaboratorsFilter = {
Expand Down