RateLimiterMiddlewareTests.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. const SandboxedModule = require('sandboxed-module')
  2. const sinon = require('sinon')
  3. const modulePath = require('path').join(
  4. __dirname,
  5. '../../../../app/src/Features/Security/RateLimiterMiddleware'
  6. )
  7. describe('RateLimiterMiddleware', function () {
  8. beforeEach(function () {
  9. this.SessionManager = {
  10. getLoggedInUserId: () => this.req.session?.user?._id,
  11. }
  12. this.RateLimiterMiddleware = SandboxedModule.require(modulePath, {
  13. requires: {
  14. '@overleaf/settings': (this.settings = {}),
  15. './LoginRateLimiter': {},
  16. '../Authentication/SessionManager': this.SessionManager,
  17. },
  18. })
  19. this.req = { params: {} }
  20. this.res = {
  21. status: sinon.stub(),
  22. write: sinon.stub(),
  23. end: sinon.stub(),
  24. }
  25. this.next = sinon.stub()
  26. })
  27. describe('rateLimit', function () {
  28. beforeEach(function () {
  29. this.projectId = 'project-id'
  30. this.docId = 'doc-id'
  31. this.rateLimiter = {
  32. consume: sinon.stub().resolves({ remainingPoints: 2 }),
  33. }
  34. this.middleware = this.RateLimiterMiddleware.rateLimit(this.rateLimiter, {
  35. params: ['projectId', 'docId'],
  36. })
  37. this.req.params = { projectId: this.projectId, docId: this.docId }
  38. })
  39. describe('when there is no session', function () {
  40. beforeEach(function (done) {
  41. this.req.ip = this.ip = '1.2.3.4'
  42. this.middleware(this.req, this.res, () => {
  43. done()
  44. })
  45. })
  46. it('should call the rate limiter with the ip address', function () {
  47. this.rateLimiter.consume.should.have.been.calledWith(
  48. `${this.projectId}:${this.docId}:${this.ip}`
  49. )
  50. })
  51. })
  52. describe('when smoke test user', function () {
  53. beforeEach(function (done) {
  54. this.userId = 'smoke-test-user-id'
  55. this.req.session = {
  56. user: { _id: this.userId },
  57. }
  58. this.settings.smokeTest = { userId: this.userId }
  59. this.middleware(this.req, this.res, () => {
  60. done()
  61. })
  62. })
  63. it('should not call the rate limiter', function () {
  64. this.rateLimiter.consume.should.not.have.been.called
  65. })
  66. })
  67. describe('when under the rate limit with logged in user', function () {
  68. beforeEach(function (done) {
  69. this.userId = 'user-id'
  70. this.req.session = {
  71. user: { _id: this.userId },
  72. }
  73. this.middleware(this.req, this.res, () => {
  74. done()
  75. })
  76. })
  77. it('should call the rate limiter backend with the userId', function () {
  78. this.rateLimiter.consume.should.have.been.calledWith(
  79. `${this.projectId}:${this.docId}:${this.userId}`
  80. )
  81. })
  82. })
  83. describe('when under the rate limit with anonymous user', function () {
  84. beforeEach(function (done) {
  85. this.req.ip = '1.2.3.4'
  86. this.middleware(this.req, this.res, () => {
  87. done()
  88. })
  89. })
  90. it('should call the rate limiter backend with the ip address', function () {
  91. this.rateLimiter.consume.should.have.been.calledWith(
  92. `${this.projectId}:${this.docId}:${this.req.ip}`
  93. )
  94. })
  95. })
  96. describe('when over the rate limit', function () {
  97. beforeEach(function (done) {
  98. this.userId = 'user-id'
  99. this.req.session = {
  100. user: { _id: this.userId },
  101. }
  102. this.res.end.callsFake(() => {
  103. done()
  104. })
  105. this.rateLimiter.consume.rejects({ remainingPoints: 0 })
  106. this.middleware(this.req, this.res, this.next)
  107. })
  108. it('should return a 429', function () {
  109. this.res.status.should.have.been.calledWith(429)
  110. })
  111. it('should not continue', function () {
  112. this.next.should.not.have.been.called
  113. })
  114. })
  115. })
  116. })