RateLimiterMiddlewareTests.js 6.4 KB

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