RateLimiterMiddlewareTests.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* eslint-disable
  2. max-len,
  3. no-return-assign,
  4. */
  5. // TODO: This file was created by bulk-decaffeinate.
  6. // Fix any style issues and re-enable lint.
  7. /*
  8. * decaffeinate suggestions:
  9. * DS102: Remove unnecessary code created because of implicit returns
  10. * DS103: Rewrite code to no longer use __guard__
  11. * DS207: Consider shorter variations of null checks
  12. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  13. */
  14. const SandboxedModule = require('sandboxed-module')
  15. const sinon = require('sinon')
  16. const modulePath = require('path').join(
  17. __dirname,
  18. '../../../../app/src/Features/Security/RateLimiterMiddleware'
  19. )
  20. describe('RateLimiterMiddleware', function () {
  21. beforeEach(function () {
  22. this.SessionManager = {
  23. getLoggedInUserId: () => {
  24. return __guard__(
  25. __guard__(
  26. this.req != null ? this.req.session : undefined,
  27. x1 => x1.user
  28. ),
  29. x => x._id
  30. )
  31. },
  32. }
  33. this.RateLimiterMiddleware = SandboxedModule.require(modulePath, {
  34. requires: {
  35. '@overleaf/settings': (this.settings = {}),
  36. '../../infrastructure/RateLimiter': (this.RateLimiter = {}),
  37. './LoginRateLimiter': {},
  38. '../Authentication/SessionManager': this.SessionManager,
  39. },
  40. })
  41. this.req = { params: {} }
  42. this.res = {
  43. status: sinon.stub(),
  44. write: sinon.stub(),
  45. end: sinon.stub(),
  46. }
  47. return (this.next = sinon.stub())
  48. })
  49. describe('rateLimit', function () {
  50. beforeEach(function () {
  51. this.rateLimiter = this.RateLimiterMiddleware.rateLimit({
  52. endpointName: 'test-endpoint',
  53. params: ['project_id', 'doc_id'],
  54. timeInterval: 42,
  55. maxRequests: 12,
  56. })
  57. return (this.req.params = {
  58. project_id: (this.project_id = 'project-id'),
  59. doc_id: (this.doc_id = 'doc-id'),
  60. })
  61. })
  62. describe('when there is no session', function () {
  63. beforeEach(function () {
  64. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  65. this.req.ip = this.ip = '1.2.3.4'
  66. return this.rateLimiter(this.req, this.res, this.next)
  67. })
  68. it('should call the rate limiter backend with the ip address', function () {
  69. return this.RateLimiter.addCount
  70. .calledWith({
  71. endpointName: 'test-endpoint',
  72. timeInterval: 42,
  73. throttle: 12,
  74. subjectName: `${this.project_id}:${this.doc_id}:${this.ip}`,
  75. })
  76. .should.equal(true)
  77. })
  78. it('should pass on to next()', function () {})
  79. })
  80. describe('when smoke test user', function () {
  81. beforeEach(function () {
  82. this.req.session = {
  83. user: {
  84. _id: (this.user_id = 'smoke-test-user-id'),
  85. },
  86. }
  87. this.settings.smokeTest = { userId: this.user_id }
  88. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  89. return this.rateLimiter(this.req, this.res, this.next)
  90. })
  91. it('should not call the rate limiter backend with the user_id', function () {
  92. this.RateLimiter.addCount
  93. .calledWith({
  94. endpointName: 'test-endpoint',
  95. timeInterval: 42,
  96. throttle: 12,
  97. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  98. })
  99. .should.equal(false)
  100. this.RateLimiter.addCount.callCount.should.equal(0)
  101. })
  102. it('should pass on to next()', function () {
  103. return this.next.called.should.equal(true)
  104. })
  105. })
  106. describe('when under the rate limit with logged in user', function () {
  107. beforeEach(function () {
  108. this.req.session = {
  109. user: {
  110. _id: (this.user_id = 'user-id'),
  111. },
  112. }
  113. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  114. return this.rateLimiter(this.req, this.res, this.next)
  115. })
  116. it('should call the rate limiter backend with the user_id', function () {
  117. return this.RateLimiter.addCount
  118. .calledWith({
  119. endpointName: 'test-endpoint',
  120. timeInterval: 42,
  121. throttle: 12,
  122. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  123. })
  124. .should.equal(true)
  125. })
  126. it('should pass on to next()', function () {
  127. return this.next.called.should.equal(true)
  128. })
  129. })
  130. describe('when under the rate limit with anonymous user', function () {
  131. beforeEach(function () {
  132. this.req.ip = this.ip = '1.2.3.4'
  133. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  134. return this.rateLimiter(this.req, this.res, this.next)
  135. })
  136. it('should call the rate limiter backend with the ip address', function () {
  137. return this.RateLimiter.addCount
  138. .calledWith({
  139. endpointName: 'test-endpoint',
  140. timeInterval: 42,
  141. throttle: 12,
  142. subjectName: `${this.project_id}:${this.doc_id}:${this.ip}`,
  143. })
  144. .should.equal(true)
  145. })
  146. it('should pass on to next()', function () {
  147. return this.next.called.should.equal(true)
  148. })
  149. })
  150. describe('when over the rate limit', function () {
  151. beforeEach(function () {
  152. this.req.session = {
  153. user: {
  154. _id: (this.user_id = 'user-id'),
  155. },
  156. }
  157. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, false)
  158. return this.rateLimiter(this.req, this.res, this.next)
  159. })
  160. it('should return a 429', function () {
  161. this.res.status.calledWith(429).should.equal(true)
  162. return this.res.end.called.should.equal(true)
  163. })
  164. it('should not continue', function () {
  165. return this.next.called.should.equal(false)
  166. })
  167. it('should log a warning', function () {
  168. return this.logger.warn
  169. .calledWith(
  170. {
  171. endpointName: 'test-endpoint',
  172. timeInterval: 42,
  173. throttle: 12,
  174. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  175. },
  176. 'rate limit exceeded'
  177. )
  178. .should.equal(true)
  179. })
  180. })
  181. })
  182. })
  183. function __guard__(value, transform) {
  184. return typeof value !== 'undefined' && value !== null
  185. ? transform(value)
  186. : undefined
  187. }