mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
feat(core): Add user.profile.beforeUpdate hook (#8144)
Add `user.profile.beforeUpdate` hook so we can prevent user email change if it overlaps with other users email.
This commit is contained in:
parent
ece48d6a13
commit
e126ed74f3
|
@ -51,6 +51,28 @@ export function sendSuccessResponse(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given error is a ResponseError. It can be either an
|
||||||
|
* instance of ResponseError or an error which has the same properties.
|
||||||
|
* The latter case is for external hooks.
|
||||||
|
*/
|
||||||
|
function isResponseError(error: Error): error is ResponseError {
|
||||||
|
if (error instanceof ResponseError) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof Error) {
|
||||||
|
return (
|
||||||
|
'httpStatusCode' in error &&
|
||||||
|
typeof error.httpStatusCode === 'number' &&
|
||||||
|
'errorCode' in error &&
|
||||||
|
typeof error.errorCode === 'number'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
interface ErrorResponse {
|
interface ErrorResponse {
|
||||||
code: number;
|
code: number;
|
||||||
message: string;
|
message: string;
|
||||||
|
@ -66,7 +88,7 @@ export function sendErrorResponse(res: Response, error: Error) {
|
||||||
message: error.message ?? 'Unknown error',
|
message: error.message ?? 'Unknown error',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (error instanceof ResponseError) {
|
if (isResponseError(error)) {
|
||||||
if (inDevelopment) {
|
if (inDevelopment) {
|
||||||
console.error(picocolors.red(error.httpStatusCode), error.message);
|
console.error(picocolors.red(error.httpStatusCode), error.message);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,8 @@ export class MeController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.externalHooks.run('user.profile.beforeUpdate', [userId, currentEmail, payload]);
|
||||||
|
|
||||||
await this.userService.update(userId, payload);
|
await this.userService.update(userId, payload);
|
||||||
const user = await this.userService.findOneOrFail({ where: { id: userId } });
|
const user = await this.userService.findOneOrFail({ where: { id: userId } });
|
||||||
|
|
||||||
|
|
|
@ -53,6 +53,12 @@ describe('MeController', () => {
|
||||||
|
|
||||||
await controller.updateCurrentUser(req, res);
|
await controller.updateCurrentUser(req, res);
|
||||||
|
|
||||||
|
expect(externalHooks.run).toHaveBeenCalledWith('user.profile.beforeUpdate', [
|
||||||
|
user.id,
|
||||||
|
user.email,
|
||||||
|
reqBody,
|
||||||
|
]);
|
||||||
|
|
||||||
expect(userService.update).toHaveBeenCalled();
|
expect(userService.update).toHaveBeenCalled();
|
||||||
|
|
||||||
const cookieOptions = captor<CookieOptions>();
|
const cookieOptions = captor<CookieOptions>();
|
||||||
|
@ -93,6 +99,28 @@ describe('MeController', () => {
|
||||||
expect(updatedUser.id).not.toBe('0');
|
expect(updatedUser.id).not.toBe('0');
|
||||||
expect(updatedUser.globalRoleId).not.toBe('42');
|
expect(updatedUser.globalRoleId).not.toBe('42');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should throw BadRequestError if beforeUpdate hook throws BadRequestError', async () => {
|
||||||
|
const user = mock<User>({
|
||||||
|
id: '123',
|
||||||
|
password: 'password',
|
||||||
|
authIdentities: [],
|
||||||
|
globalRoleId: '1',
|
||||||
|
});
|
||||||
|
const reqBody = { email: 'valid@email.com', firstName: 'John', lastName: 'Potato' };
|
||||||
|
const req = mock<MeRequest.UserUpdate>({ user, body: reqBody });
|
||||||
|
userService.findOneOrFail.mockResolvedValue(user);
|
||||||
|
|
||||||
|
externalHooks.run.mockImplementationOnce(async (hookName) => {
|
||||||
|
if (hookName === 'user.profile.beforeUpdate') {
|
||||||
|
throw new BadRequestError('Invalid email address');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(controller.updateCurrentUser(req, mock())).rejects.toThrowError(
|
||||||
|
new BadRequestError('Invalid email address'),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updatePassword', () => {
|
describe('updatePassword', () => {
|
||||||
|
|
Loading…
Reference in a new issue