RateLimiterMiddlewareTests.js 6.4 KB

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