diff --git a/services/web/app/src/Features/Email/EmailBuilder.js b/services/web/app/src/Features/Email/EmailBuilder.js
index cdaf502ec8..a24ddfeff5 100644
--- a/services/web/app/src/Features/Email/EmailBuilder.js
+++ b/services/web/app/src/Features/Email/EmailBuilder.js
@@ -527,8 +527,8 @@ templates.groupSSOReauthenticate = ctaTemplate({
return [
`Hi,
- Single sign-on for your Overleaf group has been updated.
- This means you need to reauthenticate your Overleaf account with your group’s SSO provider.
+ Single sign-on for your Overleaf group has been updated.
+ This means you need to reauthenticate your Overleaf account with your group’s SSO provider.
`,
]
@@ -786,6 +786,31 @@ templates.userOnboardingEmail = NoCTAEmailTemplate({
},
})
+templates.deletedAccount = NoCTAEmailTemplate({
+ subject() {
+ return 'Overleaf security note: account deletion confirmation'
+ },
+ title() {
+ return 'Account deleted'
+ },
+ message(opts, isPlainText) {
+ const dateFormatted = moment().format('dddd D MMMM YYYY')
+ const timeFormatted = moment().format('HH:mm')
+ const helpLink = EmailMessageHelper.displayLink(
+ 'quick guide',
+ `${settings.siteUrl}/learn/how-to/Keeping_your_account_secure`,
+ isPlainText
+ )
+
+ return [
+ `We are writing to let you know that your ${settings.appName} account was deleted on ${dateFormatted} at ${timeFormatted} GMT.`,
+ `If this was you, you're all set and can ignore this email.`,
+ `If you did not take this action, please get in touch with our support team at ${settings.adminEmail} to report this as potentially suspicious activity on your account.`,
+ `For tips on keeping your ${settings.appName} account secure, read our ${helpLink}.`,
+ ]
+ },
+})
+
templates.securityAlert = NoCTAEmailTemplate({
subject(opts) {
return `Overleaf security note: ${opts.action}`
diff --git a/services/web/app/src/Features/User/UserDeleter.js b/services/web/app/src/Features/User/UserDeleter.js
index 49d0f0b264..71fcc345c0 100644
--- a/services/web/app/src/Features/User/UserDeleter.js
+++ b/services/web/app/src/Features/User/UserDeleter.js
@@ -16,6 +16,7 @@ const InstitutionsAPI = require('../Institutions/InstitutionsAPI')
const Modules = require('../../infrastructure/Modules')
const Errors = require('../Errors/Errors')
const OnboardingDataCollectionManager = require('../OnboardingDataCollection/OnboardingDataCollectionManager')
+const EmailHandler = require('../Email/EmailHandler')
module.exports = {
deleteUser: callbackify(deleteUser),
@@ -48,6 +49,7 @@ async function deleteUser(userId, options) {
await Modules.promises.hooks.fire('deleteUser', userId)
await _createDeletedUser(user, options)
await ProjectDeleter.promises.deleteUsersProjects(user._id)
+ await _sendDeleteEmail(user)
await deleteMongoUser(user._id)
} catch (error) {
logger.warn({ error, userId }, 'something went wrong deleting the user')
@@ -110,6 +112,13 @@ async function ensureCanDeleteUser(user) {
}
}
+async function _sendDeleteEmail(user) {
+ const emailOptions = {
+ to: user.email,
+ }
+ await EmailHandler.promises.sendEmail('deletedAccount', emailOptions)
+}
+
async function _createDeletedUser(user, options) {
await DeletedUser.updateOne(
{ 'deleterData.deletedUserId': user._id },
diff --git a/services/web/test/unit/src/User/UserDeleterTests.js b/services/web/test/unit/src/User/UserDeleterTests.js
index 2550ec0f8c..bf837fa655 100644
--- a/services/web/test/unit/src/User/UserDeleterTests.js
+++ b/services/web/test/unit/src/User/UserDeleterTests.js
@@ -100,6 +100,12 @@ describe('UserDeleter', function () {
deleteOnboardingDataCollection: sinon.stub().resolves(),
}
+ this.EmailHandler = {
+ promises: {
+ sendEmail: sinon.stub().resolves(),
+ },
+ }
+
this.UserDeleter = SandboxedModule.require(modulePath, {
requires: {
'../../models/User': { User },
@@ -119,6 +125,7 @@ describe('UserDeleter', function () {
'../../infrastructure/Modules': this.Modules,
'../OnboardingDataCollection/OnboardingDataCollectionManager':
this.OnboardingDataCollectionManager,
+ '../Email/EmailHandler': this.EmailHandler,
},
})
})
@@ -251,6 +258,16 @@ describe('UserDeleter', function () {
await this.UserDeleter.promises.deleteUser(this.userId, {})
this.DeletedUserMock.verify()
})
+
+ it('should email the user', async function () {
+ await this.UserDeleter.promises.deleteUser(this.userId, {})
+ const emailOptions = {
+ to: 'bob@bob.com',
+ }
+ expect(
+ this.EmailHandler.promises.sendEmail
+ ).to.have.been.calledWith('deletedAccount', emailOptions)
+ })
})
describe('when unsubscribing from mailchimp fails', function () {