2020-07-21 10:29:08 -04:00
|
|
|
const OError = require('@overleaf/o-error')
|
|
|
|
const { User } = require('../../models/User')
|
2020-10-06 09:50:07 -04:00
|
|
|
const { callbackify } = require('util')
|
2020-07-21 10:29:08 -04:00
|
|
|
|
|
|
|
const MAX_AUDIT_LOG_ENTRIES = 200
|
|
|
|
|
2020-10-06 09:50:07 -04:00
|
|
|
function _canHaveNoInitiatorId(operation, info) {
|
|
|
|
if (operation === 'reset-password') return true
|
|
|
|
if (operation === 'unlink-sso' && info.providerId === 'collabratec')
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-07-21 10:29:08 -04:00
|
|
|
/**
|
|
|
|
* Add an audit log entry
|
|
|
|
*
|
|
|
|
* The entry should include at least the following fields:
|
|
|
|
*
|
2020-08-10 09:58:01 -04:00
|
|
|
* - userId: the user on behalf of whom the operation was performed
|
2020-07-21 10:29:08 -04:00
|
|
|
* - operation: a string identifying the type of operation
|
|
|
|
* - initiatorId: who performed the operation
|
2020-08-10 09:58:01 -04:00
|
|
|
* - ipAddress: the IP address of the initiator
|
2020-07-21 10:29:08 -04:00
|
|
|
* - info: an object detailing what happened
|
|
|
|
*/
|
|
|
|
async function addEntry(userId, operation, initiatorId, ipAddress, info = {}) {
|
2020-08-12 10:19:33 -04:00
|
|
|
if (!operation || !ipAddress)
|
|
|
|
throw new OError('missing required audit log data', {
|
|
|
|
operation,
|
|
|
|
initiatorId,
|
2021-04-27 03:52:58 -04:00
|
|
|
ipAddress,
|
2020-08-12 10:19:33 -04:00
|
|
|
})
|
|
|
|
|
2020-10-06 09:50:07 -04:00
|
|
|
if (!initiatorId && !_canHaveNoInitiatorId(operation, info)) {
|
2020-08-12 10:19:33 -04:00
|
|
|
throw new OError('missing initiatorId for audit log', {
|
|
|
|
operation,
|
2021-04-27 03:52:58 -04:00
|
|
|
ipAddress,
|
2020-08-12 10:19:33 -04:00
|
|
|
})
|
2020-10-06 09:50:07 -04:00
|
|
|
}
|
2020-08-12 10:19:33 -04:00
|
|
|
|
2020-07-21 10:29:08 -04:00
|
|
|
const timestamp = new Date()
|
|
|
|
const entry = {
|
|
|
|
operation,
|
|
|
|
initiatorId,
|
|
|
|
info,
|
|
|
|
ipAddress,
|
2021-04-27 03:52:58 -04:00
|
|
|
timestamp,
|
2020-07-21 10:29:08 -04:00
|
|
|
}
|
|
|
|
const result = await User.updateOne(
|
|
|
|
{ _id: userId },
|
|
|
|
{
|
|
|
|
$push: {
|
2021-04-27 03:52:58 -04:00
|
|
|
auditLog: { $each: [entry], $slice: -MAX_AUDIT_LOG_ENTRIES },
|
|
|
|
},
|
2020-07-21 10:29:08 -04:00
|
|
|
}
|
|
|
|
).exec()
|
|
|
|
if (result.nModified === 0) {
|
2020-08-11 05:28:29 -04:00
|
|
|
throw new OError('user not found', { userId })
|
2020-07-21 10:29:08 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const UserAuditLogHandler = {
|
2020-10-06 09:50:07 -04:00
|
|
|
addEntry: callbackify(addEntry),
|
2020-07-21 10:29:08 -04:00
|
|
|
promises: {
|
2021-04-27 03:52:58 -04:00
|
|
|
addEntry,
|
|
|
|
},
|
2020-07-21 10:29:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = UserAuditLogHandler
|