diff options
Diffstat (limited to 'spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb')
-rw-r--r-- | spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb new file mode 100644 index 00000000000..2d595632772 --- /dev/null +++ b/spec/lib/gitlab/metrics/subscribers/rack_attack_spec.rb @@ -0,0 +1,203 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Subscribers::RackAttack, :request_store do + let(:subscriber) { described_class.new } + + describe '.payload' do + context 'when the request store is empty' do + it 'returns empty data' do + expect(described_class.payload).to eql( + rack_attack_redis_count: 0, + rack_attack_redis_duration_s: 0.0 + ) + end + end + + context 'when the request store already has data' do + before do + Gitlab::SafeRequestStore[:rack_attack_instrumentation] = { + rack_attack_redis_count: 10, + rack_attack_redis_duration_s: 9.0 + } + end + + it 'returns the accumulated data' do + expect(described_class.payload).to eql( + rack_attack_redis_count: 10, + rack_attack_redis_duration_s: 9.0 + ) + end + end + end + + describe '#redis' do + it 'accumulates per-request RackAttack cache usage' do + freeze_time do + subscriber.redis( + ActiveSupport::Notifications::Event.new( + 'redis.rack_attack', Time.current, Time.current + 1.second, '1', { operation: 'fetch' } + ) + ) + subscriber.redis( + ActiveSupport::Notifications::Event.new( + 'redis.rack_attack', Time.current, Time.current + 2.seconds, '1', { operation: 'write' } + ) + ) + subscriber.redis( + ActiveSupport::Notifications::Event.new( + 'redis.rack_attack', Time.current, Time.current + 3.seconds, '1', { operation: 'read' } + ) + ) + end + + expect(Gitlab::SafeRequestStore[:rack_attack_instrumentation]).to eql( + rack_attack_redis_count: 3, + rack_attack_redis_duration_s: 6.0 + ) + end + end + + shared_examples 'log into auth logger' do + context 'when matched throttle does not require user information' do + let(:event) do + ActiveSupport::Notifications::Event.new( + event_name, Time.current, Time.current + 2.seconds, '1', request: double( + :request, + ip: '1.2.3.4', + request_method: 'GET', + fullpath: '/api/v4/internal/authorized_keys', + env: { + 'rack.attack.match_type' => match_type, + 'rack.attack.matched' => 'throttle_unauthenticated' + } + ) + ) + end + + it 'logs request information' do + expect(Gitlab::AuthLogger).to receive(:error).with( + include( + message: 'Rack_Attack', + env: match_type, + remote_ip: '1.2.3.4', + request_method: 'GET', + path: '/api/v4/internal/authorized_keys', + matched: 'throttle_unauthenticated' + ) + ) + subscriber.send(match_type, event) + end + end + + context 'when matched throttle requires user information' do + context 'when user not found' do + let(:event) do + ActiveSupport::Notifications::Event.new( + event_name, Time.current, Time.current + 2.seconds, '1', request: double( + :request, + ip: '1.2.3.4', + request_method: 'GET', + fullpath: '/api/v4/internal/authorized_keys', + env: { + 'rack.attack.match_type' => match_type, + 'rack.attack.matched' => 'throttle_authenticated_api', + 'rack.attack.match_discriminator' => 'not_exist_user_id' + } + ) + ) + end + + it 'logs request information and user id' do + expect(Gitlab::AuthLogger).to receive(:error).with( + include( + message: 'Rack_Attack', + env: match_type, + remote_ip: '1.2.3.4', + request_method: 'GET', + path: '/api/v4/internal/authorized_keys', + matched: 'throttle_authenticated_api', + user_id: 'not_exist_user_id' + ) + ) + subscriber.send(match_type, event) + end + end + + context 'when user found' do + let(:user) { create(:user) } + let(:event) do + ActiveSupport::Notifications::Event.new( + event_name, Time.current, Time.current + 2.seconds, '1', request: double( + :request, + ip: '1.2.3.4', + request_method: 'GET', + fullpath: '/api/v4/internal/authorized_keys', + env: { + 'rack.attack.match_type' => match_type, + 'rack.attack.matched' => 'throttle_authenticated_api', + 'rack.attack.match_discriminator' => user.id + } + ) + ) + end + + it 'logs request information and user meta' do + expect(Gitlab::AuthLogger).to receive(:error).with( + include( + message: 'Rack_Attack', + env: match_type, + remote_ip: '1.2.3.4', + request_method: 'GET', + path: '/api/v4/internal/authorized_keys', + matched: 'throttle_authenticated_api', + user_id: user.id, + 'meta.user' => user.username + ) + ) + subscriber.send(match_type, event) + end + end + end + end + + describe '#throttle' do + let(:match_type) { :throttle } + let(:event_name) { 'throttle.rack_attack' } + + it_behaves_like 'log into auth logger' + end + + describe '#blocklist' do + let(:match_type) { :blocklist } + let(:event_name) { 'blocklist.rack_attack' } + + it_behaves_like 'log into auth logger' + end + + describe '#track' do + let(:match_type) { :track } + let(:event_name) { 'track.rack_attack' } + + it_behaves_like 'log into auth logger' + end + + describe '#safelist' do + let(:event) do + ActiveSupport::Notifications::Event.new( + 'safelist.rack_attack', Time.current, Time.current + 2.seconds, '1', request: double( + :request, + env: { + 'rack.attack.matched' => 'throttle_unauthenticated' + } + ) + ) + end + + it 'adds the matched name to safe request store' do + subscriber.safelist(event) + expect(Gitlab::SafeRequestStore[:instrumentation_throttle_safelist]).to eql('throttle_unauthenticated') + end + end +end |