Merge pull request #25360 from overleaf/jel-group-audit-log-join

[web] Update group audit log when user joins

GitOrigin-RevId: 81c0d5003cdde384cb5ff90b57f6aa8b8dae0ee2
This commit is contained in:
Jessica Lawshe
2025-05-27 09:39:02 -05:00
committed by Copybot
parent 881db9b472
commit dcd520d7eb
4 changed files with 81 additions and 8 deletions

View File

@@ -12,6 +12,8 @@ const Features = require('../../infrastructure/Features')
const UserAuditLogHandler = require('../User/UserAuditLogHandler')
const AccountMappingHelper = require('../Analytics/AccountMappingHelper')
const { SSOConfig } = require('../../models/SSOConfig')
const mongoose = require('../../infrastructure/Mongoose')
const Modules = require('../../infrastructure/Modules')
/**
* @typedef {import('../../../../types/subscription/dashboard/subscription').Subscription} Subscription
@@ -65,7 +67,9 @@ async function syncSubscription(
)
}
async function addUserToGroup(subscriptionId, userId) {
async function addUserToGroup(subscriptionId, userId, auditLog) {
const session = await mongoose.startSession()
await UserAuditLogHandler.promises.addEntry(
userId,
'join-group-subscription',
@@ -73,10 +77,30 @@ async function addUserToGroup(subscriptionId, userId) {
undefined,
{ subscriptionId }
)
await Subscription.updateOne(
{ _id: subscriptionId },
{ $addToSet: { member_ids: userId } }
).exec()
try {
await session.withTransaction(async () => {
await Subscription.updateOne(
{ _id: subscriptionId },
{ $addToSet: { member_ids: userId } },
{ session }
).exec()
await Modules.promises.hooks.fire(
'addGroupAuditLogEntry',
{
initiatorId: auditLog?.initiatorId,
ipAddress: auditLog?.ipAddress,
groupId: subscriptionId,
operation: 'join-group',
},
session
)
})
} finally {
await session.endSession()
}
await FeaturesUpdater.promises.refreshFeatures(userId, 'add-to-group')
await _sendUserGroupPlanCodeUserProperty(userId)
await _sendSubscriptionEvent(

View File

@@ -36,10 +36,15 @@ async function createInvite(req, res, next) {
}
try {
const auditLog = {
initiatorId: teamManagerId,
ipAddress: req.ip,
}
const invitedUserData = await TeamInvitesHandler.promises.createInvite(
teamManagerId,
subscription,
email
email,
auditLog
)
return res.json({ user: invitedUserData })
} catch (err) {

View File

@@ -70,7 +70,11 @@ async function acceptInvite(token, userId, auditLog) {
throw new Errors.NotFoundError('invite not found')
}
await SubscriptionUpdater.promises.addUserToGroup(subscription._id, userId)
await SubscriptionUpdater.promises.addUserToGroup(
subscription._id,
userId,
auditLog
)
if (subscription.managedUsersEnabled) {
await Modules.promises.hooks.fire(
@@ -147,9 +151,11 @@ async function _createInvite(subscription, email, inviter) {
emailData => emailData.email === email
)
if (isInvitingSelf) {
const auditLog = { initiatorId: inviter._id }
await SubscriptionUpdater.promises.addUserToGroup(
subscription._id,
inviter._id
inviter._id,
auditLog
)
// legacy: remove any invite that might have been created in the past

View File

@@ -120,6 +120,18 @@ describe('SubscriptionUpdater', function () {
},
},
],
mongo: {
options: {
appname: 'web',
maxPoolSize: 100,
serverSelectionTimeoutMS: 60000,
socketTimeoutMS: 60000,
monitorCommands: true,
family: 4,
},
url: 'mongodb://mongo/test-overleaf',
hasSecondaries: false,
},
}
this.UserFeaturesUpdater = {
@@ -181,6 +193,13 @@ describe('SubscriptionUpdater', function () {
}),
'../../infrastructure/Features': this.Features,
'../User/UserAuditLogHandler': this.UserAuditLogHandler,
'../../infrastructure/Modules': (this.Modules = {
promises: {
hooks: {
fire: sinon.stub().resolves(),
},
},
}),
},
})
})
@@ -486,6 +505,7 @@ describe('SubscriptionUpdater', function () {
this.SubscriptionModel.updateOne
.calledWith(searchOps, insertOperation)
.should.equal(true)
expect(this.SubscriptionModel.updateOne.lastCall.args[2].session).to.exist
sinon.assert.calledWith(
this.AnalyticsManager.recordEventForUserInBackground,
this.otherUserId,
@@ -571,6 +591,24 @@ describe('SubscriptionUpdater', function () {
}
)
})
it('should add an entry to the group audit log when joining a group', async function () {
await this.SubscriptionUpdater.promises.addUserToGroup(
this.subscription._id,
this.otherUserId,
{ ipAddress: '0:0:0:0', initiatorId: 'user123' }
)
expect(this.Modules.promises.hooks.fire).to.have.been.calledWith(
'addGroupAuditLogEntry',
{
groupId: this.subscription._id,
initiatorId: 'user123',
ipAddress: '0:0:0:0',
operation: 'join-group',
}
)
})
})
describe('removeUserFromGroup', function () {