Fix user deletion flows (#15614)
**Before** - any user with workpace_members permission was able to remove a user from their workspace. This triggered the deletion of workspaceMember + of userWorkspace, but did not delete the user (even if they had no workspace left) nor the roleTarget (acts as junction between role and userWorkspace) which was left with a userWorkspaceId pointing to nothing. This is because roleTarget points to userWorkspaceId but the foreign key constraint was not implemented - any user could delete their own account. This triggered the deletion of all their workspaceMembers, but not of their userWorkspace nor their user nor the roleTarget --> we have orphaned userWorkspace, not technically but product wise - a userWorkspace without a workspaceMember does not make sense So the problems are - we have some roleTargets pointing to non-existing userWorkspaceId (which caused https://github.com/twentyhq/twenty/issues/14608 ) - we have userWorkspaces that should not exist and that have no workspaceMember counterpart - it is not possible for a user to leave a workspace by themselves, they can only leave all workspaces at once, except if they are being removed from the workspace by another user **Now** - if a user has multiple workspaces, they are given the possibility to leave one workspace while remaining in the others (we show two buttons: Leave workspace and Delete account buttons). if a user has just one workspace, they only see Delete account - when a user leaves a workspace, we delete their workspaceMember, userWorkspace and roleTarget. If they don't belong to any other workspace we also soft-delete their user - soft-deleted users get hard deleted after 30 days thanks to a cron - we have two commands to clean the orphans roleTarget and userWorkspace (TODO: query db to see how many must be run) **Next** - once the commands have been run, we can implement and introduce the foreign key constraint on roleTarget Fixes https://github.com/twentyhq/twenty/issues/14608
This commit is contained in:
+1
-1
@@ -46,7 +46,6 @@ export const permissionGraphqlApiExceptionHandler = (
|
||||
case PermissionsExceptionCode.ROLE_CANNOT_BE_ASSIGNED_TO_USERS:
|
||||
throw new UserInputError(error);
|
||||
case PermissionsExceptionCode.ROLE_NOT_FOUND:
|
||||
case PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND:
|
||||
case PermissionsExceptionCode.OBJECT_METADATA_NOT_FOUND:
|
||||
case PermissionsExceptionCode.FIELD_METADATA_NOT_FOUND:
|
||||
case PermissionsExceptionCode.PERMISSION_NOT_FOUND:
|
||||
@@ -69,6 +68,7 @@ export const permissionGraphqlApiExceptionHandler = (
|
||||
case PermissionsExceptionCode.API_KEY_ROLE_NOT_FOUND:
|
||||
case PermissionsExceptionCode.JOIN_COLUMN_NAME_REQUIRED:
|
||||
case PermissionsExceptionCode.COMPOSITE_TYPE_NOT_FOUND:
|
||||
case PermissionsExceptionCode.USER_WORKSPACE_NOT_FOUND:
|
||||
throw error;
|
||||
default: {
|
||||
return assertUnreachable(error.code);
|
||||
|
||||
@@ -155,13 +155,13 @@ export class RoleService {
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const defaultRole = await this.workspaceRepository.findOne({
|
||||
const workspace = await this.workspaceRepository.findOne({
|
||||
where: {
|
||||
id: workspaceId,
|
||||
},
|
||||
});
|
||||
|
||||
const defaultRoleId = defaultRole?.defaultRoleId;
|
||||
const defaultRoleId = workspace?.defaultRoleId;
|
||||
|
||||
if (!isDefined(defaultRoleId)) {
|
||||
throw new PermissionsException(
|
||||
|
||||
Reference in New Issue
Block a user