-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Conversation
There was a problem hiding this 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 inpermissions.exception.ts
for proper error handling - Implemented
validateUserWorkspaceIsNotUniqueAdminOrThrow
method inuser-role.service.ts
to check if a user is the last admin - Enhanced
deleteUser
method inuser.service.ts
to prevent deletion of the last admin with clear error messaging - Added
UserWorkspaceModule
toUserModule
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
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({ | ||
userWorkspaceId, | ||
workspaceId, | ||
}) { |
There was a problem hiding this comment.
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 }
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({ | |
userWorkspaceId, | |
workspaceId, | |
}) { | |
public async validateUserWorkspaceIsNotUniqueAdminOrThrow({ | |
userWorkspaceId, | |
workspaceId, | |
}: { userWorkspaceId: string; workspaceId: string }) { |
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({ | ||
adminRoleId, | ||
workspaceId, | ||
}) { |
There was a problem hiding this comment.
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 }
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({ | |
adminRoleId, | |
workspaceId, | |
}) { | |
private async validateMoreThanOneWorkspaceMemberHasAdminRoleOrThrow({ | |
adminRoleId, | |
workspaceId, | |
}: { adminRoleId: string; workspaceId: string }) { |
packages/twenty-server/src/engine/metadata-modules/user-role/user-role.service.ts
Show resolved
Hide resolved
FeatureFlagKey.IsPermissionsEnabled, | ||
workspaceId, | ||
); | ||
|
||
const workspaceMembers = await workspaceDataSource?.query( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 && |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
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