diff --git a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js index 704335955e..fae13eec00 100644 --- a/services/web/app/src/Features/Subscription/SubscriptionUpdater.js +++ b/services/web/app/src/Features/Subscription/SubscriptionUpdater.js @@ -11,6 +11,8 @@ const logger = require('@overleaf/logger') const Features = require('../../infrastructure/Features') const UserAuditLogHandler = require('../User/UserAuditLogHandler') const { SSOConfig } = require('../../models/SSOConfig') +const Modules = require('../../infrastructure/Modules') +const Settings = require('@overleaf/settings') /** * Change the admin of the given subscription. @@ -249,6 +251,29 @@ async function _deleteAndReplaceSubscriptionFromRecurly( ) } +async function _notifySupportSubscriptionDeletionSkipped( + subscription, + recurlySubscription, + hasGroupSSOEnabled +) { + const adminUrl = `${Settings.adminUrl + '/admin/user/' + subscription.admin_id}` + const groupUrl = `${Settings.adminUrl + '/admin/group/' + subscription._id}` + let message = `\n**Recurly account:** ${recurlySubscription?.account?.url}` + message += `\n**Group admin:** ${adminUrl}` + message += `\n**Group:** ${groupUrl}` + message += `\n**Managed users enabled:** ${subscription?.managedUsersEnabled}` + message += `\n**SSO enabled:** ${hasGroupSSOEnabled}` + + const data = { + subject: 'Skipped deleting pro group subscription', + inbox: 'support', + tags: 'Group subscription', + message, + } + + await Modules.promises.hooks.fire('sendSupportRequest', data) +} + async function updateSubscriptionFromRecurly( recurlySubscription, subscription, @@ -258,6 +283,18 @@ async function updateSubscriptionFromRecurly( const hasManagedUsersFeature = Features.hasFeature('saas') && subscription?.managedUsersEnabled + let hasGroupSSOEnabled = false + if (subscription?.ssoConfig) { + const ssoConfig = await SSOConfig.findOne({ + _id: subscription.ssoConfig._id || subscription.ssoConfig, + }) + .lean() + .exec() + if (ssoConfig.enabled) { + hasGroupSSOEnabled = true + } + } + // If a payment lapses and if the group is managed or has group SSO, as a temporary measure we need to // make sure that the group continues as-is and no destructive actions are taken. if (hasManagedUsersFeature) { @@ -265,28 +302,39 @@ async function updateSubscriptionFromRecurly( { subscriptionId: subscription._id }, 'expired subscription has managedUsers feature enabled, skipping deletion' ) - } else { - let hasGroupSSOEnabled = false - if (subscription?.ssoConfig) { - const ssoConfig = await SSOConfig.findOne({ - _id: subscription.ssoConfig._id || subscription.ssoConfig, - }) - .lean() - .exec() - if (ssoConfig.enabled) { - hasGroupSSOEnabled = true - } - } - - if (hasGroupSSOEnabled) { + try { + await _notifySupportSubscriptionDeletionSkipped( + subscription, + recurlySubscription, + hasGroupSSOEnabled + ) + } catch (e) { logger.warn( { subscriptionId: subscription._id }, - 'expired subscription has groupSSO feature enabled, skipping deletion' + 'unable to send notification to support that subscription deletion was skipped' ) - } else { - await deleteSubscription(subscription, requesterData) } + } else if (hasGroupSSOEnabled) { + logger.warn( + { subscriptionId: subscription._id }, + 'expired subscription has groupSSO feature enabled, skipping deletion' + ) + try { + await _notifySupportSubscriptionDeletionSkipped( + subscription, + recurlySubscription, + hasGroupSSOEnabled + ) + } catch (e) { + logger.warn( + { subscriptionId: subscription._id }, + 'unable to send notification to support that subscription deletion was skipped' + ) + } + } else { + await deleteSubscription(subscription, requesterData) } + return } const updatedPlanCode = recurlySubscription.plan.plan_code diff --git a/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js b/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js index 3d8212201f..e0e8d666bf 100644 --- a/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js +++ b/services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js @@ -13,6 +13,9 @@ describe('SubscriptionUpdater', function () { plan: { plan_code: this.recurlyPlan.planCode, }, + account: { + url: 'test_url', + }, } this.adminUser = { _id: (this.adminuser_id = '5208dd34438843e2db000007') } @@ -26,6 +29,7 @@ describe('SubscriptionUpdater', function () { save: sinon.stub().resolves(), planCode: 'student_or_something', recurlySubscription_id: 'abc123def456fab789', + managedUsersEnabled: false, } this.user_id = this.adminuser_id @@ -120,6 +124,7 @@ describe('SubscriptionUpdater', function () { }, }, ], + adminUrl: 'test_admin_url', } this.UserFeaturesUpdater = { @@ -177,6 +182,13 @@ describe('SubscriptionUpdater', function () { '../Analytics/AnalyticsManager': this.AnalyticsManager, '../../infrastructure/Features': this.Features, '../User/UserAuditLogHandler': this.UserAuditLogHandler, + '../../infrastructure/Modules': (this.Modules = { + promises: { + hooks: { + fire: sinon.stub().resolves(), + }, + }, + }), }, }) }) @@ -316,6 +328,52 @@ describe('SubscriptionUpdater', function () { {} ) this.SubscriptionModel.deleteOne.should.not.have.been.called + const adminUrl = `${this.Settings.adminUrl + '/admin/user/' + this.subscription.admin_id}` + const groupUrl = `${this.Settings.adminUrl + '/admin/group/' + this.subscription._id}` + let message = `\n**Recurly account:** ${this.recurlySubscription.account?.url}` + message += `\n**Group admin:** ${adminUrl}` + message += `\n**Group:** ${groupUrl}` + message += `\n**Managed users enabled:** false` + message += `\n**SSO enabled:** true` + expect(this.Modules.promises.hooks.fire).to.have.been.calledOnce + expect(this.Modules.promises.hooks.fire).to.have.been.calledWith( + 'sendSupportRequest', + { + subject: 'Skipped deleting pro group subscription', + inbox: 'support', + tags: 'Group subscription', + message, + } + ) + }) + + it('should not remove the subscription when expired if it has managed users is enabled', async function () { + this.Features.hasFeature.withArgs('saas').returns(true) + this.subscription.managedUsersEnabled = true + + this.recurlySubscription.state = 'expired' + await this.SubscriptionUpdater.promises.updateSubscriptionFromRecurly( + this.recurlySubscription, + this.subscription, + {} + ) + this.SubscriptionModel.deleteOne.should.not.have.been.called + const adminUrl = `${this.Settings.adminUrl + '/admin/user/' + this.subscription.admin_id}` + const groupUrl = `${this.Settings.adminUrl + '/admin/group/' + this.subscription._id}` + let message = `\n**Recurly account:** ${this.recurlySubscription.account?.url}` + message += `\n**Group admin:** ${adminUrl}` + message += `\n**Group:** ${groupUrl}` + message += `\n**Managed users enabled:** true` + message += `\n**SSO enabled:** false` + expect(this.Modules.promises.hooks.fire).to.have.been.calledWith( + 'sendSupportRequest', + { + subject: 'Skipped deleting pro group subscription', + inbox: 'support', + tags: 'Group subscription', + message, + } + ) }) it('should update all the users features', async function () {