Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DPC-4220] Add max failed attempts to invite verification #2261

Merged
merged 84 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from 76 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
193cf70
Add max tries validation
ashley-weaver Sep 4, 2024
5376a7e
Set tries_count
ashley-weaver Sep 4, 2024
c7798c1
Merge branch 'main' into aweaver/add-invite-limit
ashley-weaver Sep 6, 2024
c71b092
Add requests test
ashley-weaver Sep 6, 2024
d2b1367
Remove method
ashley-weaver Sep 6, 2024
ab6fc22
Update invitations_spec.rb
ashley-weaver Sep 6, 2024
817c29b
Linter
ashley-weaver Sep 6, 2024
336fae4
Fix test
ashley-weaver Sep 6, 2024
806c175
Update invitation.rb
ashley-weaver Sep 6, 2024
588b2b8
Put method back
ashley-weaver Sep 6, 2024
5ad3611
Use failed attempts
ashley-weaver Sep 6, 2024
a077da0
Add migration
ashley-weaver Sep 6, 2024
8d02d7b
Update invitations_spec.rb
ashley-weaver Sep 6, 2024
7f1b5b5
Remove accessor
ashley-weaver Sep 6, 2024
7058332
Save failed attempts count
ashley-weaver Sep 6, 2024
a6fc58d
Update invitations_controller.rb
ashley-weaver Sep 6, 2024
57af60a
Move return for double render error
ashley-weaver Sep 6, 2024
c5a91f5
Use redirect
ashley-weaver Sep 6, 2024
0a75712
Use if-else
ashley-weaver Sep 6, 2024
baae1a2
Use another if-else
ashley-weaver Sep 6, 2024
0c96db7
Remove doubled method
ashley-weaver Sep 6, 2024
4ffb9ee
Move save to invite model
ashley-weaver Sep 9, 2024
c337683
Fix spec and update attr
ashley-weaver Sep 9, 2024
ccedf44
Debug
ashley-weaver Sep 9, 2024
4cdefad
Update invitations_spec.rb
ashley-weaver Sep 9, 2024
d4cb2fc
Debug
ashley-weaver Sep 9, 2024
0d76db1
Update invitations_controller.rb
ashley-weaver Sep 9, 2024
3b86fd4
Refactor attempts_remaining
ashley-weaver Sep 9, 2024
4bf560f
Update invitations_controller.rb
ashley-weaver Sep 9, 2024
a755abf
Refactor add_failed_attempt
ashley-weaver Sep 9, 2024
06f0043
Fix double-render
ashley-weaver Sep 9, 2024
744e0be
Update invitations_controller.rb
ashley-weaver Sep 9, 2024
d2f78fb
Debug
ashley-weaver Sep 9, 2024
b780d42
Debug
ashley-weaver Sep 9, 2024
0e9e5e1
Reload in controller
ashley-weaver Sep 9, 2024
77cf560
Try truthiness
ashley-weaver Sep 9, 2024
3a3d67f
Update attempts_remaining
ashley-weaver Sep 10, 2024
58f370c
Fix typo
ashley-weaver Sep 10, 2024
2257570
Update test
ashley-weaver Sep 10, 2024
1e08f8d
Fix conditional
ashley-weaver Sep 10, 2024
faebc57
Update otp_component.html.erb
ashley-weaver Sep 10, 2024
f891a9a
Remove method
ashley-weaver Sep 10, 2024
39a813e
Debug
ashley-weaver Sep 10, 2024
a15e255
Debug
ashley-weaver Sep 10, 2024
9e5aa39
Update otp_component.html.erb
ashley-weaver Sep 10, 2024
e70ec28
Add failed attempt in test
ashley-weaver Sep 10, 2024
ba837ba
Update error message in test
ashley-weaver Sep 10, 2024
ae808b4
Update otp_component_spec.rb
ashley-weaver Sep 10, 2024
76f486d
Update HTML
ashley-weaver Sep 10, 2024
fdfcba4
Update error
ashley-weaver Sep 10, 2024
76573a1
Update invitations_controller.rb
ashley-weaver Sep 10, 2024
2eea2ae
Revert "Update invitations_controller.rb"
ashley-weaver Sep 10, 2024
78ae571
Revert "Update error"
ashley-weaver Sep 10, 2024
ac7612e
Update otp_component.html.erb
ashley-weaver Sep 10, 2024
886c42f
Update en.yml
ashley-weaver Sep 10, 2024
e688f3b
Move lines around
ashley-weaver Sep 10, 2024
7b7f20f
Update invitations_spec.rb
ashley-weaver Sep 10, 2024
5907bc6
Update error message
ashley-weaver Sep 10, 2024
fabd933
Refactor verify_code
ashley-weaver Sep 10, 2024
e4ae0c6
Reset failed attempts after success
ashley-weaver Sep 10, 2024
4a83507
Update invite methods
ashley-weaver Sep 10, 2024
1e006bd
Refactor add_failed_attempt
ashley-weaver Sep 10, 2024
0ab1037
Update invitations_spec.rb
ashley-weaver Sep 10, 2024
8267755
Update invitation.rb
ashley-weaver Sep 10, 2024
f9274de
Update invitation.rb
ashley-weaver Sep 10, 2024
dfbb862
Merge branch 'main' into aweaver/add-invite-limit
ashley-weaver Sep 10, 2024
de353fc
Update method name
ashley-weaver Sep 10, 2024
f44219a
Remove unneeded saves
ashley-weaver Sep 10, 2024
dcf6e94
Update method name
ashley-weaver Sep 10, 2024
191da78
Merge branch 'aweaver/add-invite-limit' of https://github.com/CMSgov/…
ashley-weaver Sep 10, 2024
f0e7b20
Update otp_component_spec.rb
ashley-weaver Sep 10, 2024
8a91158
Update error handling
ashley-weaver Sep 10, 2024
e5134ce
Add max_tries_exceeded to unacceptable_reason
ashley-weaver Sep 10, 2024
4bd72d2
Add test for max_tries_exceeded
ashley-weaver Sep 10, 2024
beba9ef
Merge branch 'main' into aweaver/add-invite-limit
ashley-weaver Sep 10, 2024
89c94d4
Include AO name in error message
ashley-weaver Sep 10, 2024
f5d2a60
Fix AO name and add preview
ashley-weaver Sep 10, 2024
ab57e2e
Update invitation.rb
ashley-weaver Sep 10, 2024
a387c96
Update invitation_spec.rb
ashley-weaver Sep 10, 2024
56fefe9
Remove request link button, add AO name to preview
ashley-weaver Sep 11, 2024
a38ba49
Update invitations_spec.rb
ashley-weaver Sep 11, 2024
ff68469
Add preview, remove flash
ashley-weaver Sep 11, 2024
a439d0c
Update AO name for preview
ashley-weaver Sep 11, 2024
21d93e3
Merge branch 'main' into aweaver/add-invite-limit
ashley-weaver Sep 13, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<h1><%= t(@status) %></h1>
<div>
<p>
<%=raw t(key=@text, org_name: @org_name) %>
<%=raw t(key=@text, org_name: @org_name, ao_display_name: @ao_display_name) %>
</p>
</div>
<% 'have to put the case statement here, as do not have route helper in ViewComponent'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def initialize(invitation, reason)
super
@invitation = invitation
@org_name = invitation&.provider_organization&.name
@ao_display_name = "#{invitation&.invited_given_name} #{invitation&.invited_family_name}"
ashley-weaver marked this conversation as resolved.
Show resolved Hide resolved
@reason = if AoVerificationService::SERVER_ERRORS.include?(reason)
:server_error
else
Expand Down
18 changes: 14 additions & 4 deletions dpc-portal/app/controllers/invitations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,25 @@ def code
end

def verify_code
unless params[:verification_code] == @invitation.verification_code
@invitation.errors.add(:verification_code, :bad_code, message: 'tbd')
return render(Page::Invitations::OtpComponent.new(@organization, @invitation), status: :bad_request)
end
return handle_failed_attempt unless params[:verification_code] == @invitation.verification_code

@invitation.reset_attempts
session["invitation_status_#{@invitation.id}"] = 'code_verified'
render(Page::Invitations::InvitationLoginComponent.new(@invitation))
end

def handle_failed_attempt
@invitation.increment_failed_attempts
if @invitation.attempts_remaining.zero?
return render(Page::Invitations::BadInvitationComponent.new(@invitation, 'max_tries_exceeded'),
status: :forbidden)
end

flash[:alert] = "Incorrect invite code. You have #{@invitation.attempts_remaining} remaining attempts."
@invitation.errors.add(:verification_code, :bad_code, message: 'Incorrect invite code.')
render(Page::Invitations::OtpComponent.new(@organization, @invitation), status: :bad_request)
end

def confirm_cd
unless session["invitation_status_#{@invitation.id}"] == 'code_verified'
return redirect_to code_organization_invitation_url(@organization, @invitation)
Expand Down
14 changes: 14 additions & 0 deletions dpc-portal/app/models/invitation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,25 @@ class Invitation < ApplicationRecord

STEPS = ['Sign in or create a Login.gov account', 'Confirm your identity', 'Confirm organization registration',
'Finished'].freeze
MAX_ATTEMPTS = 5

def phone_raw=(nbr)
@phone_raw = nbr
self.invited_phone = @phone_raw.tr('^0-9', '')
end

def increment_failed_attempts
update(failed_attempts: failed_attempts + 1) unless failed_attempts == MAX_ATTEMPTS
end

def reset_attempts
update(failed_attempts: 0)
end

def attempts_remaining
MAX_ATTEMPTS - failed_attempts
end

def show_attributes
{ full_name: "#{invited_given_name} #{invited_family_name}",
email: invited_email,
Expand Down Expand Up @@ -81,6 +94,7 @@ def unacceptable_reason # rubocop:disable Metrics/CyclomaticComplexity,Metrics/P
return 'invalid' if cancelled?
return 'accepted' if accepted?
return 'ao_renewed' if renewed? && authorized_official?
return 'max_tries_exceeded' if attempts_remaining.zero?
ashley-weaver marked this conversation as resolved.
Show resolved Hide resolved

if expired? && authorized_official?
'ao_expired'
Expand Down
3 changes: 2 additions & 1 deletion dpc-portal/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ en:
ao_med_sanctions_text: You are listed as an excluded individual by the Office of Inspector General (OIG), which prohibits you from acting as this organization’s Authorized Official. For more information, contact the <a href="https://oig.hhs.gov/about-oig/contact-us/#exclusions">OIG Exclusions Branch</a>.
org_med_sanctions_status: Your organization is in the Medicare Exclusions Database.
org_med_sanctions_text: Your organization is listed as an excluded entity by the Office of Inspector General (OIG), which prohibits it from accessing beneficiary claims data. For more information, contact the <a href="https://oig.hhs.gov/about-oig/contact-us/#exclusions">OIG Exclusions Branch</a>.

max_tries_exceeded_status: Your access is locked due to too many attempts.
max_tries_exceeded_text: "You have made too many attempts. Please request a new invite from %{ao_display_name}."
no_approved_enrollment_status: Your organization is not currently approved by Medicare.
no_approved_enrollment_text: Your organization must have an approved enrollment status with Medicare to access beneficiary claims data. Your organization may need to <a href="https://www.cms.gov/medicare/enrollment-renewal/providers-suppliers/chain-ownership-system-pecos/manage-your-enrollment">manage its enrollment</a> in PECOS, the Medicare enrollment system.
bad_npi_status: Your organization is not currently approved by Medicare.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddFailedAttemptsToInvitations < ActiveRecord::Migration[7.1]
def change
add_column :invitations, :failed_attempts, :integer, default: 0, null: false
end
end
3 changes: 2 additions & 1 deletion dpc-portal/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_08_08_140452) do
ActiveRecord::Schema[7.1].define(version: 2024_09_06_182122) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -81,6 +81,7 @@
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "status"
t.integer "failed_attempts", default: 0, null: false
end

create_table "provider_organizations", force: :cascade do |t|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,12 @@
end

context 'Errors' do
let(:error_msg) { 'Some error message' }
let(:error_msg) { 'Incorrect invite code. You have 4 remaining attempts.' }

before { cd_invite.errors.add(:verification_code, :is_bad, message: error_msg) }
before do
cd_invite.increment_failed_attempts
cd_invite.errors.add(:verification_code, :is_bad, message: error_msg)
end
it 'should have errored verification_code stanza' do
verification_code = <<~HTML
<div class="margin-bottom-4">
Expand Down
4 changes: 4 additions & 0 deletions dpc-portal/spec/models/invitation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,10 @@
invitation = create(:invitation, :ao, created_at: 49.hours.ago, status: :renewed)
expect(invitation.unacceptable_reason).to eq 'ao_renewed'
end
it 'should be max_tries_exceeded if 5 failed attempts' do
invitation = create(:invitation, :ao, failed_attempts: 5)
expect(invitation.unacceptable_reason).to eq 'max_tries_exceeded'
end
end

describe :renew do
Expand Down
14 changes: 14 additions & 0 deletions dpc-portal/spec/requests/invitations_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,20 @@
post "/organizations/#{org.id}/invitations/#{ao_invite.id}/verify_code", params: success_params
expect(response).to redirect_to(organization_invitation_path(org, ao_invite))
end
it 'should be rendered invalid after 5 failed attempts' do
expect(cd_invite.reload.failed_attempts).to eq 0
4.times.each do |i|
post "/organizations/#{org.id}/invitations/#{cd_invite.id}/verify_code", params: fail_params
expect(response).to be_bad_request
expect(flash[:alert]).to eq("Incorrect invite code. You have #{4 - i} remaining attempts.")
expect(cd_invite.reload.failed_attempts).to eq i + 1
end
post "/organizations/#{org.id}/invitations/#{cd_invite.id}/verify_code", params: fail_params
expect(response).to be_forbidden
expect(response.body).to include(I18n.t('verification.max_tries_exceeded_text', ao_display_name: 'Bob Hodges'))
expect(cd_invite.reload.failed_attempts).to eq 5
expect(cd_invite.unacceptable_reason).to eq 'max_tries_exceeded'
end
end
end

Expand Down
Loading