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

[permissions] forbid deletion of last admin user #10504

Merged
merged 6 commits into from
Feb 27, 2025

Conversation

ijreilly
Copy link
Collaborator

A user should not be able to delete their account if they are the last admin of a workspace.

It means that if a user wants to sign out of twenty, they should delete their workspace, not their account

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

PR Summary

This PR implements protection against deleting the last admin user in a workspace, ensuring workspace continuity.

  • Added new NO_ROLE_FOUND_FOR_USER_WORKSPACE error code in permissions.exception.ts for proper error handling
  • Implemented validateUserWorkspaceIsNotUniqueAdminOrThrow method in user-role.service.ts to check if a user is the last admin
  • Enhanced deleteUser method in user.service.ts to prevent deletion of the last admin with clear error messaging
  • Added UserWorkspaceModule to UserModule imports to access necessary services for admin validation
  • Created integration test in user.integration-spec.ts to verify the functionality works correctly

6 file(s) reviewed, 3 comment(s)
Edit PR Review Bot Settings | Greptile

Comment on lines 140 to 143
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({
userWorkspaceId,
workspaceId,
}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Missing type definition for method parameters. Consider adding explicit types: { userWorkspaceId: string, workspaceId: string }

Suggested change
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({
userWorkspaceId,
workspaceId,
}) {
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({
userWorkspaceId,
workspaceId,
}: { userWorkspaceId: string; workspaceId: string }) {

Comment on lines 222 to 225
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
adminRoleId,
workspaceId,
}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

style: Missing type definition for method parameters. Consider adding explicit types: { adminRoleId: string, workspaceId: string }

Suggested change
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
adminRoleId,
workspaceId,
}) {
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({
adminRoleId,
workspaceId,
}: { adminRoleId: string; workspaceId: string }) {

FeatureFlagKey.IsPermissionsEnabled,
workspaceId,
);

const workspaceMembers = await workspaceDataSource?.query(
Copy link
Member

Choose a reason for hiding this comment

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

It seems you fetch workspaceMembers here but you don't do anything with them if permission is not enabled. Is it intended? I think we still want to keep the old logic in case permission is not enabled

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oops i misplaced the end of the if block. thanks for spotting that

});
} catch (error: any) {
if (
error instanceof PermissionsException &&
Copy link
Member

Choose a reason for hiding this comment

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

Not sure about this logic? Is it only to specify a different message for the exception or am I missing something? It seems you are catching an exception to re-throw the same?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes it's only to change the message exception to have something more relevant, with more context. This message will be shown to users in production when attempting to delete their account in this situation.

the other solution was to prevent account deletion in the product, but it required to compute roles of the user's other workspaces etc. to determine whether or not their account will be deletable, + to store that information in a new graphql field. seemed a bit overkill. wdyt?

Copy link
Member

@Weiko Weiko Feb 27, 2025

Choose a reason for hiding this comment

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

I see, should we re-throw a different code then?
CANNOT_DELETE_LAST_ADMIN?
Not a strong requirement, up to you

@ijreilly ijreilly merged commit 17dbb63 into main Feb 27, 2025
32 checks passed
@ijreilly ijreilly deleted the perm--cannot-delete-last-admin branch February 27, 2025 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants