mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
Merge pull request #3063 from n8n-io/n8n-3175-add-delete-usersidentifier-endpoint
⚡ Add DELETE /users/:identifier endpoint
This commit is contained in:
commit
554ea4168f
|
@ -49,6 +49,8 @@ export const connectionName = (): string => {
|
|||
return 'default';
|
||||
};
|
||||
|
||||
export const clean = (users: User[]): Array<Partial<User>> => {
|
||||
return users.map((user) => pick(user, getSelectableProperties('user')));
|
||||
export const clean = (users: User[], keepRole = false): Array<Partial<User>> => {
|
||||
return users.map((user) =>
|
||||
pick(user, getSelectableProperties('user').concat(keepRole ? ['globalRole'] : [])),
|
||||
);
|
||||
};
|
||||
|
|
|
@ -173,11 +173,19 @@ paths:
|
|||
- name: transferId
|
||||
in: query
|
||||
description: ID of the user to transfer workflows and credentials to.
|
||||
required: true
|
||||
required: false
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: string
|
||||
- name: includeRole
|
||||
in: query
|
||||
required: false
|
||||
style: form
|
||||
explode: true
|
||||
schema:
|
||||
type: string
|
||||
example: true
|
||||
responses:
|
||||
"200":
|
||||
description: User deleted successfully
|
||||
|
|
|
@ -19,9 +19,17 @@ import config = require('../../../../../config');
|
|||
|
||||
import * as UserManagementMailer from '../../../../UserManagement/email/UserManagementMailer';
|
||||
|
||||
import { Db, ResponseHelper, InternalHooksManager } from '../../../..';
|
||||
import {
|
||||
Db,
|
||||
ResponseHelper,
|
||||
InternalHooksManager,
|
||||
ActiveWorkflowRunner,
|
||||
ITelemetryUserDeletionData,
|
||||
} from '../../../..';
|
||||
import { Role } from '../../../../databases/entities/Role';
|
||||
import { getInstanceBaseUrl } from '../../../../UserManagement/UserManagementHelper';
|
||||
import { SharedWorkflow } from '../../../../databases/entities/SharedWorkflow';
|
||||
import { SharedCredentials } from '../../../../databases/entities/SharedCredentials';
|
||||
|
||||
export = {
|
||||
createUsers: async (req: UserRequest.Invite, res: express.Response): Promise<void> => {
|
||||
|
@ -156,7 +164,100 @@ export = {
|
|||
res.json([...clean(existingUsers ?? []), ...clean(savedUsers)]);
|
||||
},
|
||||
deleteUser: async (req: UserRequest.Delete, res: express.Response): Promise<void> => {
|
||||
res.json({ success: true });
|
||||
const { identifier: idToDelete } = req.params;
|
||||
|
||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
||||
|
||||
if (req.user.id === idToDelete) {
|
||||
throw new ResponseHelper.ResponseError('Cannot delete your own user', undefined, 400);
|
||||
}
|
||||
|
||||
const { transferId } = req.query;
|
||||
|
||||
if (transferId === idToDelete) {
|
||||
throw new ResponseHelper.ResponseError(
|
||||
'Request to delete a user failed because the user to delete and the transferee are the same user',
|
||||
undefined,
|
||||
400,
|
||||
);
|
||||
}
|
||||
|
||||
const users = await Db.collections.User?.find({
|
||||
where: { id: In([transferId, idToDelete]) },
|
||||
relations: includeRole ? ['globalRole'] : undefined,
|
||||
});
|
||||
|
||||
if (!users?.length || (transferId && users.length !== 2)) {
|
||||
throw new ResponseHelper.ResponseError(
|
||||
'Request to delete a user failed because the ID of the user to delete and/or the ID of the transferee were not found in DB',
|
||||
undefined,
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
const userToDelete = users.find((user) => user.id === req.params.identifier) as User;
|
||||
|
||||
if (transferId) {
|
||||
const transferee = users.find((user) => user.id === transferId);
|
||||
await Db.transaction(async (transactionManager) => {
|
||||
await transactionManager.update(
|
||||
SharedWorkflow,
|
||||
{ user: userToDelete },
|
||||
{ user: transferee },
|
||||
);
|
||||
await transactionManager.update(
|
||||
SharedCredentials,
|
||||
{ user: userToDelete },
|
||||
{ user: transferee },
|
||||
);
|
||||
await transactionManager.delete(User, { id: userToDelete.id });
|
||||
});
|
||||
|
||||
res.json(clean([userToDelete], true)[0]);
|
||||
}
|
||||
|
||||
const [ownedSharedWorkflows = [], ownedSharedCredentials = []] = await Promise.all([
|
||||
Db.collections.SharedWorkflow?.find({
|
||||
relations: ['workflow'],
|
||||
where: { user: userToDelete },
|
||||
}),
|
||||
Db.collections.SharedCredentials?.find({
|
||||
relations: ['credentials'],
|
||||
where: { user: userToDelete },
|
||||
}),
|
||||
]);
|
||||
|
||||
await Db.transaction(async (transactionManager) => {
|
||||
const ownedWorkflows = await Promise.all(
|
||||
ownedSharedWorkflows.map(async ({ workflow }) => {
|
||||
if (workflow.active) {
|
||||
const activeWorkflowRunner = ActiveWorkflowRunner.getInstance();
|
||||
// deactivate before deleting
|
||||
void activeWorkflowRunner.remove(workflow.id.toString());
|
||||
}
|
||||
return workflow;
|
||||
}),
|
||||
);
|
||||
await transactionManager.remove(ownedWorkflows);
|
||||
await transactionManager.remove(ownedSharedCredentials.map(({ credentials }) => credentials));
|
||||
await transactionManager.delete(User, { id: userToDelete.id });
|
||||
});
|
||||
|
||||
const telemetryData: ITelemetryUserDeletionData = {
|
||||
user_id: req.user.id,
|
||||
target_user_old_status: userToDelete.isPending ? 'invited' : 'active',
|
||||
target_user_id: idToDelete,
|
||||
};
|
||||
|
||||
telemetryData.migration_strategy = transferId ? 'transfer_data' : 'delete_data';
|
||||
|
||||
if (transferId) {
|
||||
telemetryData.migration_user_id = transferId;
|
||||
}
|
||||
|
||||
void InternalHooksManager.getInstance().onUserDeletion(req.user.id, telemetryData);
|
||||
|
||||
res.json(clean([userToDelete], true)[0]);
|
||||
},
|
||||
getUser: async (req: UserRequest.Get, res: express.Response): Promise<void> => {
|
||||
const includeRole = req.query?.includeRole?.toLowerCase() === 'true' || false;
|
||||
|
|
|
@ -132,7 +132,7 @@ export function sendErrorResponse(res: Response, error: ResponseError, shouldLog
|
|||
// @ts-ignore
|
||||
response.stack = error.stack;
|
||||
}
|
||||
|
||||
console.log('se esta llmando al error');
|
||||
res.status(httpStatusCode).json(response);
|
||||
}
|
||||
|
||||
|
|
4
packages/cli/src/requests.d.ts
vendored
4
packages/cli/src/requests.d.ts
vendored
|
@ -197,10 +197,10 @@ export declare namespace UserRequest {
|
|||
>;
|
||||
|
||||
export type Delete = AuthenticatedRequest<
|
||||
{ id: string; email: string },
|
||||
{ id: string; email: string; identifier: string },
|
||||
{},
|
||||
{},
|
||||
{ transferId?: string }
|
||||
{ transferId?: string; includeRole: string }
|
||||
>;
|
||||
|
||||
export type Get = AuthenticatedRequest<
|
||||
|
|
Loading…
Reference in a new issue