Przeglądaj źródła

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

[web] Update group audit log when user joins

GitOrigin-RevId: 81c0d5003cdde384cb5ff90b57f6aa8b8dae0ee2
Jessica Lawshe 1 rok temu
rodzic
commit
dcd520d7eb

+ 29 - 5
services/web/app/src/Features/Subscription/SubscriptionUpdater.js

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

+ 6 - 1
services/web/app/src/Features/Subscription/TeamInvitesController.mjs

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

+ 8 - 2
services/web/app/src/Features/Subscription/TeamInvitesHandler.js

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

+ 38 - 0
services/web/test/unit/src/Subscription/SubscriptionUpdaterTests.js

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