Просмотр исходного кода

Merge pull request #22321 from overleaf/msm-force-flag-delete-user

[web] Fix user deletion in CE/SP when email does not exist

GitOrigin-RevId: 051f822318d63c8a9f50d5e9aeca095f3b37efb4
Miguel Serrano 1 год назад
Родитель
Сommit
5f913738a8

+ 14 - 3
services/web/app/src/Features/User/UserDeleter.js

@@ -56,7 +56,7 @@ async function deleteUser(userId, options) {
     )
     await _createDeletedUser(user, options)
     await ProjectDeleter.promises.deleteUsersProjects(user._id)
-    await _sendDeleteEmail(user)
+    await _sendDeleteEmail(user, options.force)
     await deleteMongoUser(user._id)
   } catch (error) {
     logger.warn({ error, userId }, 'something went wrong deleting the user')
@@ -119,13 +119,24 @@ async function ensureCanDeleteUser(user) {
   }
 }
 
-async function _sendDeleteEmail(user) {
+async function _sendDeleteEmail(user, force) {
   const emailOptions = {
     to: user.email,
     action: 'account deleted',
     actionDescribed: 'your Overleaf account was deleted',
   }
-  await EmailHandler.promises.sendEmail('securityAlert', emailOptions)
+  try {
+    await EmailHandler.promises.sendEmail('securityAlert', emailOptions)
+  } catch (error) {
+    if (force) {
+      logger.error(
+        { error },
+        'error sending account deletion email notification'
+      )
+    } else {
+      throw error
+    }
+  }
 }
 
 async function _createDeletedUser(user, options) {

+ 1 - 0
services/web/modules/server-ce-scripts/scripts/delete-user.mjs

@@ -24,6 +24,7 @@ async function main() {
       }
       const options = {
         ipAddress: '0.0.0.0',
+        force: true,
       }
       UserDeleter.deleteUser(user._id, options, function (err) {
         if (err) {

+ 38 - 11
services/web/test/unit/src/User/UserDeleterTests.js

@@ -299,18 +299,45 @@ describe('UserDeleter', function () {
             this.DeletedUserMock.verify()
           })
 
-          it('should email the user', async function () {
-            await this.UserDeleter.promises.deleteUser(this.userId, {
-              ipAddress: this.ipAddress,
+          describe('email notifications', function () {
+            it('should email the user', async function () {
+              await this.UserDeleter.promises.deleteUser(this.userId, {
+                ipAddress: this.ipAddress,
+              })
+              const emailOptions = {
+                to: 'bob@bob.com',
+                action: 'account deleted',
+                actionDescribed: 'your Overleaf account was deleted',
+              }
+              expect(
+                this.EmailHandler.promises.sendEmail
+              ).to.have.been.calledWith('securityAlert', emailOptions)
+            })
+
+            it('should fail when the email service fails', async function () {
+              this.EmailHandler.promises.sendEmail = sinon
+                .stub()
+                .rejects(new Error('email failed'))
+              await expect(
+                this.UserDeleter.promises.deleteUser(this.userId, {
+                  ipAddress: this.ipAddress,
+                })
+              ).to.be.rejectedWith('email failed')
+            })
+
+            describe('with "force: true" option', function () {
+              it('should succeed when the email service fails', async function () {
+                this.EmailHandler.promises.sendEmail = sinon
+                  .stub()
+                  .rejects(new Error('email failed'))
+                await expect(
+                  this.UserDeleter.promises.deleteUser(this.userId, {
+                    ipAddress: this.ipAddress,
+                    force: true,
+                  })
+                ).to.be.fulfilled
+              })
             })
-            const emailOptions = {
-              to: 'bob@bob.com',
-              action: 'account deleted',
-              actionDescribed: 'your Overleaf account was deleted',
-            }
-            expect(
-              this.EmailHandler.promises.sendEmail
-            ).to.have.been.calledWith('securityAlert', emailOptions)
           })
 
           it('should add an audit log entry', async function () {