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

GitOrigin-RevId: a467a690cf4ee8b1a1081496205559a7f183a1f9
This commit is contained in:
Simon Gardner
2025-10-06 10:22:08 +01:00
committed by Copybot
parent 14356f2675
commit 1bef1bb1d4
2 changed files with 71 additions and 33 deletions

View File

@@ -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,

View File

@@ -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 () {