You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
2.8 KiB

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