From 45c465effc9ae396c8bc09f442d42b8d562fc413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:12:08 +0100 Subject: [PATCH 01/24] :fire: Remove `UM_` from SMTP env vars --- packages/cli/config/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 058c1f9587ac3..464128e71bc05 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -599,46 +599,46 @@ const config = convict({ doc: 'How to send emails', format: ['', 'smtp'], default: '', - env: 'N8N_UM_EMAIL_MODE', + env: 'N8N_EMAIL_MODE', }, smtp: { host: { doc: 'SMTP server host', format: String, default: 'smtp.gmail.com', - env: 'N8N_UM_EMAIL_SMTP_HOST', + env: 'N8N_EMAIL_SMTP_HOST', }, port: { doc: 'SMTP Server port', format: Number, default: 465, - env: 'N8N_UM_EMAIL_SMTP_PORT', + env: 'N8N_EMAIL_SMTP_PORT', }, secure: { doc: 'Whether or not to use SSL', format: Boolean, default: true, - env: 'N8N_UM_EMAIL_SMTP_SSL', + env: 'N8N_EMAIL_SMTP_SSL', }, auth: { user: { doc: 'SMTP Login username', format: String, default: 'youremail@gmail.com', - env: 'N8N_UM_EMAIL_SMTP_USER', + env: 'N8N_EMAIL_SMTP_USER', }, pass: { doc: 'SMTP Login password', format: String, default: 'my-super-password', - env: 'N8N_UM_EMAIL_SMTP_PASS', + env: 'N8N_EMAIL_SMTP_PASS', }, }, sender: { doc: 'How to display sender name', format: String, default: '"n8n rocks" ', - env: 'N8N_UM_EMAIL_SMTP_SENDER', + env: 'N8N_SMTP_SENDER', }, }, templates: { From 8e9dc2a10cf19902ef2e49e32d24f3552a16aa1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:15:41 +0100 Subject: [PATCH 02/24] :fire: Remove SMTP host default value --- packages/cli/config/index.ts | 2 +- packages/cli/src/UserManagement/email/NodeMailer.ts | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 464128e71bc05..32f35ffac673c 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -605,7 +605,7 @@ const config = convict({ host: { doc: 'SMTP server host', format: String, - default: 'smtp.gmail.com', + default: '', // e.g. smtp.gmail.com env: 'N8N_EMAIL_SMTP_HOST', }, port: { diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index 931f896de148c..7718f719475d5 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -8,8 +8,14 @@ export class NodeMailer implements UserManagementMailerImplementation { private transport: Transporter; constructor() { + const host = config.get('userManagement.emails.smtp.host'); + + if (!host) { + throw new Error('No SMTP host specified.'); + } + this.transport = createTransport({ - host: config.get('userManagement.emails.smtp.host'), + host, port: config.get('userManagement.emails.smtp.port'), secure: config.get('userManagement.emails.smtp.secure'), auth: { From abb9ab5281d7cc3330e726a84001c14622d9a47b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:21:41 +0100 Subject: [PATCH 03/24] :zap: Update sender value --- packages/cli/config/index.ts | 2 +- packages/cli/src/UserManagement/email/NodeMailer.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 32f35ffac673c..93ae5741c6732 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -637,7 +637,7 @@ const config = convict({ sender: { doc: 'How to display sender name', format: String, - default: '"n8n rocks" ', + default: '', env: 'N8N_SMTP_SENDER', }, }, diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index 7718f719475d5..f86a1ff18cd0e 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -26,9 +26,16 @@ export class NodeMailer implements UserManagementMailerImplementation { } async sendMail(mailData: MailData): Promise { + let sender = config.get('userManagement.emails.smtp.sender'); + const user = config.get('userManagement.emails.smtp.auth.user') as string; + + if (!sender && user.includes('@')) { + sender = user; + } + try { await this.transport.sendMail({ - from: config.get('userManagement.emails.smtp.sender'), + from: sender, to: mailData.emailRecipients, subject: mailData.subject, text: mailData.textOnly, From 587300965f445f6f2afc868eac654c1a81a14d67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:24:24 +0100 Subject: [PATCH 04/24] :zap: Update invite template --- packages/cli/src/UserManagement/email/templates/invite.html | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/UserManagement/email/templates/invite.html b/packages/cli/src/UserManagement/email/templates/invite.html index cab35e6ba49e1..f94c458411344 100644 --- a/packages/cli/src/UserManagement/email/templates/invite.html +++ b/packages/cli/src/UserManagement/email/templates/invite.html @@ -1,6 +1,4 @@

Hi there,

-

You have been invited to join n8n {{domain}}.

-

Please click on the following link, or paste it into your browser to complete the process. The link is valid for 2 hours.

+

You have been invited to join n8n ({{domain}}).

+

To accept, click the following link. It is valid for 2 hours.

{{ inviteAcceptUrl }}

-
-

Thanks!

From e8d090f4f91bd68dfc6286ef0e139e9a411fae29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:26:07 +0100 Subject: [PATCH 05/24] :zap: Update password reset template --- .../UserManagement/email/templates/passwordReset.html | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/cli/src/UserManagement/email/templates/passwordReset.html b/packages/cli/src/UserManagement/email/templates/passwordReset.html index e9dfc0dd2180e..99183da6ea091 100644 --- a/packages/cli/src/UserManagement/email/templates/passwordReset.html +++ b/packages/cli/src/UserManagement/email/templates/passwordReset.html @@ -1,9 +1,5 @@

Hi {{firstName}},

-

You are receiving this because you (or someone else) requested a password reset for your account {{email}} on n8n {{domain}}.

-

Please click on the following link, or paste it into your browser to complete the process:

+

Somebody asked to reset your password on n8n ({{domain}}).

+

If it was not you, you can safely ignore this email.

> +

Click the following link to choose a new password:

{{ passwordResetUrl }} -

-

If you received this in error, you can safely ignore it.

-

Contact your n8n instance owner if you did not request to reset your password.

-
-

Thanks!

From 1354bf3fd80afa2e7bcb6fc7f6140066eb10ec6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 18:29:20 +0100 Subject: [PATCH 06/24] :zap: Update `N8N_EMAIL_MODE` default value --- packages/cli/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 93ae5741c6732..6de105ae12b3d 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -598,7 +598,7 @@ const config = convict({ mode: { doc: 'How to send emails', format: ['', 'smtp'], - default: '', + default: 'smtp', env: 'N8N_EMAIL_MODE', }, smtp: { From 4d32422382c8c556c687a1a0af7cc6bc3945df90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 19:02:31 +0100 Subject: [PATCH 07/24] :fire: Remove `EMAIL` from all SMTP vars --- packages/cli/config/index.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 6de105ae12b3d..64d7ce6f6e700 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -599,39 +599,39 @@ const config = convict({ doc: 'How to send emails', format: ['', 'smtp'], default: 'smtp', - env: 'N8N_EMAIL_MODE', + env: 'N8N_MODE', }, smtp: { host: { doc: 'SMTP server host', format: String, default: '', // e.g. smtp.gmail.com - env: 'N8N_EMAIL_SMTP_HOST', + env: 'N8N_SMTP_HOST', }, port: { doc: 'SMTP Server port', format: Number, default: 465, - env: 'N8N_EMAIL_SMTP_PORT', + env: 'N8N_SMTP_PORT', }, secure: { doc: 'Whether or not to use SSL', format: Boolean, default: true, - env: 'N8N_EMAIL_SMTP_SSL', + env: 'N8N_SMTP_SSL', }, auth: { user: { doc: 'SMTP Login username', format: String, default: 'youremail@gmail.com', - env: 'N8N_EMAIL_SMTP_USER', + env: 'N8N_SMTP_USER', }, pass: { doc: 'SMTP Login password', format: String, default: 'my-super-password', - env: 'N8N_EMAIL_SMTP_PASS', + env: 'N8N_SMTP_PASS', }, }, sender: { From 6372ec70eb4e48f348a764deedf9eb909a9adbc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 3 Mar 2022 19:18:15 +0100 Subject: [PATCH 08/24] :sparkles: Implement `verifyConnection()` --- packages/cli/config/index.ts | 4 +-- .../src/UserManagement/email/Interfaces.ts | 1 + .../src/UserManagement/email/NodeMailer.ts | 30 ++++++++++++++----- .../email/UserManagementMailer.ts | 6 ++++ .../cli/src/UserManagement/routes/users.ts | 20 ++++++++++++- 5 files changed, 51 insertions(+), 10 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 64d7ce6f6e700..eb3ae281a1a6b 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -624,13 +624,13 @@ const config = convict({ user: { doc: 'SMTP Login username', format: String, - default: 'youremail@gmail.com', + default: '', env: 'N8N_SMTP_USER', }, pass: { doc: 'SMTP Login password', format: String, - default: 'my-super-password', + default: '', env: 'N8N_SMTP_PASS', }, }, diff --git a/packages/cli/src/UserManagement/email/Interfaces.ts b/packages/cli/src/UserManagement/email/Interfaces.ts index 5eaa912510f89..97da3d49ee70b 100644 --- a/packages/cli/src/UserManagement/email/Interfaces.ts +++ b/packages/cli/src/UserManagement/email/Interfaces.ts @@ -1,5 +1,6 @@ export interface UserManagementMailerImplementation { sendMail: (mailData: MailData) => Promise; + verifyConnection: () => Promise; } export type InviteEmailData = { diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index f86a1ff18cd0e..54be258cdee62 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -8,14 +8,8 @@ export class NodeMailer implements UserManagementMailerImplementation { private transport: Transporter; constructor() { - const host = config.get('userManagement.emails.smtp.host'); - - if (!host) { - throw new Error('No SMTP host specified.'); - } - this.transport = createTransport({ - host, + host: config.get('userManagement.emails.smtp.host'), port: config.get('userManagement.emails.smtp.port'), secure: config.get('userManagement.emails.smtp.secure'), auth: { @@ -25,6 +19,28 @@ export class NodeMailer implements UserManagementMailerImplementation { }); } + async verifyConnection(): Promise { + const host = config.get('userManagement.emails.smtp.host') as string; + const user = config.get('userManagement.emails.smtp.auth.user') as string; + const pass = config.get('userManagement.emails.smtp.auth.pass') as string; + + return new Promise((resolve, reject) => { + this.transport.verify((error: Error) => { + if (!error) resolve(); + + const message = [ + "You can't invite users, because there is a problem with your SMTP setup:", + ]; + + if (!host) message.push('SMTP host not defined (N8N_SMTP_HOST}).'); + if (!user) message.push('SMTP user not defined (N8N_SMTP_USER}).'); + if (!pass) message.push('SMTP pass not defined (N8N_SMTP_PASS}).'); + + reject(new Error(message.join(' '))); + }); + }); + } + async sendMail(mailData: MailData): Promise { let sender = config.get('userManagement.emails.smtp.sender'); const user = config.get('userManagement.emails.smtp.auth.user') as string; diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 8bda8cd95dd98..6f03406d15997 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -50,6 +50,12 @@ export class UserManagementMailer { } } + async verifyConnection(): Promise { + if (!this.mailer) return Promise.reject(); + + return this.mailer.verifyConnection(); + } + async invite(inviteEmailData: InviteEmailData): Promise { let template = await getTemplate('invite', 'invite.html'); template = replaceStrings(template, inviteEmailData); diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index b8c23ff45994d..6321473c14687 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -147,6 +147,14 @@ export function usersNamespace(this: N8nApp): void { // send invite email to new or not yet setup users const mailer = getInstance(); + try { + await mailer.verifyConnection(); + } catch (error) { + if (error instanceof Error) { + throw new ResponseHelper.ResponseError(error.message, undefined, 500); + } + } + const emailingResults = await Promise.all( usersPendingSetup.map(async ([email, id]) => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions @@ -447,7 +455,17 @@ export function usersNamespace(this: N8nApp): void { const baseUrl = getInstanceBaseUrl(); const inviteAcceptUrl = `${baseUrl}/signup?inviterId=${req.user.id}&inviteeId=${reinvitee.id}`; - const result = await getInstance().invite({ + const mailer = getInstance(); + + try { + await mailer.verifyConnection(); + } catch (error) { + if (error instanceof Error) { + throw new ResponseHelper.ResponseError(error.message, undefined, 500); + } + } + + const result = await mailer.invite({ email: reinvitee.email, inviteAcceptUrl, domain: baseUrl, From 7f98c85089946e4d17902c172702295831e736e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:00:40 +0100 Subject: [PATCH 09/24] :truck: Reposition comment --- packages/cli/config/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index eb3ae281a1a6b..74048b1bd0be8 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -604,8 +604,8 @@ const config = convict({ smtp: { host: { doc: 'SMTP server host', - format: String, - default: '', // e.g. smtp.gmail.com + format: String, // e.g. smtp.gmail.com + default: '', env: 'N8N_SMTP_HOST', }, port: { From 307629b2c9295d188b4b65fb1708f17c78213514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:01:20 +0100 Subject: [PATCH 10/24] :pencil2: Fix typo --- packages/cli/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 74048b1bd0be8..553900c7d3877 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -609,7 +609,7 @@ const config = convict({ env: 'N8N_SMTP_HOST', }, port: { - doc: 'SMTP Server port', + doc: 'SMTP server port', format: Number, default: 465, env: 'N8N_SMTP_PORT', From 81449567e8bcd7fceeb51ec0ce0240bef753e7f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:05:22 +0100 Subject: [PATCH 11/24] :pencil2: Minor env var documentation improvements --- packages/cli/config/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 553900c7d3877..eb9bf07e98263 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -604,7 +604,7 @@ const config = convict({ smtp: { host: { doc: 'SMTP server host', - format: String, // e.g. smtp.gmail.com + format: String, // e.g. 'smtp.gmail.com' default: '', env: 'N8N_SMTP_HOST', }, @@ -615,20 +615,20 @@ const config = convict({ env: 'N8N_SMTP_PORT', }, secure: { - doc: 'Whether or not to use SSL', + doc: 'Whether or not to use SSL for SMTP', format: Boolean, default: true, env: 'N8N_SMTP_SSL', }, auth: { user: { - doc: 'SMTP Login username', - format: String, + doc: 'SMTP login username', + format: String, // e.g.'you@gmail.com' default: '', env: 'N8N_SMTP_USER', }, pass: { - doc: 'SMTP Login password', + doc: 'SMTP login password', format: String, default: '', env: 'N8N_SMTP_PASS', From 0f9acffd1ed46bead68a9d9c21c2d9900d9d968a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:22:50 +0100 Subject: [PATCH 12/24] :art: Fix spacing --- packages/cli/src/UserManagement/email/templates/invite.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/email/templates/invite.html b/packages/cli/src/UserManagement/email/templates/invite.html index f94c458411344..91cff556fefdb 100644 --- a/packages/cli/src/UserManagement/email/templates/invite.html +++ b/packages/cli/src/UserManagement/email/templates/invite.html @@ -1,4 +1,4 @@

Hi there,

-

You have been invited to join n8n ({{domain}}).

+

You have been invited to join n8n ({{ domain }}).

To accept, click the following link. It is valid for 2 hours.

{{ inviteAcceptUrl }}

From 342e22f8d92a1306e49fe00d3008d4b206bffc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:23:49 +0100 Subject: [PATCH 13/24] :art: Fix spacing --- .../cli/src/UserManagement/email/templates/passwordReset.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/email/templates/passwordReset.html b/packages/cli/src/UserManagement/email/templates/passwordReset.html index 99183da6ea091..ec4eb0e9e71d3 100644 --- a/packages/cli/src/UserManagement/email/templates/passwordReset.html +++ b/packages/cli/src/UserManagement/email/templates/passwordReset.html @@ -1,5 +1,5 @@

Hi {{firstName}},

-

Somebody asked to reset your password on n8n ({{domain}}).

+

Somebody asked to reset your password on n8n ({{ domain }}).

If it was not you, you can safely ignore this email.

>

Click the following link to choose a new password:

{{ passwordResetUrl }} From f18d64507afaafd81a0c53233bcd56fce7c1c9d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:38:42 +0100 Subject: [PATCH 14/24] :card_file_box: Remove SMTP settings cache --- packages/cli/commands/start.ts | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index 726b5a8603030..840a9881cbfe8 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -11,7 +11,6 @@ import { BinaryDataManager, IBinaryDataConfig, TUNNEL_SUBDOMAIN_ENV, UserSetting import { Command, flags } from '@oclif/command'; // eslint-disable-next-line import/no-extraneous-dependencies import * as Redis from 'ioredis'; -import { AES, enc } from 'crypto-js'; import { IDataObject, LoggerProxy } from 'n8n-workflow'; import { createHash } from 'crypto'; @@ -219,38 +218,6 @@ export class Start extends Command { throw new Error(RESPONSE_ERROR_MESSAGES.NO_ENCRYPTION_KEY); } - if (config.get('userManagement.emails.mode') === 'smtp') { - const { auth, ...rest } = config.get('userManagement.emails.smtp'); - - const encryptedAuth = { - user: auth.user, - pass: AES.encrypt(auth.pass, encryptionKey).toString(), - }; - - await Db.collections.Settings!.save({ - key: 'userManagement.emails.smtp', - value: JSON.stringify({ ...rest, auth: encryptedAuth }), - loadOnStartup: false, - }); - } else { - // If we don't have SMTP settings, try loading from db. - const smtpSetting = await Db.collections.Settings!.findOne({ - key: 'userManagement.emails.smtp', - }); - - if (smtpSetting) { - const { auth, ...rest } = JSON.parse(smtpSetting.value) as SmtpConfig; - - const decryptedAuth = { - user: auth.user, - pass: AES.decrypt(auth.pass, encryptionKey).toString(enc.Utf8), - }; - - config.set('userManagement.emails.mode', 'smtp'); - config.set('userManagement.emails.smtp', { ...rest, auth: decryptedAuth }); - } - } - // Load settings from database and set them to config. const databaseSettings = await Db.collections.Settings!.find({ loadOnStartup: true }); databaseSettings.forEach((setting) => { From fadc5b3c53107481c2adb32f771f6276bb157789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:39:19 +0100 Subject: [PATCH 15/24] :zap: Adjust log message --- packages/cli/src/UserManagement/routes/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index 6321473c14687..83d0ace4ee7be 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -135,7 +135,7 @@ export function usersNamespace(this: N8nApp): void { throw new ResponseHelper.ResponseError('An error occurred during user creation'); } - Logger.info('Created user shells successfully', { userId: req.user.id }); + Logger.info('Created user shell(s) successfully', { userId: req.user.id }); Logger.verbose(total > 1 ? `${total} user shells created` : `1 user shell created`, { userShells: createUsers, }); From d9a5d5846055f75f3b919fb6aefdc364941d5588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 11:43:09 +0100 Subject: [PATCH 16/24] :zap: Update error message --- packages/cli/src/UserManagement/email/NodeMailer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index 54be258cdee62..66042ca277f2d 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -28,9 +28,7 @@ export class NodeMailer implements UserManagementMailerImplementation { this.transport.verify((error: Error) => { if (!error) resolve(); - const message = [ - "You can't invite users, because there is a problem with your SMTP setup:", - ]; + const message = ['There is a problem with your SMTP setup:']; if (!host) message.push('SMTP host not defined (N8N_SMTP_HOST}).'); if (!user) message.push('SMTP user not defined (N8N_SMTP_USER}).'); From 4b06cfa652477a2808cb1c82342b41b14464ceae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:34:25 +0100 Subject: [PATCH 17/24] :pencil2: Fix template typo --- .../cli/src/UserManagement/email/templates/passwordReset.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/email/templates/passwordReset.html b/packages/cli/src/UserManagement/email/templates/passwordReset.html index ec4eb0e9e71d3..7204766f84ca0 100644 --- a/packages/cli/src/UserManagement/email/templates/passwordReset.html +++ b/packages/cli/src/UserManagement/email/templates/passwordReset.html @@ -1,5 +1,5 @@

Hi {{firstName}},

Somebody asked to reset your password on n8n ({{ domain }}).

-

If it was not you, you can safely ignore this email.

> +

If it was not you, you can safely ignore this email.

Click the following link to choose a new password:

{{ passwordResetUrl }} From 4ee55e79a41697c58785be1acfe52bc598432ba3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:34:57 +0100 Subject: [PATCH 18/24] :pencil2: Adjust wording --- packages/editor-ui/src/plugins/i18n/locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index 802d614d01217..e38ac84f8fa02 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -1097,7 +1097,6 @@ }, "BASIC_INFORMATION": "Basic Information", "CHANGE_PASSWORD": "Change Password", - "CHECK_INBOX_AND_SPAM": "Please check your inbox (and perhaps your spam folder)", "CONFIRM_DATA_HANDLING_AFTER_DELETION": "What should we do with their data?", "CONFIRM_USER_DELETION": "Are you sure you want to delete this invited user?", "CURRENT_PASSWORD": "Current password", @@ -1114,6 +1113,7 @@ "FINISH_ACCOUNT_SETUP": "Finish account setup", "FIRST_NAME": "First name", "FORGOT_MY_PASSWORD": "Forgot my password", + "FORGOT_PASSWORD_SUCCESS_MESSAGE": "We’ve emailed {email} (if there’s a matching account)", "GET_RECOVERY_LINK": "Email me a recovery link", "GO_BACK": "Go back", "INVALID_EMAIL_ERROR": "{email} is not a valid email", @@ -1175,7 +1175,7 @@ "TRANSFERRED_TO_USER": "Transferred to {user}", "TRANSFER_WORKFLOWS_AND_CREDENTIALS": "Transfer their workflows and credentials to another user", "USERS": "Users", - "USERS_INVITED_ERROR": "Users could not be invited", + "USERS_INVITED_ERROR": "Could not invite users", "USERS_INVITED_SUCCESS": "Users invited", "USER_DELETE_ERROR": "Problem while deleting user", "USER_DELETE_SUCCESS": "User deleted", From 1ceed5eeb2021be88e9ec816fc5813399613b008 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:35:18 +0100 Subject: [PATCH 19/24] :zap: Interpolate email into success toast --- packages/editor-ui/src/views/ForgotMyPasswordView.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/views/ForgotMyPasswordView.vue b/packages/editor-ui/src/views/ForgotMyPasswordView.vue index beacae9c43195..9a2aeb82ce81d 100644 --- a/packages/editor-ui/src/views/ForgotMyPasswordView.vue +++ b/packages/editor-ui/src/views/ForgotMyPasswordView.vue @@ -72,7 +72,7 @@ export default mixins( }, }, methods: { - async onSubmit(values: {[key: string]: string}) { + async onSubmit(values: { email: string }) { try { this.loading = true; await this.$store.dispatch('users/sendForgotPasswordEmail', values); @@ -80,7 +80,10 @@ export default mixins( this.$showMessage({ type: 'success', title: this.$locale.baseText('RECOVERY_EMAIL_SENT'), - message: this.$locale.baseText('CHECK_INBOX_AND_SPAM'), + message: this.$locale.baseText( + 'FORGOT_PASSWORD_SUCCESS_MESSAGE', + { interpolate: { email: values.email }}, + ), }); } catch (error) { this.$showError(error, this.$locale.baseText('SENDING_EMAIL_ERROR')); From 2dcc4adc196b9414e2a677f48f905302d487a567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:35:43 +0100 Subject: [PATCH 20/24] :pencil2: Adjust base message in `verifyConnection()` --- packages/cli/src/UserManagement/email/NodeMailer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/cli/src/UserManagement/email/NodeMailer.ts b/packages/cli/src/UserManagement/email/NodeMailer.ts index 66042ca277f2d..d486a5eb46d46 100644 --- a/packages/cli/src/UserManagement/email/NodeMailer.ts +++ b/packages/cli/src/UserManagement/email/NodeMailer.ts @@ -28,11 +28,11 @@ export class NodeMailer implements UserManagementMailerImplementation { this.transport.verify((error: Error) => { if (!error) resolve(); - const message = ['There is a problem with your SMTP setup:']; + const message = []; - if (!host) message.push('SMTP host not defined (N8N_SMTP_HOST}).'); - if (!user) message.push('SMTP user not defined (N8N_SMTP_USER}).'); - if (!pass) message.push('SMTP pass not defined (N8N_SMTP_PASS}).'); + if (!host) message.push('SMTP host not defined (N8N_SMTP_HOST).'); + if (!user) message.push('SMTP user not defined (N8N_SMTP_USER).'); + if (!pass) message.push('SMTP pass not defined (N8N_SMTP_PASS).'); reject(new Error(message.join(' '))); }); From 14a057550b2c03b90e73d432d18956bd0a836689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:36:00 +0100 Subject: [PATCH 21/24] :zap: Verify connection on password reset --- .../src/UserManagement/routes/passwordReset.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/routes/passwordReset.ts b/packages/cli/src/UserManagement/routes/passwordReset.ts index fe1904d6bd06a..f44db463f345c 100644 --- a/packages/cli/src/UserManagement/routes/passwordReset.ts +++ b/packages/cli/src/UserManagement/routes/passwordReset.ts @@ -78,7 +78,21 @@ export function passwordResetNamespace(this: N8nApp): void { url.searchParams.append('userId', id); url.searchParams.append('token', resetPasswordToken); - await UserManagementMailer.getInstance().passwordReset({ + const mailer = UserManagementMailer.getInstance(); + + try { + await mailer.verifyConnection(); + } catch (error) { + if (error instanceof Error) { + throw new ResponseHelper.ResponseError( + `Please contact your administrator: ${error.message}`, + undefined, + 500, + ); + } + } + + await mailer.passwordReset({ email, firstName, lastName, From e4e8a32218b10f6aec910cbede9d1ae8e96643fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 4 Mar 2022 16:36:22 +0100 Subject: [PATCH 22/24] :zap: Bring up POST /users SMTP check --- .../cli/src/UserManagement/routes/users.ts | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index 83d0ace4ee7be..ed5413445cc3c 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -41,6 +41,20 @@ export function usersNamespace(this: N8nApp): void { ); } + const mailer = getInstance(); + + try { + await mailer.verifyConnection(); + } catch (error) { + if (error instanceof Error) { + throw new ResponseHelper.ResponseError( + `There is a problem with your SMTP setup: ${error.message}`, + undefined, + 500, + ); + } + } + if (!config.get('userManagement.isInstanceOwnerSetUp')) { Logger.debug( 'Request to send email invite(s) to user(s) failed because emailing was not set up', @@ -145,15 +159,6 @@ export function usersNamespace(this: N8nApp): void { const usersPendingSetup = Object.entries(createUsers).filter(([email, id]) => id && email); // send invite email to new or not yet setup users - const mailer = getInstance(); - - try { - await mailer.verifyConnection(); - } catch (error) { - if (error instanceof Error) { - throw new ResponseHelper.ResponseError(error.message, undefined, 500); - } - } const emailingResults = await Promise.all( usersPendingSetup.map(async ([email, id]) => { From b0c074a4177f68cf995bb778a10434b71df025cc Mon Sep 17 00:00:00 2001 From: Ben Hesseldieck Date: Mon, 7 Mar 2022 11:12:32 +0100 Subject: [PATCH 23/24] :bug: remove cookie if cookie is not valid --- packages/cli/src/UserManagement/routes/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/UserManagement/routes/auth.ts b/packages/cli/src/UserManagement/routes/auth.ts index 2db59c69561c7..b98b40ecaf707 100644 --- a/packages/cli/src/UserManagement/routes/auth.ts +++ b/packages/cli/src/UserManagement/routes/auth.ts @@ -71,7 +71,7 @@ export function authenticationMethods(this: N8nApp): void { user = await resolveJwt(cookieContents); return sanitizeUser(user); } catch (error) { - throw new Error('Invalid login information'); + res.clearCookie(AUTH_COOKIE_NAME); } } From 7a91c21f625c4976bc91f6c7acb4811fd9c0f29b Mon Sep 17 00:00:00 2001 From: Ben Hesseldieck Date: Mon, 7 Mar 2022 12:50:26 +0100 Subject: [PATCH 24/24] :zap: verify connection on instantiation --- .../email/UserManagementMailer.ts | 3 ++- .../email/templates/invite.html | 2 +- .../email/templates/passwordReset.html | 2 +- .../UserManagement/routes/passwordReset.ts | 19 ++++++++---------- .../cli/src/UserManagement/routes/users.ts | 20 +++++++++---------- 5 files changed, 21 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 6f03406d15997..525f5397d3c4f 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -89,9 +89,10 @@ export class UserManagementMailer { let mailerInstance: UserManagementMailer | undefined; -export function getInstance(): UserManagementMailer { +export async function getInstance(): Promise { if (mailerInstance === undefined) { mailerInstance = new UserManagementMailer(); + await mailerInstance.verifyConnection(); } return mailerInstance; } diff --git a/packages/cli/src/UserManagement/email/templates/invite.html b/packages/cli/src/UserManagement/email/templates/invite.html index 91cff556fefdb..178017d19d5c9 100644 --- a/packages/cli/src/UserManagement/email/templates/invite.html +++ b/packages/cli/src/UserManagement/email/templates/invite.html @@ -1,4 +1,4 @@

Hi there,

You have been invited to join n8n ({{ domain }}).

-

To accept, click the following link. It is valid for 2 hours.

+

To accept, click the following link:

{{ inviteAcceptUrl }}

diff --git a/packages/cli/src/UserManagement/email/templates/passwordReset.html b/packages/cli/src/UserManagement/email/templates/passwordReset.html index 7204766f84ca0..2d8aa5eb5ddb4 100644 --- a/packages/cli/src/UserManagement/email/templates/passwordReset.html +++ b/packages/cli/src/UserManagement/email/templates/passwordReset.html @@ -1,5 +1,5 @@

Hi {{firstName}},

Somebody asked to reset your password on n8n ({{ domain }}).

If it was not you, you can safely ignore this email.

-

Click the following link to choose a new password:

+

Click the following link to choose a new password. It is valid for 2 hours.

{{ passwordResetUrl }} diff --git a/packages/cli/src/UserManagement/routes/passwordReset.ts b/packages/cli/src/UserManagement/routes/passwordReset.ts index f44db463f345c..1a5b3aee74c3c 100644 --- a/packages/cli/src/UserManagement/routes/passwordReset.ts +++ b/packages/cli/src/UserManagement/routes/passwordReset.ts @@ -78,10 +78,15 @@ export function passwordResetNamespace(this: N8nApp): void { url.searchParams.append('userId', id); url.searchParams.append('token', resetPasswordToken); - const mailer = UserManagementMailer.getInstance(); - try { - await mailer.verifyConnection(); + const mailer = await UserManagementMailer.getInstance(); + await mailer.passwordReset({ + email, + firstName, + lastName, + passwordResetUrl: url.toString(), + domain: baseUrl, + }); } catch (error) { if (error instanceof Error) { throw new ResponseHelper.ResponseError( @@ -92,14 +97,6 @@ export function passwordResetNamespace(this: N8nApp): void { } } - await mailer.passwordReset({ - email, - firstName, - lastName, - passwordResetUrl: url.toString(), - domain: baseUrl, - }); - Logger.info('Sent password reset email successfully', { userId: user.id, email }); }), ); diff --git a/packages/cli/src/UserManagement/routes/users.ts b/packages/cli/src/UserManagement/routes/users.ts index 2fd9409b6fffa..2b649d01cee05 100644 --- a/packages/cli/src/UserManagement/routes/users.ts +++ b/packages/cli/src/UserManagement/routes/users.ts @@ -14,7 +14,7 @@ import { getInstanceBaseUrl, sanitizeUser, validatePassword } from '../UserManag import { User } from '../../databases/entities/User'; import { SharedWorkflow } from '../../databases/entities/SharedWorkflow'; import { SharedCredentials } from '../../databases/entities/SharedCredentials'; -import { getInstance } from '../email/UserManagementMailer'; +import * as UserManagementMailer from '../email/UserManagementMailer'; import config = require('../../../config'); import { issueCookie } from '../auth/jwt'; @@ -37,10 +37,9 @@ export function usersNamespace(this: N8nApp): void { ); } - const mailer = getInstance(); - + let mailer: UserManagementMailer.UserManagementMailer | undefined; try { - await mailer.verifyConnection(); + mailer = await UserManagementMailer.getInstance(); } catch (error) { if (error instanceof Error) { throw new ResponseHelper.ResponseError( @@ -160,7 +159,7 @@ export function usersNamespace(this: N8nApp): void { usersPendingSetup.map(async ([email, id]) => { // eslint-disable-next-line @typescript-eslint/restrict-template-expressions const inviteAcceptUrl = `${baseUrl}/signup?inviterId=${req.user.id}&inviteeId=${id}`; - const result = await mailer.invite({ + const result = await mailer?.invite({ email, inviteAcceptUrl, domain: baseUrl, @@ -171,7 +170,7 @@ export function usersNamespace(this: N8nApp): void { email, }, }; - if (!result.success) { + if (!result?.success) { Logger.error('Failed to send email', { userId: req.user.id, inviteAcceptUrl, @@ -468,23 +467,22 @@ export function usersNamespace(this: N8nApp): void { const baseUrl = getInstanceBaseUrl(); const inviteAcceptUrl = `${baseUrl}/signup?inviterId=${req.user.id}&inviteeId=${reinvitee.id}`; - const mailer = getInstance(); - + let mailer: UserManagementMailer.UserManagementMailer | undefined; try { - await mailer.verifyConnection(); + mailer = await UserManagementMailer.getInstance(); } catch (error) { if (error instanceof Error) { throw new ResponseHelper.ResponseError(error.message, undefined, 500); } } - const result = await mailer.invite({ + const result = await mailer?.invite({ email: reinvitee.email, inviteAcceptUrl, domain: baseUrl, }); - if (!result.success) { + if (!result?.success) { Logger.error('Failed to send email', { email: reinvitee.email, inviteAcceptUrl,