Merge pull request #31536 from overleaf/kc-add-batch-download-audit-log

Add logging for batch downloads

GitOrigin-RevId: b3d03ebd20657b571be0d894bc1d2b335844d1fa
This commit is contained in:
Kate Crichton
2026-02-18 13:56:43 +00:00
committed by Copybot
parent aab1764236
commit 95efb60fb5
2 changed files with 61 additions and 1 deletions

View File

@@ -13,7 +13,7 @@ function getSafeProjectName(project) {
export default {
downloadProject(req, res, next) {
const userId = SessionManager.getSessionUser(req.session)
const userId = SessionManager.getLoggedInUserId(req.session)
const projectId = req.params.Project_id
Metrics.inc('zip-downloads')
DocumentUpdaterHandler.flushProjectToMongo(projectId, function (error) {
@@ -49,6 +49,7 @@ export default {
},
downloadMultipleProjects(req, res, next) {
const userId = SessionManager.getLoggedInUserId(req.session)
const projectIds = req.query.project_ids.split(',')
Metrics.inc('zip-downloads-multiple')
DocumentUpdaterHandler.flushMultipleProjectsToMongo(
@@ -57,6 +58,15 @@ export default {
if (error) {
return next(error)
}
// Log audit entry for each project in the batch
for (const projectId of projectIds) {
ProjectAuditLogHandler.addEntryInBackground(
projectId,
'project-downloaded',
userId,
req.ip
)
}
ProjectZipStreamManager.createZipStreamForMultipleProjects(
projectIds,
function (error, stream) {

View File

@@ -42,6 +42,15 @@ describe('ProjectDownloadsController', function () {
})
)
vi.doMock(
'../../../../app/src/Features/Project/ProjectAuditLogHandler.mjs',
() => ({
default: (ctx.ProjectAuditLogHandler = {
addEntryInBackground: sinon.stub(),
}),
})
)
ctx.ProjectDownloadsController = (await import(modulePath)).default
})
@@ -52,6 +61,13 @@ describe('ProjectDownloadsController', function () {
.stub()
.callsArgWith(1, null, ctx.stream)
ctx.req.params = { Project_id: ctx.project_id }
ctx.req.ip = '192.168.1.1'
ctx.req.session = {
user: {
_id: 'user-id-123',
email: 'user@example.com',
},
}
ctx.project_name = 'project name with accênts and % special characters'
ctx.ProjectGetter.getProject = sinon
.stub()
@@ -104,6 +120,17 @@ describe('ProjectDownloadsController', function () {
it('should record the action via Metrics', function (ctx) {
return ctx.metrics.inc.calledWith('zip-downloads').should.equal(true)
})
it('should add an audit log entry', function (ctx) {
return ctx.ProjectAuditLogHandler.addEntryInBackground
.calledWith(
ctx.project_id,
'project-downloaded',
ctx.req.session.user._id,
ctx.req.ip
)
.should.equal(true)
})
})
describe('downloadMultipleProjects', function () {
@@ -114,6 +141,13 @@ describe('ProjectDownloadsController', function () {
.callsArgWith(1, null, ctx.stream)
ctx.project_ids = ['project-1', 'project-2']
ctx.req.query = { project_ids: ctx.project_ids.join(',') }
ctx.req.ip = '192.168.1.1'
ctx.req.session = {
user: {
_id: 'user-id-123',
email: 'user@example.com',
},
}
ctx.DocumentUpdaterHandler.flushMultipleProjectsToMongo = sinon
.stub()
.callsArgWith(1)
@@ -159,5 +193,21 @@ describe('ProjectDownloadsController', function () {
.calledWith('zip-downloads-multiple')
.should.equal(true)
})
it('should add an audit log entry for each project', function (ctx) {
ctx.ProjectAuditLogHandler.addEntryInBackground.callCount.should.equal(
ctx.project_ids.length
)
for (const projectId of ctx.project_ids) {
ctx.ProjectAuditLogHandler.addEntryInBackground
.calledWith(
projectId,
'project-downloaded',
ctx.req.session.user._id,
ctx.req.ip
)
.should.equal(true)
}
})
})
})