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

Prevent Group subscription admins from deleting their own group subscription when invited to join

GitOrigin-RevId: a467a690cf4ee8b1a1081496205559a7f183a1f9
Simon Gardner 1 год назад
Родитель
Сommit
1bef1bb1d4

+ 37 - 31
services/web/app/src/Features/Subscription/TeamInvitesHandler.js

@@ -65,40 +65,36 @@ async function importInvite(subscription, inviterName, email, token, sentAt) {
   return subscription.save()
 }
 
-async function _deleteUserSubscription(userId, ipAddress) {
+async function _deleteUserSubscription(subscription, userId, ipAddress) {
   // Delete released user subscription to make it on a free plan
-  const subscription =
-    await SubscriptionLocator.promises.getUsersSubscription(userId)
 
-  if (subscription) {
-    logger.debug(
-      {
-        subscriptionId: subscription._id,
-      },
-      'deleting user subscription'
-    )
+  logger.debug(
+    {
+      subscriptionId: subscription._id,
+    },
+    'deleting user subscription'
+  )
 
-    const deleterData = {
-      id: userId,
-      ip: ipAddress,
-    }
-    await SubscriptionUpdater.promises.deleteSubscription(
-      subscription,
-      deleterData
-    )
+  const deleterData = {
+    id: userId,
+    ip: ipAddress,
+  }
+  await SubscriptionUpdater.promises.deleteSubscription(
+    subscription,
+    deleterData
+  )
 
-    // Terminate the subscription in Recurly
-    if (subscription.recurlySubscription_id) {
-      try {
-        await RecurlyClient.promises.terminateSubscriptionByUuid(
-          subscription.recurlySubscription_id
-        )
-      } catch (err) {
-        logger.error(
-          { err, subscriptionId: subscription._id },
-          'terminating subscription failed'
-        )
-      }
+  // Terminate the subscription in Recurly
+  if (subscription.recurlySubscription_id) {
+    try {
+      await RecurlyClient.promises.terminateSubscriptionByUuid(
+        subscription.recurlySubscription_id
+      )
+    } catch (err) {
+      logger.error(
+        { err, subscriptionId: subscription._id },
+        'terminating subscription failed'
+      )
     }
   }
 }
@@ -117,7 +113,17 @@ async function acceptInvite(token, userId, ipAddress) {
   )
 
   if (subscription.managedUsersEnabled) {
-    await _deleteUserSubscription(userId, ipAddress)
+    // check if user has a personal subscription
+    const userSubscription =
+      await SubscriptionLocator.promises.getUsersSubscription(userId)
+
+    if (userSubscription) {
+      // if user has a personal subscription and joins a managed group, delete their personal subscription
+      // but make sure that it's not the same subscription as the group one.
+      if (!userSubscription._id.equals(subscription._id)) {
+        await _deleteUserSubscription(userSubscription, userId, ipAddress)
+      }
+    }
     await Modules.promises.hooks.fire(
       'enrollInManagedSubscription',
       userId,

+ 34 - 2
services/web/test/unit/src/Subscription/TeamInvitesHandlerTests.js

@@ -344,12 +344,27 @@ describe('TeamInvitesHandler', function () {
         email: 'tyrion@example.com',
       }
 
+      this.user_subscription = {
+        id: '66264b9125930b976cc0811e',
+        _id: new ObjectId('66264b9125930b976cc0811e'),
+        groupPlan: false,
+        recurlySubscription_id: 'fa1b2cfa156gh',
+        admin_id: '123456789',
+        member_ids: [],
+        teamInvites: [],
+        save: sinon.stub().resolves(),
+      }
+
       this.ipAddress = '127.0.0.1'
 
       this.UserGetter.promises.getUserByAnyEmail
         .withArgs(this.user.email)
         .resolves(this.user)
 
+      this.SubscriptionLocator.promises.getUsersSubscription
+        .withArgs(this.user.id)
+        .resolves(this.user_subscription)
+
       this.subscription.teamInvites.push({
         email: 'john.snow@example.com',
         token: 'dddddddd',
@@ -421,12 +436,12 @@ describe('TeamInvitesHandler', function () {
         )
         sinon.assert.calledWith(
           this.SubscriptionUpdater.promises.deleteSubscription,
-          this.subscription,
+          this.user_subscription,
           { id: this.user.id, ip: this.ipAddress }
         )
         sinon.assert.calledWith(
           this.RecurlyClient.promises.terminateSubscriptionByUuid,
-          this.subscription.recurlySubscription_id
+          this.user_subscription.recurlySubscription_id
         )
         sinon.assert.calledWith(
           this.Modules.promises.hooks.fire,
@@ -435,6 +450,23 @@ describe('TeamInvitesHandler', function () {
           this.subscription
         )
       })
+
+      it('should not delete the users subscription if that subscription is also the join target', async function () {
+        this.subscription.managedUsersEnabled = true
+        this.SubscriptionLocator.promises.getUsersSubscription
+          .withArgs(this.user.id)
+          .resolves(this.subscription)
+
+        await this.TeamInvitesHandler.promises.acceptInvite(
+          'dddddddd',
+          this.user.id,
+          this.ipAddress
+        )
+
+        sinon.assert.notCalled(
+          this.SubscriptionUpdater.promises.deleteSubscription
+        )
+      })
     })
 
     describe('with group SSO enabled', function () {