UserAuditLogHandler.test.mjs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { vi, expect } from 'vitest'
  2. import sinon from 'sinon'
  3. import mongodb from 'mongodb-legacy'
  4. import indirectlyImportModels from '../helpers/indirectlyImportModels.js'
  5. const { UserAuditLogEntry } = indirectlyImportModels(['UserAuditLogEntry'])
  6. const { ObjectId } = mongodb
  7. const MODULE_PATH = '../../../../app/src/Features/User/UserAuditLogHandler'
  8. describe('UserAuditLogHandler', function () {
  9. beforeEach(async function (ctx) {
  10. ctx.userId = new ObjectId()
  11. ctx.initiatorId = new ObjectId()
  12. ctx.subscriptionId = new ObjectId()
  13. ctx.action = {
  14. operation: 'clear-sessions',
  15. initiatorId: ctx.initiatorId,
  16. info: {
  17. sessions: [
  18. {
  19. ip_address: '0:0:0:0',
  20. session_created: '2020-07-15T16:07:57.652Z',
  21. },
  22. ],
  23. },
  24. ip: '0:0:0:0',
  25. }
  26. ctx.UserAuditLogEntryMock = sinon.mock(UserAuditLogEntry)
  27. ctx.getUniqueManagedSubscriptionMemberOfMock = sinon.stub().resolves()
  28. vi.doMock(
  29. '../../../../app/src/Features/Subscription/SubscriptionLocator',
  30. () => ({
  31. default: {
  32. promises: {
  33. getUniqueManagedSubscriptionMemberOf:
  34. ctx.getUniqueManagedSubscriptionMemberOfMock,
  35. },
  36. },
  37. })
  38. )
  39. vi.doMock('../../../../app/src/models/UserAuditLogEntry', () => ({
  40. UserAuditLogEntry,
  41. }))
  42. ctx.UserAuditLogHandler = (await import(MODULE_PATH)).default
  43. })
  44. afterEach(function (ctx) {
  45. ctx.UserAuditLogEntryMock.restore()
  46. })
  47. describe('addEntry', function () {
  48. describe('success', function () {
  49. beforeEach(function (ctx) {
  50. ctx.dbUpdate = ctx.UserAuditLogEntryMock.expects('create')
  51. .chain('exec')
  52. .resolves({ modifiedCount: 1 })
  53. })
  54. it('writes a log', async function (ctx) {
  55. await ctx.UserAuditLogHandler.promises.addEntry(
  56. ctx.userId,
  57. ctx.action.operation,
  58. ctx.action.initiatorId,
  59. ctx.action.ip,
  60. ctx.action.info
  61. )
  62. ctx.UserAuditLogEntryMock.verify()
  63. })
  64. it('updates the log for password reset operation without a initiatorId', async function (ctx) {
  65. await ctx.UserAuditLogHandler.promises.addEntry(
  66. ctx.userId,
  67. 'reset-password',
  68. undefined,
  69. ctx.action.ip,
  70. ctx.action.info
  71. )
  72. ctx.UserAuditLogEntryMock.verify()
  73. })
  74. it('updates the log for a email removal via script', async function (ctx) {
  75. await ctx.UserAuditLogHandler.promises.addEntry(
  76. ctx.userId,
  77. 'remove-email',
  78. undefined,
  79. ctx.action.ip,
  80. {
  81. removedEmail: 'foo',
  82. script: true,
  83. }
  84. )
  85. ctx.UserAuditLogEntryMock.verify()
  86. })
  87. it('updates the log when no ip address or initiatorId is specified for a group join event', async function (ctx) {
  88. await ctx.UserAuditLogHandler.promises.addEntry(
  89. ctx.userId,
  90. 'join-group-subscription',
  91. undefined,
  92. undefined,
  93. {
  94. subscriptionId: 'foo',
  95. }
  96. )
  97. ctx.UserAuditLogEntryMock.verify()
  98. })
  99. it('includes managedSubscriptionId for managed group user events ', async function (ctx) {
  100. await ctx.UserAuditLogHandler.promises.addEntry(
  101. ctx.userId,
  102. 'reset-password',
  103. undefined,
  104. ctx.action.ip
  105. )
  106. ctx.UserAuditLogEntryMock.verify()
  107. expect(ctx.getUniqueManagedSubscriptionMemberOfMock).to.have.been.called
  108. })
  109. it('does not includes managedSubscriptionId for events not in the managed group event list', async function (ctx) {
  110. await ctx.UserAuditLogHandler.promises.addEntry(
  111. ctx.userId,
  112. 'foo',
  113. ctx.action.initiatorId,
  114. ctx.action.ip
  115. )
  116. ctx.UserAuditLogEntryMock.verify()
  117. expect(ctx.getUniqueManagedSubscriptionMemberOfMock).not.to.have.been
  118. .called
  119. })
  120. })
  121. describe('errors', function () {
  122. describe('missing parameters', function () {
  123. it('throws an error when no operation', async function (ctx) {
  124. await expect(
  125. ctx.UserAuditLogHandler.promises.addEntry(
  126. ctx.userId,
  127. undefined,
  128. ctx.action.initiatorId,
  129. ctx.action.ip,
  130. ctx.action.info
  131. )
  132. ).to.be.rejected
  133. })
  134. it('throws an error when no IP and not excempt', async function (ctx) {
  135. await expect(
  136. ctx.UserAuditLogHandler.promises.addEntry(
  137. ctx.userId,
  138. ctx.action.operation,
  139. ctx.action.initiatorId,
  140. undefined,
  141. ctx.action.info
  142. )
  143. ).to.be.rejected
  144. })
  145. it('throws an error when no initiatorId and not a password reset operation', async function (ctx) {
  146. await expect(
  147. ctx.UserAuditLogHandler.promises.addEntry(
  148. ctx.userId,
  149. ctx.action.operation,
  150. undefined,
  151. ctx.action.ip,
  152. ctx.action.info
  153. )
  154. ).to.be.rejected
  155. })
  156. it('throws an error when remove-email is not from a script, but has no initiatorId', async function (ctx) {
  157. await expect(
  158. ctx.UserAuditLogHandler.promises.addEntry(
  159. ctx.userId,
  160. 'remove-email',
  161. undefined,
  162. ctx.action.ip,
  163. {
  164. removedEmail: 'foo',
  165. }
  166. )
  167. ).to.be.rejected
  168. })
  169. })
  170. })
  171. })
  172. })