UserAuditLogHandler.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. const OError = require('@overleaf/o-error')
  2. const logger = require('@overleaf/logger')
  3. const { UserAuditLogEntry } = require('../../models/UserAuditLogEntry')
  4. const { callbackify } = require('util')
  5. const SubscriptionLocator = require('../Subscription/SubscriptionLocator')
  6. function _canHaveNoIpAddressId(operation, info) {
  7. if (operation === 'join-group-subscription') return true
  8. if (operation === 'leave-group-subscription') return true
  9. if (operation === 'must-reset-password-set') return true
  10. if (operation === 'remove-email' && info.script) return true
  11. if (operation === 'release-managed-user' && info.script) return true
  12. if (operation === 'unlink-dropbox' && info.batch) return true
  13. return false
  14. }
  15. function _canHaveNoInitiatorId(operation, info) {
  16. if (operation === 'reset-password') return true
  17. if (operation === 'unlink-sso' && info.providerId === 'collabratec')
  18. return true
  19. if (operation === 'unlink-sso' && info.script === true) return true
  20. if (operation === 'unlink-institution-sso-not-migrated') return true
  21. if (operation === 'remove-email' && info.script) return true
  22. if (operation === 'join-group-subscription') return true
  23. if (operation === 'leave-group-subscription') return true
  24. if (operation === 'must-reset-password-set') return true
  25. if (operation === 'must-reset-password-unset') return true
  26. if (operation === 'account-suspension' && info.script) return true
  27. if (operation === 'release-managed-user' && info.script) return true
  28. }
  29. // events that are visible to managed user admins in Group Audit Logs view
  30. const MANAGED_GROUP_USER_EVENTS = ['login', 'reset-password', 'update-password']
  31. /**
  32. * Add an audit log entry
  33. *
  34. * The entry should include at least the following fields:
  35. *
  36. * - userId: the user on behalf of whom the operation was performed
  37. * - operation: a string identifying the type of operation
  38. * - initiatorId: who performed the operation
  39. * - ipAddress: the IP address of the initiator
  40. * - info: an object detailing what happened
  41. */
  42. async function addEntry(userId, operation, initiatorId, ipAddress, info = {}) {
  43. if (!operation) {
  44. throw new OError('missing operation for audit log', {
  45. initiatorId,
  46. ipAddress,
  47. })
  48. }
  49. if (!ipAddress && !_canHaveNoIpAddressId(operation, info)) {
  50. throw new OError('missing ipAddress for audit log', {
  51. operation,
  52. initiatorId,
  53. })
  54. }
  55. if (!initiatorId && !_canHaveNoInitiatorId(operation, info)) {
  56. throw new OError('missing initiatorId for audit log', {
  57. operation,
  58. ipAddress,
  59. })
  60. }
  61. const entry = {
  62. userId,
  63. operation,
  64. initiatorId,
  65. info,
  66. ipAddress,
  67. }
  68. if (MANAGED_GROUP_USER_EVENTS.includes(operation)) {
  69. try {
  70. const managedSubscription =
  71. await SubscriptionLocator.promises.getUniqueManagedSubscriptionMemberOf(
  72. userId
  73. )
  74. if (managedSubscription) {
  75. entry.managedSubscriptionId = managedSubscription._id
  76. }
  77. } catch (err) {
  78. logger.error({ err, userId }, 'failed to lookup managed subscription')
  79. }
  80. }
  81. await UserAuditLogEntry.create(entry)
  82. }
  83. const UserAuditLogHandler = {
  84. MANAGED_GROUP_USER_EVENTS,
  85. addEntry: callbackify(addEntry),
  86. promises: {
  87. addEntry,
  88. },
  89. }
  90. module.exports = UserAuditLogHandler