Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
21b7940
Fix #confirm_no_pending_gpo_profile specs
soniaconnolly May 19, 2023
16ee4d8
Redirect in IdvStepConcern if user is idv_doc_auth throttled
soniaconnolly May 19, 2023
d9a07bb
Add throttle checks to idv_controller
soniaconnolly May 23, 2023
a8b45f3
refactor
soniaconnolly May 23, 2023
094a255
refactor check_throttles_and_redirect
May 23, 2023
e1ba74b
lint
soniaconnolly May 23, 2023
2845a36
Move check_throttled_and_redirect to IdvSession concern and use in Id…
soniaconnolly May 23, 2023
4e1752d
rename throttled_type method arg to throttle_type
soniaconnolly May 24, 2023
317ae83
Add extra analytics arg for :proof_address
soniaconnolly May 24, 2023
6740f48
Let PhoneController check its own rate limiter
soniaconnolly May 26, 2023
4a30faa
Extract RateLimitConcern
soniaconnolly May 26, 2023
5d87f47
Move specs from IdvStepConcernSpec to RateLimitConcernSpec
soniaconnolly May 26, 2023
ec4ebec
Check if throttle and controller match
soniaconnolly May 26, 2023
a2a82cb
Check if throttle and controller match at the beginning
soniaconnolly May 26, 2023
2ecc6c3
Check desktop DocumentCapture rate limit on show
soniaconnolly May 26, 2023
a4599a2
lint
soniaconnolly May 30, 2023
f5c6111
Check for matching controllers only on update
soniaconnolly May 30, 2023
b0f0ef4
Remove DocumentCaptureController#show throttle check
soniaconnolly May 30, 2023
6ceec4f
Move IdvController rate limit check to a before_action
soniaconnolly May 30, 2023
a35215d
Rename method in spec too
soniaconnolly May 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions app/controllers/concerns/idv_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,6 @@ def idv_session
)
end

def idv_attempter_throttled?
Throttle.new(
user: effective_user,
throttle_type: :idv_resolution,
).throttled?
end

def redirect_unless_effective_user
redirect_to root_url if !effective_user
end
Expand Down
2 changes: 2 additions & 0 deletions app/controllers/concerns/idv_step_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ module IdvStepConcern
extend ActiveSupport::Concern

include IdvSession
include RateLimitConcern

included do
before_action :confirm_two_factor_authenticated
before_action :confirm_idv_needed
before_action :confirm_not_rate_limited
before_action :confirm_no_pending_gpo_profile
before_action :confirm_no_pending_in_person_enrollment
end
Expand Down
63 changes: 63 additions & 0 deletions app/controllers/concerns/rate_limit_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
module RateLimitConcern
extend ActiveSupport::Concern

def confirm_not_rate_limited
rate_limited = false
%i[idv_resolution idv_doc_auth proof_address].each do |throttle_type|
next if throttle_and_controller_match(throttle_type) && action_name == 'update'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of checking action_name here, could we use this method like this in controllers to be more explicit:

before_action :check_rate_limited_and_redirect, only: :show

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want it to check on update everywhere except in the controller that uses that particular rate limit.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added LG-9970 to address the issue with update and doomed final attempts when rate_limited in a followup PR.


if rate_limit_redirect!(throttle_type)
rate_limited = true
break
end
end
rate_limited
end

def rate_limit_redirect!(throttle_type)
if idv_attempter_rate_limited?(throttle_type)
track_rate_limited_event(throttle_type)
rate_limited_redirect(throttle_type)
return true
end
end

def track_rate_limited_event(throttle_type)
analytics_args = { throttle_type: throttle_type }
analytics_args[:step_name] = :phone if throttle_type == :proof_address

irs_attempts_api_tracker.idv_verification_rate_limited(throttle_context: 'single-session')
analytics.throttler_rate_limit_triggered(**analytics_args)
end

def rate_limited_redirect(throttle_type)
case throttle_type
when :idv_resolution
redirect_to idv_session_errors_failure_url
when :idv_doc_auth
redirect_to idv_session_errors_throttled_url
when :proof_address
redirect_to idv_phone_errors_failure_url if self.class != Idv::PhoneController
end
end

def throttle_and_controller_match(throttle_type)
case throttle_type
when :idv_resolution
self.instance_of?(Idv::VerifyInfoController) ||
self.instance_of?(Idv::InPerson::VerifyInfoController)
when :idv_doc_auth
self.instance_of?(Idv::DocumentCaptureController) ||
self.instance_of?(Idv::HybridMobile::DocumentCaptureController)
when :proof_address
self.instance_of?(Idv::PhoneController)
end
end

def idv_attempter_rate_limited?(throttle_type)
Throttle.new(
user: effective_user,
throttle_type: throttle_type,
).throttled?
end
end
1 change: 1 addition & 0 deletions app/controllers/idv/document_capture_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class DocumentCaptureController < ApplicationController
include IdvStepConcern
include StepIndicatorConcern
include StepUtilitiesConcern
include RateLimitConcern

before_action :confirm_two_factor_authenticated
before_action :confirm_upload_step_complete
Expand Down
8 changes: 2 additions & 6 deletions app/controllers/idv_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,19 @@ class IdvController < ApplicationController
include IdvSession
include AccountReactivationConcern
include FraudReviewConcern
include RateLimitConcern

before_action :confirm_two_factor_authenticated
before_action :profile_needs_reactivation?, only: [:index]
before_action :handle_fraud
before_action :confirm_not_rate_limited

def index
if decorated_session.requested_more_recent_verification? ||
current_user.reproof_for_irs?(service_provider: current_sp)
verify_identity
elsif active_profile?
redirect_to idv_activated_url
elsif idv_attempter_throttled?
irs_attempts_api_tracker.idv_verification_rate_limited(throttle_context: 'single-session')
analytics.throttler_rate_limit_triggered(
throttle_type: :idv_resolution,
)
redirect_to idv_session_errors_failure_url
else
verify_identity
end
Expand Down
15 changes: 10 additions & 5 deletions spec/controllers/concerns/idv_step_concern_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ def show
end
end

describe 'before_actions' do
it 'includes confirm_not_rate_limited before_action' do
expect(Idv::StepController).to have_actions(
:before,
:confirm_not_rate_limited,
)
end
end

describe '#confirm_idv_needed' do
controller Idv::StepController do
before_action :confirm_idv_needed
Expand Down Expand Up @@ -220,18 +229,14 @@ def show
get :show

expect(response.body).to eq 'Hello'
expect(response).to_not redirect_to idv_in_person_ready_to_verify_url
expect(response).to_not redirect_to idv_gpo_verify_url
expect(response.status).to eq 200
end
end

context 'with pending gpo profile' do
let(:user) { create(:user, :with_pending_gpo_profile, :fully_registered) }

before do
allow(IdentityConfig.store).to receive(:in_person_proofing_enabled).and_return(true)
end

it 'redirects to enter your code page' do
get :show

Expand Down
100 changes: 100 additions & 0 deletions spec/controllers/concerns/rate_limit_concern_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
require 'rails_helper'

describe 'RateLimitConcern' do
let(:user) { create(:user, :fully_registered, email: 'old_email@example.com') }

module Idv
class StepController < ApplicationController
include RateLimitConcern

def show
render plain: 'Hello'
end

def update
render plain: 'Bye'
end
end
end

describe '#confirm_not_rate_limited' do
controller Idv::StepController do
before_action :confirm_not_rate_limited
end

before(:each) do
sign_in(user)
allow(subject).to receive(:current_user).and_return(user)
routes.draw do
get 'show' => 'idv/step#show'
put 'update' => 'idv/step#update'
end
end

context 'user is not throttled' do
let(:user) { create(:user, :fully_registered) }

it 'does not redirect' do
get :show

expect(response.body).to eq 'Hello'
expect(response.status).to eq 200
end
end

context 'with idv_doc_auth throttle (DocumentCapture)' do
it 'redirects to idv_doc_auth throttled error page' do
throttle = Throttle.new(user: user, throttle_type: :idv_doc_auth)
throttle.increment_to_throttled!

get :show

expect(response).to redirect_to idv_session_errors_throttled_url
end
end

context 'with idv_resolution throttle (VerifyInfo)' do
it 'redirects to idv_resolution throttled error page' do
throttle = Throttle.new(user: user, throttle_type: :idv_resolution)
throttle.increment_to_throttled!

get :show

expect(response).to redirect_to idv_session_errors_failure_url
end
end

context 'with proof_address throttle (PhoneStep)' do
before do
throttle = Throttle.new(user: user, throttle_type: :proof_address)
throttle.increment_to_throttled!
end

it 'redirects to proof_address throttled error page' do
get :show

expect(response).to redirect_to idv_phone_errors_failure_url
end

context 'controller and throttle match' do
before do
allow(subject).to receive(:throttle_and_controller_match).
and_return(true)
end

it 'redirects on show' do
get :show

expect(response).to redirect_to idv_phone_errors_failure_url
end

it 'does not redirect on update' do
put :update

expect(response.body).to eq 'Bye'
expect(response.status).to eq 200
end
end
end
end
end
13 changes: 13 additions & 0 deletions spec/controllers/idv/document_capture_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,19 @@
expect(response).to redirect_to(idv_ssn_url)
end
end

context 'user is rate_limited' do
it 'redirects to rate limited page' do
user = create(:user)

Throttle.new(throttle_type: :idv_doc_auth, user: user).increment_to_throttled!
allow(subject).to receive(:current_user).and_return(user)

get :show

expect(response).to redirect_to(idv_session_errors_throttled_url)
end
end
end

describe '#update' do
Expand Down
40 changes: 39 additions & 1 deletion spec/controllers/idv_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
expect(response).to redirect_to(idv_not_verified_url)
end

context 'if number of attempts has been exceeded' do
context 'if number of verify_info attempts has been exceeded' do
before do
user = create(:user)
profile = create(
Expand All @@ -74,6 +74,44 @@
end
end

context 'if number of document capture attempts has been exceeded' do
before do
user = create(:user)
profile = create(
:profile,
user: user,
)
Throttle.new(throttle_type: :idv_doc_auth, user: user).increment_to_throttled!

stub_sign_in(profile.user)
end

it 'redirects to throttled page' do
get :index

expect(response).to redirect_to idv_session_errors_throttled_url
end
end

context 'if number of verify phone attempts has been exceeded' do
before do
user = create(:user)
profile = create(
:profile,
user: user,
)
Throttle.new(throttle_type: :proof_address, user: user).increment_to_throttled!

stub_sign_in(profile.user)
end

it 'redirects to throttled page' do
get :index

expect(response).to redirect_to idv_phone_errors_failure_url
end
end

it 'redirects to account recovery if user has a password reset profile' do
profile = create(:profile, :password_reset)
stub_sign_in(profile.user)
Expand Down