rack_attack_spec.rb 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. # frozen_string_literal: true
  2. require 'rails_helper'
  3. describe Rack::Attack, type: :request do
  4. def app
  5. Rails.application
  6. end
  7. shared_examples 'throttled endpoint' do
  8. before do
  9. # Rack::Attack periods are not rolling, so avoid flaky tests by setting the time in a way
  10. # to avoid crossing period boundaries.
  11. # The code Rack::Attack uses to set periods is the following:
  12. # https://github.com/rack/rack-attack/blob/v6.6.1/lib/rack/attack/cache.rb#L64-L66
  13. # So we want to minimize `Time.now.to_i % period`
  14. travel_to Time.zone.at((Time.now.to_i / period.seconds).to_i * period.seconds)
  15. end
  16. context 'when the number of requests is lower than the limit' do
  17. it 'does not change the request status' do
  18. limit.times do
  19. request.call
  20. expect(response).to_not have_http_status(429)
  21. end
  22. end
  23. end
  24. context 'when the number of requests is higher than the limit' do
  25. it 'returns http too many requests after limit and returns to normal status after period' do
  26. (limit * 2).times do |i|
  27. request.call
  28. expect(response).to have_http_status(429) if i > limit
  29. end
  30. travel period
  31. request.call
  32. expect(response).to_not have_http_status(429)
  33. end
  34. end
  35. end
  36. let(:remote_ip) { '1.2.3.5' }
  37. describe 'throttle excessive sign-up requests by IP address' do
  38. context 'when accessed through the website' do
  39. let(:limit) { 25 }
  40. let(:period) { 5.minutes }
  41. let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
  42. context 'with exact path' do
  43. let(:path) { '/auth' }
  44. it_behaves_like 'throttled endpoint'
  45. end
  46. context 'with path with format' do
  47. let(:path) { '/auth.html' }
  48. it_behaves_like 'throttled endpoint'
  49. end
  50. end
  51. context 'when accessed through the API' do
  52. let(:limit) { 5 }
  53. let(:period) { 30.minutes }
  54. let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
  55. context 'with exact path' do
  56. let(:path) { '/api/v1/accounts' }
  57. it_behaves_like 'throttled endpoint'
  58. end
  59. context 'with path with format' do
  60. let(:path) { '/api/v1/accounts.json' }
  61. it 'returns http not found' do
  62. request.call
  63. expect(response).to have_http_status(404)
  64. end
  65. end
  66. end
  67. end
  68. describe 'throttle excessive sign-in requests by IP address' do
  69. let(:limit) { 25 }
  70. let(:period) { 5.minutes }
  71. let(:request) { -> { post path, headers: { 'REMOTE_ADDR' => remote_ip } } }
  72. context 'with exact path' do
  73. let(:path) { '/auth/sign_in' }
  74. it_behaves_like 'throttled endpoint'
  75. end
  76. context 'with path with format' do
  77. let(:path) { '/auth/sign_in.html' }
  78. it_behaves_like 'throttled endpoint'
  79. end
  80. end
  81. end