RateLimiterMiddlewareTests.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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.AuthenticationController = {
  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. 'settings-sharelatex': (this.settings = {}),
  36. '../../infrastructure/RateLimiter': (this.RateLimiter = {}),
  37. './LoginRateLimiter': {},
  38. '../Authentication/AuthenticationController': this
  39. .AuthenticationController,
  40. },
  41. })
  42. this.req = { params: {} }
  43. this.res = {
  44. status: sinon.stub(),
  45. write: sinon.stub(),
  46. end: sinon.stub(),
  47. }
  48. return (this.next = sinon.stub())
  49. })
  50. describe('rateLimit', function () {
  51. beforeEach(function () {
  52. this.rateLimiter = this.RateLimiterMiddleware.rateLimit({
  53. endpointName: 'test-endpoint',
  54. params: ['project_id', 'doc_id'],
  55. timeInterval: 42,
  56. maxRequests: 12,
  57. })
  58. return (this.req.params = {
  59. project_id: (this.project_id = 'project-id'),
  60. doc_id: (this.doc_id = 'doc-id'),
  61. })
  62. })
  63. describe('when there is no session', function () {
  64. beforeEach(function () {
  65. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  66. this.req.ip = this.ip = '1.2.3.4'
  67. return this.rateLimiter(this.req, this.res, this.next)
  68. })
  69. it('should call the rate limiter backend with the ip address', function () {
  70. return this.RateLimiter.addCount
  71. .calledWith({
  72. endpointName: 'test-endpoint',
  73. timeInterval: 42,
  74. throttle: 12,
  75. subjectName: `${this.project_id}:${this.doc_id}:${this.ip}`,
  76. })
  77. .should.equal(true)
  78. })
  79. it('should pass on to next()', function () {})
  80. })
  81. describe('when smoke test user', function () {
  82. beforeEach(function () {
  83. this.req.session = {
  84. user: {
  85. _id: (this.user_id = 'smoke-test-user-id'),
  86. },
  87. }
  88. this.settings.smokeTest = { userId: this.user_id }
  89. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  90. return this.rateLimiter(this.req, this.res, this.next)
  91. })
  92. it('should not call the rate limiter backend with the user_id', function () {
  93. this.RateLimiter.addCount
  94. .calledWith({
  95. endpointName: 'test-endpoint',
  96. timeInterval: 42,
  97. throttle: 12,
  98. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  99. })
  100. .should.equal(false)
  101. this.RateLimiter.addCount.callCount.should.equal(0)
  102. })
  103. it('should pass on to next()', function () {
  104. return this.next.called.should.equal(true)
  105. })
  106. })
  107. describe('when under the rate limit with logged in user', function () {
  108. beforeEach(function () {
  109. this.req.session = {
  110. user: {
  111. _id: (this.user_id = 'user-id'),
  112. },
  113. }
  114. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  115. return this.rateLimiter(this.req, this.res, this.next)
  116. })
  117. it('should call the rate limiter backend with the user_id', function () {
  118. return this.RateLimiter.addCount
  119. .calledWith({
  120. endpointName: 'test-endpoint',
  121. timeInterval: 42,
  122. throttle: 12,
  123. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  124. })
  125. .should.equal(true)
  126. })
  127. it('should pass on to next()', function () {
  128. return this.next.called.should.equal(true)
  129. })
  130. })
  131. describe('when under the rate limit with anonymous user', function () {
  132. beforeEach(function () {
  133. this.req.ip = this.ip = '1.2.3.4'
  134. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, true)
  135. return this.rateLimiter(this.req, this.res, this.next)
  136. })
  137. it('should call the rate limiter backend with the ip address', function () {
  138. return this.RateLimiter.addCount
  139. .calledWith({
  140. endpointName: 'test-endpoint',
  141. timeInterval: 42,
  142. throttle: 12,
  143. subjectName: `${this.project_id}:${this.doc_id}:${this.ip}`,
  144. })
  145. .should.equal(true)
  146. })
  147. it('should pass on to next()', function () {
  148. return this.next.called.should.equal(true)
  149. })
  150. })
  151. describe('when over the rate limit', function () {
  152. beforeEach(function () {
  153. this.req.session = {
  154. user: {
  155. _id: (this.user_id = 'user-id'),
  156. },
  157. }
  158. this.RateLimiter.addCount = sinon.stub().callsArgWith(1, null, false)
  159. return this.rateLimiter(this.req, this.res, this.next)
  160. })
  161. it('should return a 429', function () {
  162. this.res.status.calledWith(429).should.equal(true)
  163. return this.res.end.called.should.equal(true)
  164. })
  165. it('should not continue', function () {
  166. return this.next.called.should.equal(false)
  167. })
  168. it('should log a warning', function () {
  169. return this.logger.warn
  170. .calledWith(
  171. {
  172. endpointName: 'test-endpoint',
  173. timeInterval: 42,
  174. throttle: 12,
  175. subjectName: `${this.project_id}:${this.doc_id}:${this.user_id}`,
  176. },
  177. 'rate limit exceeded'
  178. )
  179. .should.equal(true)
  180. })
  181. })
  182. })
  183. })
  184. function __guard__(value, transform) {
  185. return typeof value !== 'undefined' && value !== null
  186. ? transform(value)
  187. : undefined
  188. }