diff --git a/Gemfile b/Gemfile
index 06adfaf4e08..a09f4f2f93e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,7 +11,10 @@ gem 'autoprefixer-rails', '~> 10.0'
gem 'aws-sdk-kms', '~> 1.4'
gem 'aws-sdk-lambda'
gem 'aws-sdk-ses', '~> 1.6'
+gem 'aws-sdk-sqs'
gem 'base32-crockford'
+gem 'daemons', '~> 1.3'
+gem 'delayed_job_active_record', '~> 4.1'
gem 'device_detector'
gem 'devise', '~> 4.7.2'
gem 'dotiw', '>= 4.0.1'
@@ -32,6 +35,7 @@ gem 'jwt'
gem 'local_time'
gem 'lograge', '>= 0.11.2'
gem 'maxminddb'
+gem 'mimemagic', '0.3.5', github: 'mimemagicrb/mimemagic', ref: '40dd02bb6b442535f97c35326c0383bc67'
gem 'net-sftp'
gem 'newrelic_rpm'
gem 'pg'
@@ -120,5 +124,5 @@ end
group :production do
gem 'aamva', github: '18F/identity-aamva-api-client-gem', tag: 'v4.1.0'
- gem 'lexisnexis', github: '18F/identity-lexisnexis-api-client-gem', tag: 'v3.1.0'
+ gem 'lexisnexis', github: '18F/identity-lexisnexis-api-client-gem', tag: 'v3.1.1'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index e3e5c55de59..0b7deaee845 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -29,10 +29,10 @@ GIT
GIT
remote: https://github.com/18F/identity-idp-functions.git
- revision: 051a8c3dca143b215838176a07b1f0545ffec0a2
- ref: 051a8c3dca143b215838176a07b1f0545ffec0a2
+ revision: d9241bdfea85a76c170e456a89ec6601549f4c4a
+ ref: d9241bdfea85a76c170e456a89ec6601549f4c4a
specs:
- identity-idp-functions (0.15.0)
+ identity-idp-functions (0.15.2)
aamva (>= 4.0.0)
aws-sdk-s3 (>= 1.73)
aws-sdk-ssm (>= 1.55)
@@ -41,10 +41,10 @@ GIT
GIT
remote: https://github.com/18F/identity-lexisnexis-api-client-gem.git
- revision: d6e73358ab899e8740e0008aff0e03e26bd4eb56
- tag: v3.1.0
+ revision: 0e22ac2518a724b63a928feb68197b203ea47660
+ tag: v3.1.1
specs:
- lexisnexis (3.1.0)
+ lexisnexis (3.1.1)
activesupport
faraday
@@ -94,6 +94,13 @@ GIT
aws-sdk-pinpointsmsvoice
i18n
+GIT
+ remote: https://github.com/mimemagicrb/mimemagic.git
+ revision: 40dd02bb6b442535f97c35326c0383bc67146ac4
+ ref: 40dd02bb6b442535f97c35326c0383bc67
+ specs:
+ mimemagic (0.3.5)
+
GEM
remote: https://rubygems.org/
specs:
@@ -199,6 +206,9 @@ GEM
aws-sdk-ses (1.36.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
+ aws-sdk-sqs (1.35.0)
+ aws-sdk-core (~> 3, >= 3.109.0)
+ aws-sigv4 (~> 1.1)
aws-sdk-ssm (1.103.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
@@ -269,7 +279,13 @@ GEM
crass (1.0.6)
css_parser (1.7.1)
addressable
+ daemons (1.3.1)
debug_inspector (0.0.3)
+ delayed_job (4.1.9)
+ activesupport (>= 3.0, < 6.2)
+ delayed_job_active_record (4.1.5)
+ activerecord (>= 3.0, < 6.2)
+ delayed_job (>= 3.0, < 5)
derailed_benchmarks (1.8.1)
benchmark-ips (~> 2)
get_process_mem (~> 0)
@@ -418,7 +434,6 @@ GEM
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2021.0225)
- mimemagic (0.3.5)
mini_histogram (0.3.1)
mini_mime (1.0.2)
mini_portile2 (2.5.0)
@@ -432,7 +447,7 @@ GEM
net-ssh (5.2.0)
newrelic_rpm (6.14.0)
nio4r (2.5.5)
- nokogiri (1.11.1)
+ nokogiri (1.11.2)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
notiffany (0.1.3)
@@ -738,6 +753,7 @@ DEPENDENCIES
aws-sdk-kms (~> 1.4)
aws-sdk-lambda
aws-sdk-ses (~> 1.6)
+ aws-sdk-sqs
axe-matchers (~> 2.6.0)
base32-crockford
better_errors (>= 2.5.1)
@@ -748,6 +764,8 @@ DEPENDENCIES
capybara-screenshot (>= 1.0.23)
capybara-selenium (>= 0.0.6)
codeclimate-test-reporter
+ daemons (~> 1.3)
+ delayed_job_active_record (~> 4.1)
derailed_benchmarks (~> 1.8)
device_detector
devise (~> 4.7.2)
@@ -778,6 +796,7 @@ DEPENDENCIES
local_time
lograge (>= 0.11.2)
maxminddb
+ mimemagic (= 0.3.5)!
net-sftp
newrelic_rpm
nokogiri (~> 1.11.0)
diff --git a/app/assets/stylesheets/components/_btn.scss b/app/assets/stylesheets/components/_btn.scss
index 054a7132a79..352a4d16bc5 100644
--- a/app/assets/stylesheets/components/_btn.scss
+++ b/app/assets/stylesheets/components/_btn.scss
@@ -111,3 +111,30 @@
text-decoration: none;
}
}
+
+.usa-button.usa-button--unstyled:visited {
+ // Temporary: Links in the IdP do not currently conform to the design system and instead retain
+ // their color even if visited. Part of the work of LG-3877 should be to remove these styles, and
+ // instead allow both unstyled buttons and links inherit the default design system visited color.
+ // Alternatively, consider removing unstyled button classes from links, since the intention of an
+ // unstyled button is to take the visual appearance of a link.
+ color: $link-color;
+}
+
+.usa-button.usa-button--unstyled:hover,
+.usa-button.usa-button--hover.usa-button--unstyled {
+ // Temporary: Links in the IdP do not currently conform to the design system and instead retain
+ // their color while hovered. Part of the work of LG-3877 should be to remove these styles, and
+ // instead allow both unstyled buttons and links inherit the default design system hover color.
+ color: $link-color;
+}
+
+.usa-button--unstyled {
+ &:hover,
+ &:active {
+ // Temporary: These styles should be ported upstream to the design system, optionally as part of
+ // future reconciliation effort with uswds/uswds#4077.
+ -moz-osx-font-smoothing: inherit;
+ -webkit-font-smoothing: inherit;
+ }
+}
diff --git a/app/controllers/account_reset/recover_controller.rb b/app/controllers/account_reset/recover_controller.rb
index 76ac54170af..7548e115f0c 100644
--- a/app/controllers/account_reset/recover_controller.rb
+++ b/app/controllers/account_reset/recover_controller.rb
@@ -22,7 +22,7 @@ def send_notifications
current_user.confirmed_email_addresses.each do |email_address|
UserMailer.confirm_email_and_reverify(current_user,
email_address,
- current_user.account_recovery_request).deliver_later
+ current_user.account_recovery_request).deliver_now
end
end
diff --git a/app/controllers/concerns/two_factor_authenticatable_methods.rb b/app/controllers/concerns/two_factor_authenticatable_methods.rb
index 045ecad7c80..336ce5a79d8 100644
--- a/app/controllers/concerns/two_factor_authenticatable_methods.rb
+++ b/app/controllers/concerns/two_factor_authenticatable_methods.rb
@@ -178,7 +178,7 @@ def send_phone_added_email
event = create_user_event_with_disavowal(:phone_added, current_user)
current_user.confirmed_email_addresses.each do |email_address|
UserMailer.phone_added(current_user, email_address, disavowal_token: event.disavowal_token).
- deliver_later
+ deliver_now
end
end
diff --git a/app/controllers/idv/usps_controller.rb b/app/controllers/idv/usps_controller.rb
index beaa89e84e6..0ffdd7bf07b 100644
--- a/app/controllers/idv/usps_controller.rb
+++ b/app/controllers/idv/usps_controller.rb
@@ -168,7 +168,7 @@ def error_message
def send_reminder
current_user.confirmed_email_addresses.each do |email_address|
- UserMailer.letter_reminder(current_user, email_address.email).deliver_later
+ UserMailer.letter_reminder(current_user, email_address.email).deliver_now
end
end
diff --git a/app/controllers/users/email_confirmations_controller.rb b/app/controllers/users/email_confirmations_controller.rb
index f53ad220814..775941cf380 100644
--- a/app/controllers/users/email_confirmations_controller.rb
+++ b/app/controllers/users/email_confirmations_controller.rb
@@ -40,7 +40,7 @@ def process_successful_confirmation(email_address)
def confirm_and_notify_user(email_address)
email_address.update!(confirmed_at: Time.zone.now)
email_address.user.confirmed_email_addresses.each do |confirmed_email_address|
- UserMailer.email_added(email_address.user, confirmed_email_address.email).deliver_later
+ UserMailer.email_added(email_address.user, confirmed_email_address.email).deliver_now
end
end
diff --git a/app/controllers/users/emails_controller.rb b/app/controllers/users/emails_controller.rb
index 5ecd3453dea..c8837bc130a 100644
--- a/app/controllers/users/emails_controller.rb
+++ b/app/controllers/users/emails_controller.rb
@@ -100,7 +100,7 @@ def retain_confirmed_emails
def send_delete_email_notification
@current_confirmed_emails.each do |confirmed_email|
- UserMailer.email_deleted(current_user, confirmed_email).deliver_later
+ UserMailer.email_deleted(current_user, confirmed_email).deliver_now
end
end
end
diff --git a/app/controllers/users/piv_cac_authentication_setup_controller.rb b/app/controllers/users/piv_cac_authentication_setup_controller.rb
index f7d3f7f41ec..8d6fc3f754a 100644
--- a/app/controllers/users/piv_cac_authentication_setup_controller.rb
+++ b/app/controllers/users/piv_cac_authentication_setup_controller.rb
@@ -15,9 +15,6 @@ class PivCacAuthenticationSetupController < ApplicationController
def new
if params.key?(:token)
process_piv_cac_setup
- # this branch is deprecated, remove it
- elsif flash[:error_type].present?
- render_error
else
render_prompt
end
@@ -74,16 +71,6 @@ def piv_cac_service_url_with_redirect
)
end
- def render_error
- @presenter = PivCacErrorPresenter.new(
- error: flash[:error_type],
- view: view_context,
- try_again_url: setup_piv_cac_url,
- )
-
- render :error
- end
-
def process_piv_cac_setup
result = user_piv_cac_form.submit
analytics.track_event(Analytics::MULTI_FACTOR_AUTH_SETUP, result.to_h)
diff --git a/app/controllers/users/verify_account_controller.rb b/app/controllers/users/verify_account_controller.rb
index 500972a7f04..552d116eec0 100644
--- a/app/controllers/users/verify_account_controller.rb
+++ b/app/controllers/users/verify_account_controller.rb
@@ -10,22 +10,36 @@ def index
usps_mail = Idv::UspsMail.new(current_user)
@mail_spammed = usps_mail.mail_spammed?
@verify_account_form = VerifyAccountForm.new(user: current_user)
- return unless FeatureManagement.reveal_usps_code?
- @code = session[:last_usps_confirmation_code]
+ @code = session[:last_usps_confirmation_code] if FeatureManagement.reveal_usps_code?
+
+ if Throttler::IsThrottled.call(current_user.id, :verify_gpo_key)
+ render :throttled
+ else
+ render :index
+ end
end
def create
@verify_account_form = build_verify_account_form
- result = @verify_account_form.submit
- analytics.track_event(Analytics::ACCOUNT_VERIFICATION_SUBMITTED, result.to_h)
+ throttled = Throttler::IsThrottledElseIncrement.call(
+ current_user.id,
+ :verify_gpo_key,
+ )
- if result.success?
- create_user_event(:account_verified)
- flash[:success] = t('account.index.verification.success')
- redirect_to sign_up_completed_url
+ if throttled
+ render :throttled
else
- render :index
+ result = @verify_account_form.submit
+ analytics.track_event(Analytics::ACCOUNT_VERIFICATION_SUBMITTED, result.to_h)
+
+ if result.success?
+ create_user_event(:account_verified)
+ flash[:success] = t('account.index.verification.success')
+ redirect_to sign_up_completed_url
+ else
+ render :index
+ end
end
end
diff --git a/app/controllers/users/verify_personal_key_controller.rb b/app/controllers/users/verify_personal_key_controller.rb
index 853107688e4..c551ac77c01 100644
--- a/app/controllers/users/verify_personal_key_controller.rb
+++ b/app/controllers/users/verify_personal_key_controller.rb
@@ -12,15 +12,31 @@ def new
user: current_user,
personal_key: '',
)
+
+ if Throttler::IsThrottled.call(current_user.id, :verify_personal_key)
+ render :throttled
+ else
+ render :new
+ end
end
def create
- result = personal_key_form.submit
- analytics.track_event(Analytics::PERSONAL_KEY_REACTIVATION_SUBMITTED, result.to_h)
- if result.success?
- handle_success(result)
+ throttled = Throttler::IsThrottledElseIncrement.call(
+ current_user.id,
+ :verify_personal_key,
+ )
+
+ if throttled
+ render :throttled
else
- handle_failure(result)
+ result = personal_key_form.submit
+
+ analytics.track_event(Analytics::PERSONAL_KEY_REACTIVATION_SUBMITTED, result.to_h)
+ if result.success?
+ handle_success(result)
+ else
+ handle_failure(result)
+ end
end
end
diff --git a/app/forms/idv/api_image_upload_form.rb b/app/forms/idv/api_image_upload_form.rb
index dd166484310..59dbcda55f7 100644
--- a/app/forms/idv/api_image_upload_form.rb
+++ b/app/forms/idv/api_image_upload_form.rb
@@ -21,7 +21,7 @@ def initialize(params, liveness_checking_enabled:, issuer:, analytics: nil)
def submit
throttled_else_increment
- @form_response = validate_form
+ form_response = validate_form
client_response = nil
doc_pii_response = nil
@@ -29,11 +29,7 @@ def submit
if form_response.success?
client_response = post_images_to_client
- if client_response.success?
- doc_pii_response = validate_pii_from_doc(client_response)
- else
- client_response = client_response.merge(form_response)
- end
+ doc_pii_response = validate_pii_from_doc(client_response) if client_response.success?
end
return determine_response(
@@ -59,10 +55,7 @@ def validate_form
response = Idv::DocAuthFormResponse.new(
success: valid?,
errors: errors.messages,
- extra: {
- remaining_attempts: remaining_attempts,
- user_id: user_uuid,
- },
+ extra: extra_attributes,
)
track_event(
@@ -80,6 +73,7 @@ def post_images_to_client
selfie_image: selfie&.read,
liveness_checking_enabled: liveness_checking_enabled?,
)
+ response = response.merge(extra_attributes_response)
update_analytics(response)
@@ -88,14 +82,30 @@ def post_images_to_client
def validate_pii_from_doc(client_response)
response = Idv::DocPiiForm.new(client_response.pii_from_doc).submit
+ response = response.merge(extra_attributes_response)
+
track_event(
Analytics::IDV_DOC_AUTH_SUBMITTED_PII_VALIDATION,
- response.to_h.merge(user_id: user_uuid),
+ response.to_h,
)
store_pii(client_response) if client_response.success? && response.success?
- # merge in the image_form_response to pick up the remaining_attempts
- response.merge(form_response)
+ response
+ end
+
+ def extra_attributes_response
+ @extra_attributes_response ||= Idv::DocAuthFormResponse.new(
+ success: true,
+ errors: {},
+ extra: extra_attributes,
+ )
+ end
+
+ def extra_attributes
+ @extra_attributes ||= {
+ remaining_attempts: remaining_attempts,
+ user_id: user_uuid,
+ }
end
def remaining_attempts
@@ -186,7 +196,7 @@ def update_analytics(client_response)
update_funnel(client_response)
track_event(
Analytics::IDV_DOC_AUTH_SUBMITTED_IMAGE_UPLOAD_VENDOR,
- client_response.to_h.merge(user_id: user_uuid),
+ client_response.to_h,
)
end
diff --git a/app/forms/register_user_email_form.rb b/app/forms/register_user_email_form.rb
index 0870b995bde..c28b47de984 100644
--- a/app/forms/register_user_email_form.rb
+++ b/app/forms/register_user_email_form.rb
@@ -128,7 +128,7 @@ def send_sign_up_unconfirmed_email(request_id)
def send_sign_up_confirmed_email
@throttled = Throttler::IsThrottledElseIncrement.call(existing_user.id, :reg_confirmed_email)
- UserMailer.signup_with_your_email(existing_user, email).deliver_later unless @throttled
+ UserMailer.signup_with_your_email(existing_user, email).deliver_now unless @throttled
end
def user_unconfirmed?
diff --git a/app/javascript/packages/document-capture/components/acuant-capture.jsx b/app/javascript/packages/document-capture/components/acuant-capture.jsx
index 1bf5badbf2e..0a94998680b 100644
--- a/app/javascript/packages/document-capture/components/acuant-capture.jsx
+++ b/app/javascript/packages/document-capture/components/acuant-capture.jsx
@@ -363,7 +363,7 @@ function AcuantCapture(
{isMobile && (
<% end %>
diff --git a/app/views/accounts/actions/_manage_personal_key.html.erb b/app/views/accounts/actions/_manage_personal_key.html.erb
index 02121680783..e4f593bcc06 100644
--- a/app/views/accounts/actions/_manage_personal_key.html.erb
+++ b/app/views/accounts/actions/_manage_personal_key.html.erb
@@ -1,7 +1,7 @@
<%= button_to(
create_new_personal_key_url,
method: :post,
- class: 'btn btn-link margin-left-1',
+ class: 'usa-button usa-button--unstyled margin-left-1',
form_class: 'inline-block',
) do %>
diff --git a/app/views/idv/cac/_start_over_or_cancel.html.erb b/app/views/idv/cac/_start_over_or_cancel.html.erb
index 6015c2d0477..b047a04edcc 100644
--- a/app/views/idv/cac/_start_over_or_cancel.html.erb
+++ b/app/views/idv/cac/_start_over_or_cancel.html.erb
@@ -1,4 +1,4 @@
<%= button_to(t('doc_auth.buttons.start_over'), idv_cac_step_path(:reset), method: :put,
- class: 'btn btn-link', form_class: 'inline-block') %>
+ class: 'usa-button usa-button--unstyled', form_class: 'inline-block') %>
<%= render 'shared/cancel', link: idv_cancel_path %>
diff --git a/app/views/idv/cac/verify.html.erb b/app/views/idv/cac/verify.html.erb
index 4ff6795e3f0..71b47d821b2 100644
--- a/app/views/idv/cac/verify.html.erb
+++ b/app/views/idv/cac/verify.html.erb
@@ -22,7 +22,7 @@
<%= button_to(t('in_person_proofing.buttons.change_address'),
- idv_cac_step_path(step: :redo_enter_info), method: :put, class: 'btn btn-link') %>
+ idv_cac_step_path(step: :redo_enter_info), method: :put, class: 'usa-button usa-button--unstyled') %>
<%= "#{t('in_person_proofing.forms.address1')}: #{flow_session[:pii_from_doc][:address1]}" %>
@@ -39,7 +39,7 @@
<%= button_to(t('in_person_proofing.buttons.change_ssn'),
- idv_cac_step_path(step: :redo_enter_info), method: :put, class: 'btn btn-link') %>
+ idv_cac_step_path(step: :redo_enter_info), method: :put, class: 'usa-button usa-button--unstyled') %>
<%= "#{t('in_person_proofing.forms.dob')}: #{flow_session[:pii_from_doc][:dob]}" %>
diff --git a/app/views/idv/doc_auth/_back.html.erb b/app/views/idv/doc_auth/_back.html.erb
index 82823efec88..2f1cd0745aa 100644
--- a/app/views/idv/doc_auth/_back.html.erb
+++ b/app/views/idv/doc_auth/_back.html.erb
@@ -24,7 +24,7 @@ classes << local_assigns[:class] if local_assigns[:class]
text,
path,
method: :put,
- class: [*classes, 'btn btn-link']
+ class: [*classes, 'usa-button usa-button--unstyled']
) %>
<% else %>
<%= link_to(text, path, class: classes) %>
diff --git a/app/views/idv/doc_auth/_start_over.html.erb b/app/views/idv/doc_auth/_start_over.html.erb
index 45f1417e7cd..a2b5c6ae3ef 100644
--- a/app/views/idv/doc_auth/_start_over.html.erb
+++ b/app/views/idv/doc_auth/_start_over.html.erb
@@ -2,6 +2,6 @@
t('doc_auth.buttons.start_over'),
idv_doc_auth_step_path(step: :reset),
method: :put,
- class: 'btn btn-link',
+ class: 'usa-button usa-button--unstyled',
form_class: 'inline-block margin-top-4'
) %>
diff --git a/app/views/idv/doc_auth/upload.html.erb b/app/views/idv/doc_auth/upload.html.erb
index 250b15a2eb8..7870feac198 100644
--- a/app/views/idv/doc_auth/upload.html.erb
+++ b/app/views/idv/doc_auth/upload.html.erb
@@ -92,7 +92,7 @@
method: 'PUT',
html: { autocomplete: 'off', role: 'form', class: 'inline' }
) do %>
-
+
<%= t('doc_auth.info.upload_computer_link') %>
<% end %>
diff --git a/app/views/idv/doc_auth/verify.html.erb b/app/views/idv/doc_auth/verify.html.erb
index c04e329f7ed..004a1bb71e9 100644
--- a/app/views/idv/doc_auth/verify.html.erb
+++ b/app/views/idv/doc_auth/verify.html.erb
@@ -37,7 +37,7 @@
t('doc_auth.buttons.change_ssn'),
idv_doc_auth_step_path(step: :redo_ssn),
method: :put,
- class: 'btn btn-link',
+ class: 'usa-button usa-button--unstyled',
) %>
<%= t('doc_auth.forms.ssn') %>
diff --git a/app/views/idv/in_person/_start_over_or_cancel.html.erb b/app/views/idv/in_person/_start_over_or_cancel.html.erb
index e513b14faef..e4a810b180b 100644
--- a/app/views/idv/in_person/_start_over_or_cancel.html.erb
+++ b/app/views/idv/in_person/_start_over_or_cancel.html.erb
@@ -1,4 +1,4 @@
<%= button_to(t('doc_auth.buttons.start_over'), idv_in_person_step_path(:reset), method: :put,
- class: 'btn btn-link', form_class: 'inline-block') %>
+ class: 'usa-button usa-button--unstyled', form_class: 'inline-block') %>
<%= render 'shared/cancel', link: idv_cancel_path %>
diff --git a/app/views/idv/in_person/encrypt.html.erb b/app/views/idv/in_person/encrypt.html.erb
index 6cf4114ded0..7b96cad918e 100644
--- a/app/views/idv/in_person/encrypt.html.erb
+++ b/app/views/idv/in_person/encrypt.html.erb
@@ -24,7 +24,7 @@
<%= t('idv.forgot_password.link_html',
link: link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url,
- class: 'btn btn-link margin-left-1', form_class: 'inline-block')) %>
+ class: 'usa-button usa-button--unstyled margin-left-1', form_class: 'inline-block')) %>
<%= f.button :submit, t('forms.buttons.continue'), class: 'btn btn-primary btn-wide sm-col-6 col-12' %>
diff --git a/app/views/idv/in_person/verify.html.erb b/app/views/idv/in_person/verify.html.erb
index e66445e8dd2..b794b472891 100644
--- a/app/views/idv/in_person/verify.html.erb
+++ b/app/views/idv/in_person/verify.html.erb
@@ -46,7 +46,7 @@
<%= button_to(t('in_person_proofing.buttons.change_ssn'),
- idv_doc_auth_step_path(step: :redo_ssn), method: :put, class: 'btn btn-link') %>
+ idv_doc_auth_step_path(step: :redo_ssn), method: :put, class: 'usa-button usa-button--unstyled') %>
<%= t('in_person_proofing.forms.ssn') %>: 111-11-1111
diff --git a/app/views/idv/otp_verification/show.html.erb b/app/views/idv/otp_verification/show.html.erb
index a964ba2780b..27aad01242c 100644
--- a/app/views/idv/otp_verification/show.html.erb
+++ b/app/views/idv/otp_verification/show.html.erb
@@ -24,7 +24,7 @@
<%= button_to(t('links.two_factor_authentication.get_another_code'), idv_resend_otp_path,
method: :post,
- class: 'btn btn-link btn-border ico ico-refresh text-decoration-none',
+ class: 'usa-button usa-button--unstyled btn-border ico ico-refresh text-decoration-none',
form_class: 'inline-block') %>
diff --git a/app/views/idv/review/new.html.erb b/app/views/idv/review/new.html.erb
index 643100b550e..aedf7dc1526 100644
--- a/app/views/idv/review/new.html.erb
+++ b/app/views/idv/review/new.html.erb
@@ -18,7 +18,7 @@
<%= t('idv.forgot_password.link_html',
link: link_to(t('idv.forgot_password.link_text'), idv_forgot_password_url,
- class: 'btn btn-link margin-left-1', form_class: 'inline-block')) %>
+ class: 'usa-button usa-button--unstyled margin-left-1', form_class: 'inline-block')) %>
<%= accordion('review-verified-info', t('idv.messages.review.intro')) do %>
diff --git a/app/views/idv/shared/_back_to_sp_link.html.erb b/app/views/idv/shared/_back_to_sp_link.html.erb
index 7332d22164e..a3c8c9dd2bd 100644
--- a/app/views/idv/shared/_back_to_sp_link.html.erb
+++ b/app/views/idv/shared/_back_to_sp_link.html.erb
@@ -3,10 +3,10 @@
<%= link_to image_tag(asset_url('carat-right.svg'), size: '10'),
- return_to_sp_failure_to_proof_path, class: 'bold block btn-link text-decoration-none' %>
+ return_to_sp_failure_to_proof_path, class: 'bold block usa-button usa-button--unstyled text-decoration-none' %>
<%= link_to t('idv.failure.help.get_help_html', sp_name: decorated_session.sp_name),
- return_to_sp_failure_to_proof_path, class: 'block btn-link text-decoration-none' %>
+ return_to_sp_failure_to_proof_path, class: 'block usa-button usa-button--unstyled text-decoration-none' %>
<% end %>
diff --git a/app/views/idv/shared/_document_capture.html.erb b/app/views/idv/shared/_document_capture.html.erb
index be1d1036017..1d17a83a44d 100644
--- a/app/views/idv/shared/_document_capture.html.erb
+++ b/app/views/idv/shared/_document_capture.html.erb
@@ -146,7 +146,7 @@
<% if session['idv/capture_doc'] %>
<%= button_to(cancel_link_text, idv_capture_doc_step_url(step: :cancel), method: :put,
- class: 'btn btn-link') %>
+ class: 'usa-button usa-button--unstyled') %>
<% else %>
<%= render 'shared/cancel', link: idv_cancel_path %>
diff --git a/app/views/idv/shared/_reset_your_account.html.erb b/app/views/idv/shared/_reset_your_account.html.erb
index 2f932175bc1..4be74a88614 100644
--- a/app/views/idv/shared/_reset_your_account.html.erb
+++ b/app/views/idv/shared/_reset_your_account.html.erb
@@ -2,7 +2,7 @@
<%= link_to image_tag(asset_url('carat-right.svg'), size: '10'),
- return_to_sp_failure_to_proof_path, class: 'bold block btn-link text-decoration-none' %>
+ return_to_sp_failure_to_proof_path, class: 'bold block usa-button usa-button--unstyled text-decoration-none' %>
<%= link_to t('two_factor_authentication.account_reset.reset_your_account'),
account_reset_request_path %>
diff --git a/app/views/shared/_cancel.html.erb b/app/views/shared/_cancel.html.erb
index 2be7fa5dfa3..787a23433f0 100644
--- a/app/views/shared/_cancel.html.erb
+++ b/app/views/shared/_cancel.html.erb
@@ -1,6 +1,6 @@
<% if user_signing_up? %>
- <%= link_to cancel_link_text, sign_up_cancel_path, method: :get, class: 'btn btn-link' %>
+ <%= link_to cancel_link_text, sign_up_cancel_path, method: :get, class: 'usa-button usa-button--unstyled' %>
<% else %>
<%= link_to cancel_link_text, link || return_to_sp_cancel_path %>
<% end %>
diff --git a/app/views/two_factor_authentication/otp_verification/show.html.erb b/app/views/two_factor_authentication/otp_verification/show.html.erb
index 31e7bb1a1b9..74c6f08f39a 100644
--- a/app/views/two_factor_authentication/otp_verification/show.html.erb
+++ b/app/views/two_factor_authentication/otp_verification/show.html.erb
@@ -36,7 +36,7 @@
otp_delivery_preference: @presenter.otp_delivery_preference,
resend: true
}),
- class: 'btn btn-link btn-border ico ico-refresh text-decoration-none flex-no-shrink margin-right-205',
+ class: 'usa-button usa-button--unstyled btn-border ico ico-refresh text-decoration-none flex-no-shrink margin-right-205',
form_class: 'inline-block') %>
diff --git a/app/views/users/emails/verify.html.erb b/app/views/users/emails/verify.html.erb
index f8b0129da29..5167b2b4541 100644
--- a/app/views/users/emails/verify.html.erb
+++ b/app/views/users/emails/verify.html.erb
@@ -23,7 +23,7 @@
<%= t('notices.signed_up_and_confirmed.no_email_sent_explanation_start') %>
-<%= button_to(t('links.resend'), add_email_resend_path, method: :post, class: 'btn btn-link margin-left-1', form_class: 'inline-block') %>
+<%= button_to(t('links.resend'), add_email_resend_path, method: :post, class: 'usa-button usa-button--unstyled margin-left-1', form_class: 'inline-block') %>
<% link = link_to(t('notices.use_diff_email.link'), add_email_path) %>
diff --git a/app/views/users/piv_cac_login/account_not_found.html.erb b/app/views/users/piv_cac_login/account_not_found.html.erb
deleted file mode 100644
index 53ab944f840..00000000000
--- a/app/views/users/piv_cac_login/account_not_found.html.erb
+++ /dev/null
@@ -1,8 +0,0 @@
-<% title t('headings.piv_cac_login.account_not_found') %>
-
-
<%= t('headings.piv_cac_login.account_not_found') %>
-<%= t('instructions.mfa.piv_cac.account_not_found_html',
- sign_in: link_to(t('headings.sign_in_without_sp'), root_url),
- create_account: link_to(t('links.create_account'), sign_up_email_url),
- )
-%>
diff --git a/app/views/users/piv_cac_login/did_not_work.html.erb b/app/views/users/piv_cac_login/did_not_work.html.erb
deleted file mode 100644
index b5f349a0f9d..00000000000
--- a/app/views/users/piv_cac_login/did_not_work.html.erb
+++ /dev/null
@@ -1,16 +0,0 @@
-<% title t('headings.piv_cac.did_not_work') %>
-
-
- <%= t('headings.piv_cac.did_not_work') %>
-
-
-
- <%= t('instructions.mfa.piv_cac.did_not_work_html',
- please_try_again: link_to(I18n.t('instructions.mfa.piv_cac.please_try_again'), login_piv_cac_url)) %>
-
-
-
- <%= link_to t('instructions.mfa.piv_cac.back_to_sign_in'), root_url, class: 'usa-button' %>
-
-
-<%= render 'shared/cancel', link: new_user_session_url %>
diff --git a/app/views/users/piv_cac_login/temporary_error.html.erb b/app/views/users/piv_cac_login/temporary_error.html.erb
deleted file mode 100644
index 6f95291478c..00000000000
--- a/app/views/users/piv_cac_login/temporary_error.html.erb
+++ /dev/null
@@ -1,11 +0,0 @@
-<% title t('headings.piv_cac_login.temporary_error') %>
-
- <%= t('headings.piv_cac_login.temporary_error') %>
-
-
- <%= t('instructions.mfa.piv_cac.temporary_error') %>
-
-
- <%= link_to t('instructions.mfa.piv_cac.back_to_sign_in'), root_url, class: 'usa-button' %>
-
-<%= render 'shared/cancel', link: new_user_session_url %>
diff --git a/app/views/users/verify_account/index.html.erb b/app/views/users/verify_account/index.html.erb
index 2b7c3a5c2bd..11663d4febe 100644
--- a/app/views/users/verify_account/index.html.erb
+++ b/app/views/users/verify_account/index.html.erb
@@ -20,7 +20,7 @@
<%= link_to t('idv.messages.usps.resend'), idv_usps_path, class: 'block margin-bottom-2' %>
<% end %>
-<%= button_to t('idv.messages.clear_and_start_over'), idv_session_path, method: :delete, class: 'btn btn-link' %>
+<%= button_to t('idv.messages.clear_and_start_over'), idv_session_path, method: :delete, class: 'usa-button usa-button--unstyled' %>
<%= link_to t('idv.buttons.cancel'), account_path %>
diff --git a/app/views/users/verify_account/throttled.html.erb b/app/views/users/verify_account/throttled.html.erb
new file mode 100644
index 00000000000..314043fd908
--- /dev/null
+++ b/app/views/users/verify_account/throttled.html.erb
@@ -0,0 +1,9 @@
+<% title t('titles.verify_profile') %>
+
+
+ <%= t('forms.verify_profile.title') %>
+
+
+
+ <%= t('errors.verify_profile.throttled') %> <%= link_to(t('links.go_back'), account_path) %>.
+
diff --git a/app/views/users/verify_personal_key/new.html.erb b/app/views/users/verify_personal_key/new.html.erb
index d392b53e797..1a86b89708a 100644
--- a/app/views/users/verify_personal_key/new.html.erb
+++ b/app/views/users/verify_personal_key/new.html.erb
@@ -20,7 +20,7 @@
<%= t('forms.personal_key.alternative') %>
<%= button_to(t('links.reverify'), reactivate_account_path, method: :put,
- class: 'btn btn-link margin-left-1', form_class: 'inline-block') %>
+ class: 'usa-button usa-button--unstyled margin-left-1', form_class: 'inline-block') %>
<%= render 'shared/cancel', link: account_path %>
diff --git a/app/views/users/verify_personal_key/throttled.html.erb b/app/views/users/verify_personal_key/throttled.html.erb
new file mode 100644
index 00000000000..8622cbdfe75
--- /dev/null
+++ b/app/views/users/verify_personal_key/throttled.html.erb
@@ -0,0 +1,9 @@
+<% title t('headings.verify_personal_key') %>
+
+
+ <%= t('headings.verify_personal_key') %>
+
+
+
+ <%= t('errors.verify_personal_key.throttled') %> <%= link_to(t('links.go_back'), account_path) %>.
+
diff --git a/bin/delayed_job b/bin/delayed_job
new file mode 100755
index 00000000000..edf195985f6
--- /dev/null
+++ b/bin/delayed_job
@@ -0,0 +1,5 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
+require 'delayed/command'
+Delayed::Command.new(ARGV).daemonize
diff --git a/config/application.rb b/config/application.rb
index e832dd60796..42d87769502 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -23,7 +23,7 @@ class Application < Rails::Application
config.active_record.belongs_to_required_by_default = false
config.assets.unknown_asset_fallback = true
- config.active_job.queue_adapter = 'inline'
+ config.active_job.queue_adapter = :inline
config.time_zone = 'UTC'
# Generate CSRF tokens that are encoded in URL-safe Base64.
diff --git a/config/application.yml.default b/config/application.yml.default
index 04cd28a4677..7664c0636e6 100644
--- a/config/application.yml.default
+++ b/config/application.yml.default
@@ -125,6 +125,8 @@ remember_device_expiration_hours_aal_1: '720'
remember_device_expiration_hours_aal_2: '12'
report_timeout:
requests_per_ip_track_only_mode: 'false'
+risc_notifications_sqs_enabled: 'false'
+ruby_workers_enabled: 'false'
saml_secret_rotation_enabled:
service_provider_request_ttl_hours: '24'
session_check_delay: '30'
@@ -140,6 +142,10 @@ usps_download_sftp_timeout: '5'
usps_upload_enabled: 'false'
usps_upload_sftp_timeout: '5'
valid_authn_contexts: '["http://idmanagement.gov/ns/assurance/loa/1", "http://idmanagement.gov/ns/assurance/loa/3", "http://idmanagement.gov/ns/assurance/ial/1", "http://idmanagement.gov/ns/assurance/ial/2", "http://idmanagement.gov/ns/assurance/ial/0", "http://idmanagement.gov/ns/assurance/ial/2?strict=true", "urn:gov:gsa:ac:classes:sp:PasswordProtectedTransport:duo", "http://idmanagement.gov/ns/assurance/aal/2", "http://idmanagement.gov/ns/assurance/aal/3", "http://idmanagement.gov/ns/assurance/aal/3?hspd12=true"]'
+verify_gpo_key_attempt_window_in_minutes: '10'
+verify_gpo_key_max_attempts: '3'
+verify_personal_key_attempt_window_in_minutes: '15'
+verify_personal_key_max_attempts: '5'
usps_ipp_password: ''
usps_ipp_root_url: ''
usps_ipp_sponsor_id: ''
@@ -443,6 +449,7 @@ test:
reset_password_email_max_attempts: '5'
reset_password_email_window_in_minutes: '80'
resolution_proof_result_lambda_token: test_token_resolution
+ ruby_workers_enabled: 'true'
s3_report_bucket_prefix:
s3_reports_enabled: 'false'
saml_endpoint_configs: '[{"suffix":"2021","secret_key_passphrase":"trust-but-verify"}]'
@@ -451,6 +458,10 @@ test:
session_encryption_key: 27bad3c25711099429c1afdfd1890910f3b59f5a4faec1c85e945cb8b02b02f261ba501d99cfbb4fab394e0102de6fecf8ffe260f322f610db3e96b2a775c120
sps_over_quota_limit_notify_email_list: '["test1@test.com"]'
telephony_adapter: test
+ verify_gpo_key_attempt_window_in_minutes: '3'
+ verify_gpo_key_max_attempts: '2'
+ verify_personal_key_attempt_window_in_minutes: '3'
+ verify_personal_key_max_attempts: '1'
use_dashboard_service_providers: 'false'
use_kms: 'false'
usps_confirmation_max_days: '10'
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 4f191c8e5d0..cab7bbe5389 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -2,6 +2,7 @@
# Verifies that versions and hashed value of the package contents in the project's package.json
config.webpacker.check_yarn_integrity = true
+ config.active_job.queue_adapter = :delayed_job
config.cache_classes = false
config.eager_load = false
config.consider_all_requests_local = true
diff --git a/config/initializers/active_job_logger_patch.rb b/config/initializers/active_job_logger_patch.rb
deleted file mode 100644
index 37ebf6ab0aa..00000000000
--- a/config/initializers/active_job_logger_patch.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-# This overrides the ActiveJob logger to remove sensitive info from the logs,
-# such as Devise tokens, OTP codes, phone numbers, emails, and data sent to
-# Google Analytics.
-ActiveSupport.on_load :active_job do # rubocop:disable Metrics/BlockLength
- module ActiveJob
- module Logging
- class LogSubscriber
- def enqueue(event)
- info { json_for(event: event, event_type: 'Enqueued') }
- end
-
- def perform_start(event)
- info { json_for(event: event, event_type: 'Performing') }
- end
-
- def perform(event)
- info { json_for(event: event, event_type: 'Performed') }
- end
-
- private
-
- def json_for(event:, event_type:)
- job = event.payload[:job]
-
- {
- timestamp: Time.zone.now,
- event_type: event_type,
- job_class: job.class.name,
- job_queue: queue_name(event),
- job_id: job.job_id,
- duration: "#{event.duration.round(2)}ms",
- }.to_json
- end
-
- def args_info(_job)
- ''
- end
- end
- end
- end
-end
diff --git a/config/initializers/delayed_job.rb b/config/initializers/delayed_job.rb
new file mode 100644
index 00000000000..92087607ca3
--- /dev/null
+++ b/config/initializers/delayed_job.rb
@@ -0,0 +1,6 @@
+Delayed::Worker.destroy_failed_jobs = true
+Delayed::Worker.sleep_delay = 5
+Delayed::Worker.max_attempts = 1
+Delayed::Worker.max_run_time = 5.minutes
+Delayed::Worker.read_ahead = 10
+Delayed::Worker.default_queue_name = 'default'
diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb
index 62932e4d783..4aba2ab4bff 100644
--- a/config/initializers/secure_headers.rb
+++ b/config/initializers/secure_headers.rb
@@ -7,7 +7,7 @@
config.x_permitted_cross_domain_policies = 'none'
connect_src = ["'self'", '*.newrelic.com', '*.nr-data.net', '*.google-analytics.com',
- 'us.acas.acuant.net', 'services.assureid.net']
+ 'us.acas.acuant.net']
connect_src << %w[ws://localhost:3035 http://localhost:3035] if Rails.env.development?
default_csp_config = {
default_src: ["'self'"],
diff --git a/config/locales/errors/en.yml b/config/locales/errors/en.yml
index f4b59bbd235..08b0bd36509 100644
--- a/config/locales/errors/en.yml
+++ b/config/locales/errors/en.yml
@@ -118,6 +118,10 @@ en:
unique_name: That name is already taken. Please choose a different name.
two_factor_auth_setup:
must_select_option: Select an authentication method.
+ verify_personal_key:
+ throttled: You tried too many times, please try again in 15 minutes.
+ verify_profile:
+ throttled: You tried too many times, please try again in 10 minutes.
webauthn_setup:
already_registered: Security key already registered. Please try a different
security key.
diff --git a/config/locales/errors/es.yml b/config/locales/errors/es.yml
index a8e4a0d352c..871d35082d1 100644
--- a/config/locales/errors/es.yml
+++ b/config/locales/errors/es.yml
@@ -121,6 +121,10 @@ es:
unique_name: El nombre ya fue escogido. Por favor, elija un nombre diferente.
two_factor_auth_setup:
must_select_option: Seleccione un método de autenticación.
+ verify_personal_key:
+ throttled: Lo intentaste muchas veces, vuelve a intentarlo en 15 minutos.
+ verify_profile:
+ throttled: Lo intentaste muchas veces, vuelve a intentarlo en 10 minutos.
webauthn_setup:
already_registered: Clave de seguridad ya registrada. Por favor, intente una
clave de seguridad diferente.
diff --git a/config/locales/errors/fr.yml b/config/locales/errors/fr.yml
index e8758d5e196..bc91ef4b73a 100644
--- a/config/locales/errors/fr.yml
+++ b/config/locales/errors/fr.yml
@@ -128,6 +128,10 @@ fr:
unique_name: Ce nom est déjà pris. Veuillez choisir un autre nom.
two_factor_auth_setup:
must_select_option: Sélectionnez une méthode d'authentification.
+ verify_personal_key:
+ throttled: Vous avez essayé plusieurs fois, essayez à nouveau dans 15 minutes.
+ verify_profile:
+ throttled: Vous avez essayé plusieurs fois, essayez à nouveau dans 10 minutes.
webauthn_setup:
already_registered: Clé de sécurité déjà enregistrée. Veuillez essayer une clé
de sécurité différente.
diff --git a/config/locales/headings/en.yml b/config/locales/headings/en.yml
index 83756e5fd0e..533e58f6474 100644
--- a/config/locales/headings/en.yml
+++ b/config/locales/headings/en.yml
@@ -55,7 +55,6 @@ en:
use it to sign in.
new: Sign in with your PIV or CAC
success: You successfully set up PIV/CAC as an authentication method.
- temporary_error: We're having technical difficulties using your PIV/CAC
piv_cac_setup:
already_associated: The PIV/CAC you presented is associated with another user.
new: Use your PIV/CAC card to secure your account
@@ -68,5 +67,6 @@ en:
totp_setup:
new: Add an authentication app
verify_email: Check your email
+ verify_personal_key: Verify your personal key
webauthn_setup:
new: Add your security key
diff --git a/config/locales/headings/es.yml b/config/locales/headings/es.yml
index cb1a02ac233..201af5760e9 100644
--- a/config/locales/headings/es.yml
+++ b/config/locales/headings/es.yml
@@ -55,7 +55,6 @@ es:
para que pueda usarlo para iniciar sesión.
new: Use su PIV / CAC para iniciar sesión en su cuenta
success: Configuró correctamente PIV/CAC como método de autenticación.
- temporary_error: Estamos teniendo dificultades técnicas para usar su PIV/CAC
piv_cac_setup:
already_associated: La PIV/CAC que has presentado está asociada a otro usuario.
new: Use su tarjeta PIV/CAC para asegurar su cuenta
@@ -68,5 +67,6 @@ es:
totp_setup:
new: Agregar una aplicación de autenticación
verify_email: Revise su email
+ verify_personal_key: Verifica tu clave personal
webauthn_setup:
new: Añade tu clave de seguridad
diff --git a/config/locales/headings/fr.yml b/config/locales/headings/fr.yml
index c71ca574c1c..a85b7c7301b 100644
--- a/config/locales/headings/fr.yml
+++ b/config/locales/headings/fr.yml
@@ -56,7 +56,6 @@ fr:
à deux facteurs pour pouvoir l'utiliser pour vous connecter.
new: Utilisez votre PIV / CAC pour vous connecter à votre compte
success: Vous avez correctement configuré PIV/CAC en tant que méthode d’authentification.
- temporary_error: Nous rencontrons des difficultés techniques avec votre PIV/CAC
piv_cac_setup:
already_associated: La carte PIV/CAC que vous avez présentée est associée à
un autre utilisateur.
@@ -70,5 +69,6 @@ fr:
totp_setup:
new: Ajouter une application d'authentification
verify_email: Consultez vos courriels
+ verify_personal_key: Vérifier votre clé personnelle
webauthn_setup:
new: Ajoutez votre clé de sécurité
diff --git a/config/locales/instructions/en.yml b/config/locales/instructions/en.yml
index 3b82527d1fb..8788379af54 100644
--- a/config/locales/instructions/en.yml
+++ b/config/locales/instructions/en.yml
@@ -59,8 +59,6 @@ en:
step_3_info_html: You'll need to
choose a certificate (the
right one likely has your name in it) and
enter your PIN
(your PIN was created when you set up your PIV/CAC).
- temporary_error: Please try again later or sign in with your email and password
- instead.
try_again: try again
sms:
confirm_code_html: Need another code? %{resend_code_link}. Message rates may
diff --git a/config/locales/instructions/es.yml b/config/locales/instructions/es.yml
index f7896f31eb4..1468c0f15f9 100644
--- a/config/locales/instructions/es.yml
+++ b/config/locales/instructions/es.yml
@@ -61,8 +61,6 @@ es:
step_3_info_html: Tendrá que
elegir un certificado (el
correcto probablemente tenga su nombre) e
ingrese su PIN
(su PIN se creó cuando configuró su PIV/CAC)
- temporary_error: Vuelve a intentarlo más tarde o inicia sesión con tu correo
- electrónico y contraseña.
try_again: inténtelo de nuevo
sms:
confirm_code_html: "¿Necesita otro código? %{resend_code_link}. Puede estar
diff --git a/config/locales/instructions/fr.yml b/config/locales/instructions/fr.yml
index f88969a39d5..3bb1f520869 100644
--- a/config/locales/instructions/fr.yml
+++ b/config/locales/instructions/fr.yml
@@ -69,8 +69,6 @@ fr:
de droite contient probablement votre nom) et
entrer votre code
PIN (votre code PIN a été créé lors de la configuration de votre
PIV / CAC ).
- temporary_error: Veuillez réessayer ultérieurement ou connectez-vous avec
- votre adresse électronique et votre mot de passe.
try_again: réessayer
sms:
confirm_code_html: Vous avez besoin d'un autre code? %{resend_code_link}.
diff --git a/config/routes.rb b/config/routes.rb
index 592f69ce28f..5ccd474a894 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -77,10 +77,6 @@
get '/login/piv_cac' => 'users/piv_cac_login#new'
get '/login/piv_cac_error' => 'users/piv_cac_login#error'
- # these routes are deprecated
- get '/login/piv_cac_account_not_found' => 'users/piv_cac_login#account_not_found'
- get '/login/piv_cac_did_not_work' => 'users/piv_cac_login#did_not_work'
- get '/login/piv_cac_temporary_error' => 'users/piv_cac_login#temporary_error'
get '/login/present_piv_cac' => 'users/piv_cac_login#redirect_to_piv_cac_service'
get '/login/password' => 'password_capture#new', as: :capture_password
diff --git a/db/migrate/20210315144559_create_delayed_jobs.rb b/db/migrate/20210315144559_create_delayed_jobs.rb
new file mode 100644
index 00000000000..d55c28bc2b9
--- /dev/null
+++ b/db/migrate/20210315144559_create_delayed_jobs.rb
@@ -0,0 +1,22 @@
+class CreateDelayedJobs < ActiveRecord::Migration[6.1]
+ def self.up
+ create_table :delayed_jobs do |table|
+ table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue
+ table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually.
+ table.text :handler, null: false # YAML-encoded string of the object that will do work
+ table.text :last_error # reason for last failure (See Note below)
+ table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future.
+ table.datetime :locked_at # Set when a client is working on this object
+ table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead)
+ table.string :locked_by # Who is working on this object (if locked)
+ table.string :queue # The name of the queue this job is in
+ table.timestamps null: true
+ end
+
+ add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority"
+ end
+
+ def self.down
+ drop_table :delayed_jobs
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2adea805d83..3bca0fea0ce 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2021_03_03_182041) do
+ActiveRecord::Schema.define(version: 2021_03_15_144559) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -79,6 +79,21 @@
t.index ["user_id", "created_at"], name: "index_backup_code_configurations_on_user_id_and_created_at"
end
+ create_table "delayed_jobs", force: :cascade do |t|
+ t.integer "priority", default: 0, null: false
+ t.integer "attempts", default: 0, null: false
+ t.text "handler", null: false
+ t.text "last_error"
+ t.datetime "run_at"
+ t.datetime "locked_at"
+ t.datetime "failed_at"
+ t.string "locked_by"
+ t.string "queue"
+ t.datetime "created_at", precision: 6
+ t.datetime "updated_at", precision: 6
+ t.index ["priority", "run_at"], name: "delayed_jobs_priority"
+ end
+
create_table "deleted_users", force: :cascade do |t|
t.integer "user_id", null: false
t.string "uuid", null: false
diff --git a/lib/deploy/activate.rb b/lib/deploy/activate.rb
index da9c4b2f03e..5f57fe4a253 100644
--- a/lib/deploy/activate.rb
+++ b/lib/deploy/activate.rb
@@ -28,6 +28,7 @@ def run
s3_path: '/%
s/idp/v1/application.yml',
local_path: env_yaml_path,
)
+ download_web_or_worker_yml_if_exists
deep_merge_s3_data_with_example_application_yml
set_proper_file_permissions_for_application_yml
@@ -103,6 +104,17 @@ def deep_merge_s3_data_with_example_application_yml
File.open(result_yaml_path, 'w') { |file| file.puts YAML.dump(application_config) }
end
+ def download_web_or_worker_yml_if_exists
+ return unless web_or_worker_yml
+
+ app_secrets_s3.download_file(
+ s3_path: "/%s/idp/v1/#{web_or_worker_yml}",
+ local_path: web_or_worker_yaml_path,
+ )
+ rescue Aws::S3::Errors::NoSuchKey => err
+ logger.warn("did not load #{web_or_worker_yml}, continuing")
+ end
+
def set_proper_file_permissions_for_application_yml
FileUtils.chmod(0o640, [env_yaml_path, result_yaml_path])
end
@@ -147,7 +159,14 @@ def root
end
def application_config
- YAML.load_file(example_application_yaml_path).deep_merge(YAML.load_file(env_yaml_path))
+ config = YAML.load_file(example_application_yaml_path).
+ deep_merge(YAML.load_file(env_yaml_path))
+
+ if web_or_worker_yml && File.exist?(web_or_worker_yaml_path)
+ config.deep_merge(YAML.load_file(web_or_worker_yaml_path))
+ else
+ config
+ end
end
def example_application_yaml_path
@@ -169,5 +188,19 @@ def geolocation_db_path
def pwned_passwords_path
File.join(root, 'pwned_passwords/pwned_passwords.txt')
end
+
+ def web_or_worker_yaml_path
+ File.join(root, "config/#{web_or_worker_yml}")
+ end
+
+ # @return [String, nil]
+ def web_or_worker_yml
+ case Identity::Hostdata.instance_role
+ when 'idp'
+ 'web.yml'
+ when 'worker'
+ 'worker.yml'
+ end
+ end
end
end
diff --git a/lib/feature_management.rb b/lib/feature_management.rb
index 63fcc92eea4..9aa43a1dd9c 100644
--- a/lib/feature_management.rb
+++ b/lib/feature_management.rb
@@ -142,6 +142,10 @@ def self.voip_block?
AppConfig.env.voip_block == 'true'
end
+ def self.ruby_workers_enabled?
+ AppConfig.env.ruby_workers_enabled == 'true'
+ end
+
# Manual allowlist for VOIPs, should only include known VOIPs that we use for smoke tests
# @return [Set] set of phone numbers normalized to e164
def self.voip_allowed_phones
diff --git a/lib/lambda_jobs/git_ref.rb b/lib/lambda_jobs/git_ref.rb
index 9197ef86209..160d76af46d 100644
--- a/lib/lambda_jobs/git_ref.rb
+++ b/lib/lambda_jobs/git_ref.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
module LambdaJobs
- GIT_REF = '051a8c3dca143b215838176a07b1f0545ffec0a2'
+ GIT_REF = 'd9241bdfea85a76c170e456a89ec6601549f4c4a'
end
diff --git a/lib/tasks/newrelic.rake b/lib/tasks/newrelic.rake
new file mode 100644
index 00000000000..d7e879e0b03
--- /dev/null
+++ b/lib/tasks/newrelic.rake
@@ -0,0 +1,18 @@
+namespace :newrelic do
+ ##
+ # This task is essentially the same as running the following:
+ #
+ # bundle exec newrelic deployment -r $(git rev-parse HEAD)
+ #
+ # The reason for the rake task is that our `newrelic.yml` file contains ERB
+ # blocks that expect the AppConfig to be setup and for identity-hostdata to be
+ # loaded. This rake task loads the rails environment before reporting the
+ # deployment so the NewRelic config is loaded correctly.
+ #
+ desc 'Report a new deployment to NewRelic'
+ task deployment: :environment do
+ require 'new_relic/cli/command'
+ revision = `git rev-parse HEAD`.chomp
+ NewRelic::Cli::Deployments.new(revision: `git rev-parse HEAD`).run
+ end
+end
diff --git a/package.json b/package.json
index 2d1bbf42951..03efaab7536 100644
--- a/package.json
+++ b/package.json
@@ -31,7 +31,7 @@
"clipboard": "^2.0.6",
"domready": "^1.0.8",
"focus-trap": "^6.2.3",
- "identity-style-guide": "^5.0.1",
+ "identity-style-guide": "^5.0.3",
"intl-tel-input": "^17.0.8",
"libphonenumber-js": "^1.9.6",
"postcss-clean": "^1.1.0",
diff --git a/spec/config/initializers/active_job_logger_patch_spec.rb b/spec/config/initializers/active_job_logger_patch_spec.rb
deleted file mode 100644
index aade0319acb..00000000000
--- a/spec/config/initializers/active_job_logger_patch_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'rails_helper'
-
-# Covers config/initializers/active_job_logger_patch.rb, which overrides
-# ActiveJob::Logging::LogSubscriber to standardize output and prevent sensitive
-# user data from being logged.
-describe ActiveJob::Logging::LogSubscriber do
- it 'overrides the default job logger to output only specified parameters in JSON format' do
- # rubocop:disable Rails/ApplicationJob
- class FakeJob < ActiveJob::Base
- def perform(sensitive_param:); end
- end
- # rubocop:enable Rails/ApplicationJob
-
- # This list corresponds to the initializer's output
- permitted_attributes = %w[
- timestamp
- event_type
- job_class
- job_queue
- job_id
- duration
- ]
-
- # In this case, we need to assert before the action which logs, block-style to
- # match the initializer
- expect(Rails.logger).to receive(:info) do |&blk|
- output = JSON.parse(blk.call)
-
- # [Sidenote: The nested assertions don't seem to be reflected in the spec
- # count--perhaps because of the uncommon block format?--but reversing them
- # will show them failing as expected.]
- output.each_key { |k| expect(permitted_attributes).to include(k) }
- expect(output.keys).to_not include('sensitive_param')
- end
-
- FakeJob.perform_later(sensitive_param: '111-22-3333')
- end
-end
diff --git a/spec/controllers/idv/doc_auth_controller_spec.rb b/spec/controllers/idv/doc_auth_controller_spec.rb
index 7716edd7322..4f4ea1267d6 100644
--- a/spec/controllers/idv/doc_auth_controller_spec.rb
+++ b/spec/controllers/idv/doc_auth_controller_spec.rb
@@ -185,6 +185,7 @@
let(:front_image_iv) { SecureRandom.random_bytes(12) }
let(:back_image_iv) { SecureRandom.random_bytes(12) }
let(:selfie_image_iv) { SecureRandom.random_bytes(12) }
+ encryption_helper = IdentityIdpFunctions::EncryptionHelper.new
before do
mock_document_capture_step
diff --git a/spec/controllers/idv/image_uploads_controller_spec.rb b/spec/controllers/idv/image_uploads_controller_spec.rb
index 6fba548a80b..b7dbe127952 100644
--- a/spec/controllers/idv/image_uploads_controller_spec.rb
+++ b/spec/controllers/idv/image_uploads_controller_spec.rb
@@ -213,6 +213,7 @@
exception: nil,
result: 'Passed',
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
expect(@analytics).to receive(:track_event).with(
@@ -220,6 +221,7 @@
success: true,
errors: {},
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
action
@@ -272,6 +274,7 @@
exception: nil,
result: 'Passed',
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
expect(@analytics).to receive(:track_event).with(
@@ -281,6 +284,7 @@
pii: [I18n.t('doc_auth.errors.lexis_nexis.full_name_check')],
},
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
action
@@ -309,6 +313,7 @@
exception: nil,
result: 'Passed',
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
expect(@analytics).to receive(:track_event).with(
@@ -318,6 +323,7 @@
pii: [I18n.t('doc_auth.errors.lexis_nexis.general_error_no_liveness')],
},
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
action
@@ -346,6 +352,7 @@
exception: nil,
result: 'Passed',
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
expect(@analytics).to receive(:track_event).with(
@@ -355,6 +362,7 @@
pii: [I18n.t('doc_auth.errors.lexis_nexis.birth_date_checks')],
},
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
action
@@ -404,6 +412,7 @@
front: ['Too blurry', 'Wrong document'],
},
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
exception: nil,
)
@@ -449,6 +458,7 @@
result: 'Caution',
exception: nil,
user_id: user.uuid,
+ remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1,
)
action
diff --git a/spec/controllers/lambda_callback/address_proof_result_controller_spec.rb b/spec/controllers/lambda_callback/address_proof_result_controller_spec.rb
index b51f98677b9..09adbc6ff93 100644
--- a/spec/controllers/lambda_callback/address_proof_result_controller_spec.rb
+++ b/spec/controllers/lambda_callback/address_proof_result_controller_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe LambdaCallback::AddressProofResultController do
+ include IdvHelper
+
describe '#create' do
let(:document_capture_session) { DocumentCaptureSession.new(user: create(:user)) }
let(:trace_id) { SecureRandom.uuid }
@@ -12,7 +14,6 @@
it 'accepts and stores successful address proofing results' do
applicant = { phone: Faker::PhoneNumber.cell_phone }
- document_capture_session.create_proofing_session
Idv::Agent.new(applicant).proof_address(document_capture_session, trace_id: trace_id)
proofer_result = document_capture_session.load_proofing_result[:result]
@@ -28,7 +29,6 @@
phone: IdentityIdpFunctions::AddressMockClient::UNVERIFIABLE_PHONE_NUMBER,
}
- document_capture_session.create_proofing_session
Idv::Agent.new(applicant).proof_address(document_capture_session, trace_id: trace_id)
proofer_result = document_capture_session.load_proofing_result[:result]
diff --git a/spec/controllers/lambda_callback/resolution_proof_result_controller_spec.rb b/spec/controllers/lambda_callback/resolution_proof_result_controller_spec.rb
index 7890cbb1581..fc89e2bf474 100644
--- a/spec/controllers/lambda_callback/resolution_proof_result_controller_spec.rb
+++ b/spec/controllers/lambda_callback/resolution_proof_result_controller_spec.rb
@@ -1,6 +1,8 @@
require 'rails_helper'
describe LambdaCallback::ResolutionProofResultController do
+ include IdvHelper
+
describe '#create' do
let(:document_capture_session) { DocumentCaptureSession.new(user: create(:user)) }
let(:trace_id) { SecureRandom.uuid }
@@ -14,12 +16,12 @@
applicant = { first_name: Faker::Name.first_name, ssn: Faker::IDNumber.valid,
zipcode: Faker::Address.zip_code, state_id_number: '123',
state_id_type: 'drivers_license', state_id_jurisdiction: 'WI' }
- document_capture_session.create_proofing_session
Idv::Agent.new(applicant).proof_resolution(
document_capture_session,
should_proof_state_id: true,
trace_id: trace_id,
)
+
proofer_result = document_capture_session.load_proofing_result[:result]
post :create, params: { result_id: document_capture_session.result_id,
@@ -32,7 +34,6 @@
it 'accepts and stores unsuccessful resolution proofing results' do
applicant = { first_name: 'Bad Name', ssn: Faker::IDNumber.valid,
zipcode: Faker::Address.zip_code }
- document_capture_session.create_proofing_session
Idv::Agent.new(applicant).proof_resolution(
document_capture_session,
should_proof_state_id: false,
@@ -57,7 +58,6 @@
applicant = { first_name: 'Time', ssn: Faker::IDNumber.valid,
zipcode: Faker::Address.zip_code }
- document_capture_session.create_proofing_session
Idv::Agent.new(applicant).proof_resolution(
document_capture_session,
should_proof_state_id: false,
diff --git a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb
index 2a0b3f90a7c..c31b249793c 100644
--- a/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb
+++ b/spec/controllers/two_factor_authentication/otp_verification_controller_spec.rb
@@ -281,7 +281,7 @@
stub_analytics
allow(@analytics).to receive(:track_event)
allow(subject).to receive(:create_user_event)
- @mailer = instance_double(ActionMailer::MessageDelivery, deliver_later: true)
+ @mailer = instance_double(ActionMailer::MessageDelivery, deliver_now: true)
subject.current_user.email_addresses.each do |email_address|
allow(UserMailer).to receive(:phone_added).
with(subject.current_user, email_address, disavowal_token: instance_of(String)).
@@ -328,7 +328,7 @@
expect(UserMailer).to have_received(:phone_added).
with(subject.current_user, email_address, disavowal_token: instance_of(String))
end
- expect(@mailer).to have_received(:deliver_later)
+ expect(@mailer).to have_received(:deliver_now)
end
end
diff --git a/spec/controllers/users/passwords_controller_spec.rb b/spec/controllers/users/passwords_controller_spec.rb
index 680984d02b2..2ee897512d3 100644
--- a/spec/controllers/users/passwords_controller_spec.rb
+++ b/spec/controllers/users/passwords_controller_spec.rb
@@ -50,7 +50,7 @@
it 'sends the user an email' do
user = create(:user)
mail = double
- expect(mail).to receive(:deliver_later)
+ expect(mail).to receive(:deliver_now)
expect(UserMailer).to receive(:password_changed).
with(user, user.email_addresses.first, hash_including(:disavowal_token)).
and_return(mail)
diff --git a/spec/controllers/users/reset_passwords_controller_spec.rb b/spec/controllers/users/reset_passwords_controller_spec.rb
index a0e0cf7b2ca..73de6eb811a 100644
--- a/spec/controllers/users/reset_passwords_controller_spec.rb
+++ b/spec/controllers/users/reset_passwords_controller_spec.rb
@@ -461,7 +461,7 @@
end
def stub_user_mailer(user)
- mailer = instance_double(ActionMailer::MessageDelivery, deliver_later: true)
+ mailer = instance_double(ActionMailer::MessageDelivery, deliver_now: true)
user.email_addresses.each do |email_address|
allow(UserMailer).to receive(:password_changed).
with(user, email_address, disavowal_token: instance_of(String)).
diff --git a/spec/controllers/users/verify_account_controller_spec.rb b/spec/controllers/users/verify_account_controller_spec.rb
index ad664ea265d..a27bc36a9f2 100644
--- a/spec/controllers/users/verify_account_controller_spec.rb
+++ b/spec/controllers/users/verify_account_controller_spec.rb
@@ -9,7 +9,8 @@
before do
stub_analytics
- user = stub_sign_in
+ user = create(:user)
+ stub_sign_in(user)
decorated_user = stub_decorated_user_with_pending_profile(user)
create(
:usps_confirmation_code,
@@ -33,6 +34,14 @@
expect(response).to render_template('users/verify_account/index')
end
+
+ it 'shows throttled page is user is throttled' do
+ allow(Throttler::IsThrottled).to receive(:call).once.and_return(true)
+
+ action
+
+ expect(response).to render_template(:throttled)
+ end
end
context 'user does not have pending profile' do
@@ -87,5 +96,31 @@
expect(response).to render_template('users/verify_account/index')
end
end
+
+ context 'with throttle reached' do
+ let(:submitted_otp) { 'a-wrong-otp' }
+
+ it 'renders the index page to show errors' do
+ max_attempts = AppConfig.env.verify_gpo_key_max_attempts.to_i
+
+ expect(@analytics).to receive(:track_event).with(
+ Analytics::ACCOUNT_VERIFICATION_SUBMITTED,
+ success: false, errors: { otp: [t('errors.messages.confirmation_code_incorrect')]},
+ ).exactly(max_attempts).times
+
+ (max_attempts + 1).times do |i|
+ post(
+ :create,
+ params: {
+ verify_account_form: {
+ otp: submitted_otp,
+ },
+ },
+ )
+ end
+
+ expect(response).to render_template('users/verify_account/throttled')
+ end
+ end
end
end
diff --git a/spec/controllers/users/verify_personal_key_controller_spec.rb b/spec/controllers/users/verify_personal_key_controller_spec.rb
index 9a4d7e2f328..ae6a1168e41 100644
--- a/spec/controllers/users/verify_personal_key_controller_spec.rb
+++ b/spec/controllers/users/verify_personal_key_controller_spec.rb
@@ -35,6 +35,13 @@
expect(subject.flash[:info]).to eq(t('notices.account_reactivation'))
end
+
+ it 'shows throttled page after being throttled' do
+ allow(Throttler::IsThrottled).to receive(:call).once.and_return(true)
+ get :new
+
+ expect(response).to render_template(:throttled)
+ end
end
end
@@ -86,5 +93,28 @@
expect(response).to render_template(:new)
end
end
+
+ context 'with throttle reached' do
+ let(:bad_key) { 'baaad' }
+ before do
+ allow(VerifyPersonalKeyForm).to receive(:new).
+ with(user: subject.current_user, personal_key: bad_key).
+ and_return(form)
+ allow(form).to receive(:submit).and_return(response_bad)
+ end
+
+ it 'renders throttled page' do
+ stub_analytics
+ expect(@analytics).to receive(:track_event).with(
+ Analytics::PERSONAL_KEY_REACTIVATION_SUBMITTED,
+ { errors: { personal_key: ['bad_key'] }, success: false },
+ ).once
+
+ post :create, params: { personal_key: bad_key }
+ post :create, params: { personal_key: bad_key }
+
+ expect(response).to render_template(:throttled)
+ end
+ end
end
end
diff --git a/spec/features/idv/doc_auth/document_capture_step_spec.rb b/spec/features/idv/doc_auth/document_capture_step_spec.rb
index 5fbf53d9bec..ff97df963f2 100644
--- a/spec/features/idv/doc_auth/document_capture_step_spec.rb
+++ b/spec/features/idv/doc_auth/document_capture_step_spec.rb
@@ -274,6 +274,8 @@
allow(LambdaJobs::Runner).to receive(:new).
with(hash_including(job_class: Idv::Proofer.document_job_class)).
and_call_original
+ allow(AppConfig.env).to receive(:ruby_workers_enabled).
+ and_return('false')
end
it 'proceeds to the next page with valid info' do
diff --git a/spec/features/multiple_emails/reset_password_spec.rb b/spec/features/multiple_emails/reset_password_spec.rb
index 84feff56cc4..5d646b7754f 100644
--- a/spec/features/multiple_emails/reset_password_spec.rb
+++ b/spec/features/multiple_emails/reset_password_spec.rb
@@ -40,7 +40,7 @@
)
mail = double
- expect(mail).to receive(:deliver_later)
+ expect(mail).to receive(:deliver_now)
expect(UserMailer).to receive(:unconfirmed_email_instructions).with(
instance_of(User),
unconfirmed_email_address.email,
diff --git a/spec/forms/idv/api_image_upload_form_spec.rb b/spec/forms/idv/api_image_upload_form_spec.rb
index b8c8d201384..83d81ccc068 100644
--- a/spec/forms/idv/api_image_upload_form_spec.rb
+++ b/spec/forms/idv/api_image_upload_form_spec.rb
@@ -100,7 +100,11 @@
context 'posting images to client fails' do
let(:failed_response) do
- IdentityDocAuth::Response.new(success: false, errors: { front: 'glare' })
+ IdentityDocAuth::Response.new(
+ success: false,
+ errors: { front: 'glare' },
+ extra: { remaining_attempts: AppConfig.env.acuant_max_attempts.to_i - 1 },
+ )
end
before do
allow(subject).to receive(:post_images_to_client).and_return(failed_response)
diff --git a/spec/forms/register_user_email_form_spec.rb b/spec/forms/register_user_email_form_spec.rb
index 6f8d7607986..d4f2962641d 100644
--- a/spec/forms/register_user_email_form_spec.rb
+++ b/spec/forms/register_user_email_form_spec.rb
@@ -13,7 +13,7 @@
mailer = instance_double(ActionMailer::MessageDelivery)
allow(UserMailer).to receive(:signup_with_your_email).
with(existing_user, existing_user.email).and_return(mailer)
- allow(mailer).to receive(:deliver_later)
+ allow(mailer).to receive(:deliver_now)
extra = {
email_already_exists: true,
@@ -28,7 +28,7 @@
with(success: true, errors: {}, extra: extra).and_return(result)
expect(subject.submit(email: 'TAKEN@gmail.com')).to eq result
expect(subject.email).to eq 'taken@gmail.com'
- expect(mailer).to have_received(:deliver_later)
+ expect(mailer).to have_received(:deliver_now)
end
end
diff --git a/spec/javascripts/packages/document-capture/components/button-spec.jsx b/spec/javascripts/packages/document-capture/components/button-spec.jsx
index 557c4088739..45d2c747f72 100644
--- a/spec/javascripts/packages/document-capture/components/button-spec.jsx
+++ b/spec/javascripts/packages/document-capture/components/button-spec.jsx
@@ -13,7 +13,7 @@ describe('document-capture/components/button', () => {
expect(button.nodeName).to.equal('BUTTON');
expect(button.type).to.equal('button');
expect(button.classList.contains('btn')).to.be.true();
- expect(button.classList.contains('btn-primary')).to.be.false();
+ expect(button.classList.contains('btn-primary')).to.be.true();
expect(button.classList.contains('btn-secondary')).to.be.false();
expect(button.classList.contains('btn-wide')).to.be.false();
expect(button.classList.contains('btn-link')).to.be.false();
@@ -32,8 +32,8 @@ describe('document-capture/components/button', () => {
expect(onClick.getCall(0).args[0]).to.equal('click');
});
- it('renders as primary', () => {
- const { getByText } = render(Click me);
+ it('renders as wide', () => {
+ const { getByText } = render(Click me);
const button = getByText('Click me');
@@ -43,8 +43,8 @@ describe('document-capture/components/button', () => {
expect(button.classList.contains('btn-link')).to.be.false();
});
- it('renders as secondary', () => {
- const { getByText } = render(Click me);
+ it('renders as outline', () => {
+ const { getByText } = render(Click me);
const button = getByText('Click me');
diff --git a/spec/jobs/address_proofing_job_spec.rb b/spec/jobs/address_proofing_job_spec.rb
new file mode 100644
index 00000000000..5b5fcfb7bac
--- /dev/null
+++ b/spec/jobs/address_proofing_job_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+RSpec.describe AddressProofingJob, type: :job do
+ it 'stores results' do
+ document_capture_session = DocumentCaptureSession.new(result_id: SecureRandom.hex)
+ encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt(
+ { applicant_pii: { phone: Faker::PhoneNumber.cell_phone } }.to_json,
+ )
+
+ AddressProofingJob.perform_now(
+ result_id: document_capture_session.result_id,
+ encrypted_arguments: encrypted_arguments, callback_url: nil, trace_id: nil
+ )
+
+ result = document_capture_session.load_proofing_result[:result]
+ expect(result).to be_present
+ end
+end
diff --git a/spec/jobs/document_proofing_job_spec.rb b/spec/jobs/document_proofing_job_spec.rb
new file mode 100644
index 00000000000..84a142116dc
--- /dev/null
+++ b/spec/jobs/document_proofing_job_spec.rb
@@ -0,0 +1,41 @@
+require 'rails_helper'
+
+RSpec.describe DocumentProofingJob, type: :job do
+ it 'stores results' do
+ encryption_helper = IdentityIdpFunctions::EncryptionHelper.new
+ encryption_key = SecureRandom.random_bytes(32)
+ front_image_iv = SecureRandom.random_bytes(12)
+ back_image_iv = SecureRandom.random_bytes(12)
+
+
+ stub_request(:get, 'http://example.com/front').
+ to_return(body: encryption_helper.encrypt(
+ data: '{}', key: encryption_key, iv: front_image_iv,
+ ))
+ stub_request(:get, 'http://example.com/back').
+ to_return(body: encryption_helper.encrypt(
+ data: '{}', key: encryption_key, iv: back_image_iv,
+ ))
+ document_arguments = {
+ encryption_key: Base64.encode64(encryption_key),
+ front_image_iv: Base64.encode64(front_image_iv),
+ back_image_iv: Base64.encode64(back_image_iv),
+ front_image_url: 'http://example.com/front',
+ back_image_url: 'http://example.com/back',
+ }
+
+ document_capture_session = DocumentCaptureSession.new(result_id: SecureRandom.hex)
+ encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt(
+ { document_arguments: document_arguments }.to_json,
+ )
+
+ DocumentProofingJob.perform_now(
+ result_id: document_capture_session.result_id,
+ liveness_checking_enabled: false, encrypted_arguments: encrypted_arguments,
+ callback_url: nil, trace_id: nil
+ )
+
+ result = document_capture_session.load_doc_auth_async_result
+ expect(result).to be_present
+ end
+end
diff --git a/spec/jobs/resolution_proofing_job_spec.rb b/spec/jobs/resolution_proofing_job_spec.rb
new file mode 100644
index 00000000000..49d67e71d5c
--- /dev/null
+++ b/spec/jobs/resolution_proofing_job_spec.rb
@@ -0,0 +1,29 @@
+require 'rails_helper'
+
+RSpec.describe ResolutionProofingJob, type: :job do
+ it 'stores results' do
+ pii = {
+ ssn: '444-55-8888',
+ first_name: Faker::Name.first_name,
+ zipcode: Faker::Address.zip_code,
+ state_id_number: '123456789',
+ state_id_type: 'drivers_license',
+ state_id_jurisdiction: Faker::Address.state_abbr,
+ }
+
+ document_capture_session = DocumentCaptureSession.new(result_id: SecureRandom.hex)
+ encrypted_arguments = Encryption::Encryptors::SessionEncryptor.new.encrypt(
+ { applicant_pii: pii }.to_json,
+
+ )
+
+ ResolutionProofingJob.perform_now(
+ result_id: document_capture_session.result_id, should_proof_state_id: false,
+ dob_year_only: false, encrypted_arguments: encrypted_arguments,
+ callback_url: nil, trace_id: nil
+ )
+
+ result = document_capture_session.load_proofing_result[:result]
+ expect(result).to be_present
+ end
+end
diff --git a/spec/lib/deploy/activate_spec.rb b/spec/lib/deploy/activate_spec.rb
index 100b7c6a91c..854aa2e62ee 100644
--- a/spec/lib/deploy/activate_spec.rb
+++ b/spec/lib/deploy/activate_spec.rb
@@ -17,6 +17,7 @@
let(:logger) { Logger.new('/dev/null') }
let(:s3_client) { Aws::S3::Client.new(stub_responses: true) }
let(:set_up_files!) {}
+ let(:instance_role) { 'idp' }
let(:result_yaml_path) { File.join(root, 'config', 'application.yml') }
let(:env_yaml_path) { File.join(root, 'config', 'application_s3_env.yml') }
@@ -35,6 +36,7 @@
context 'in a deployed production environment' do
before do
allow(Identity::Hostdata).to receive(:env).and_return('int')
+ allow(Identity::Hostdata).to receive(:instance_role).and_return(instance_role)
stub_request(:get, 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document').
to_return(body: {
@@ -42,11 +44,15 @@
'accountId' => '12345',
}.to_json)
- s3_client.stub_responses(
- :get_object,
- { body: application_yml },
- { body: geolite_content },
- { body: pwned_passwords_content },
+ s3_client.stub_responses(:get_object, proc do |context|
+ key = context.params[:key]
+ body = s3_contents[key]
+ if body.present?
+ { body: body }
+ else
+ raise Aws::S3::Errors::NoSuchKey.new(nil, nil)
+ end
+ end
)
allow(s3_client).to receive(:get_object).and_call_original
@@ -55,6 +61,14 @@
allow(subject).to receive(:setup_idp_config_symlinks)
end
+ let(:s3_contents) do
+ {
+ 'int/idp/v1/application.yml' => application_yml,
+ 'common/GeoIP2-City.mmdb' => geolite_content,
+ 'common/pwned-passwords.txt' => pwned_passwords_content,
+ }
+ end
+
let(:application_yml) do
<<~YAML
production:
@@ -89,6 +103,74 @@
expect(combined_application_yml['production']['lockout_period_in_minutes']).to eq('10')
end
+ context 'on a web instance' do
+ let(:instance_role) { 'idp' }
+
+ context 'when web.yml exists in s3' do
+ before do
+ s3_contents['int/idp/v1/web.yml'] = <<~YAML
+ web_yaml_value: 'true'
+ YAML
+ end
+
+ it 'merges web.yml into application.yml' do
+ subject.run
+
+ expect(File.exist?("#{root}/config/web.yml")).to eq(true)
+
+ combined_application_yml = YAML.load_file(result_yaml_path)
+ expect(combined_application_yml['web_yaml_value']).to eq('true')
+ end
+ end
+
+ context 'when web.yml does not exist in s3' do
+ it 'warns and leaves application.yml as-is' do
+ expect(logger).to receive(:warn).with(/web.yml/)
+
+ expect { subject.run }.to_not raise_error
+
+ expect(File.exist?("#{root}/config/web.yml")).to eq(false)
+
+ combined_application_yml = YAML.load_file(result_yaml_path)
+ expect(combined_application_yml).to_not have_key('web_yaml_value')
+ end
+ end
+ end
+
+ context 'on a worker instance' do
+ let(:instance_role) { 'worker' }
+
+ context 'when worker.yml exists in s3' do
+ before do
+ s3_contents['int/idp/v1/worker.yml'] = <<~YAML
+ worker_yaml_value: 'true'
+ YAML
+ end
+
+ it 'merges worker.yml into application.yml' do
+ subject.run
+
+ expect(File.exist?("#{root}/config/worker.yml")).to eq(true)
+
+ combined_application_yml = YAML.load_file(result_yaml_path)
+ expect(combined_application_yml['worker_yaml_value']).to eq('true')
+ end
+ end
+
+ context 'when worker.yml does not exist in s3' do
+ it 'warns and leaves application.yml as-is' do
+ expect(logger).to receive(:warn).with(/worker.yml/)
+
+ expect { subject.run }.to_not raise_error
+
+ expect(File.exist?("#{root}/config/worker.yml")).to eq(false)
+
+ combined_application_yml = YAML.load_file(result_yaml_path)
+ expect(combined_application_yml).to_not have_key('worker_yaml_value')
+ end
+ end
+ end
+
it 'sets the correct permissions on the YAML files' do
subject.run
diff --git a/spec/presenters/backup_code_create_presenter_spec.rb b/spec/presenters/backup_code_create_presenter_spec.rb
index a83cafc400d..09100abf721 100644
--- a/spec/presenters/backup_code_create_presenter_spec.rb
+++ b/spec/presenters/backup_code_create_presenter_spec.rb
@@ -44,7 +44,7 @@
describe '#continue_bttn_class' do
it 'displays as a link to continue' do
- expect(presenter.continue_bttn_class).to eq 'btn btn-link'
+ expect(presenter.continue_bttn_class).to eq 'usa-button usa-button--unstyled'
end
end
end
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index c55644f2fbc..5d7b39d2304 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -88,6 +88,11 @@
config.before(:each) do
IdentityDocAuth::Mock::DocAuthMockClient.reset!
+ original_queue_adapter = ActiveJob::Base.queue_adapter
+ descendants = ActiveJob::Base.descendants + [ActiveJob::Base]
+
+ ActiveJob::Base.queue_adapter = :inline
+ descendants.each(&:disable_test_adapter)
end
config.around(:each, type: :feature) do |example|
diff --git a/spec/services/account_reset/cancel_spec.rb b/spec/services/account_reset/cancel_spec.rb
index 836eb2daf5d..b01842e372f 100644
--- a/spec/services/account_reset/cancel_spec.rb
+++ b/spec/services/account_reset/cancel_spec.rb
@@ -57,7 +57,7 @@
it 'notifies the user via email of the account reset cancellation' do
token = create_account_reset_request_for(user)
- @mailer = instance_double(ActionMailer::MessageDelivery, deliver_later: true)
+ @mailer = instance_double(ActionMailer::MessageDelivery, deliver_now: true)
user.email_addresses.each do |email_address|
expect(UserMailer).to receive(:account_reset_cancel).with(user, email_address).
and_return(@mailer)
diff --git a/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb b/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb
index 8fa6a347772..8e4af7dc388 100644
--- a/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb
+++ b/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb
@@ -18,8 +18,8 @@
expect(UserMailer).to receive(:account_reset_cancel).
with(user, email_address2).and_return(mail2)
- expect(mail1).to receive(:deliver_later)
- expect(mail2).to receive(:deliver_later)
+ expect(mail1).to receive(:deliver_now)
+ expect(mail2).to receive(:deliver_now)
subject.call
end
diff --git a/spec/services/attribute_asserter_spec.rb b/spec/services/attribute_asserter_spec.rb
index bd12fa99490..efa8712e2be 100644
--- a/spec/services/attribute_asserter_spec.rb
+++ b/spec/services/attribute_asserter_spec.rb
@@ -14,7 +14,7 @@
)
end
let(:name_id_format) { Saml::Idp::Constants::NAME_ID_FORMAT_PERSISTENT }
- let(:service_provider_ial) { 2 }
+ let(:service_provider_ial) { 1 }
let(:service_provider_aal) { nil }
let(:service_provider) do
instance_double(
diff --git a/spec/services/email_notifier_spec.rb b/spec/services/email_notifier_spec.rb
index 3ac71d04903..17d82925749 100644
--- a/spec/services/email_notifier_spec.rb
+++ b/spec/services/email_notifier_spec.rb
@@ -19,7 +19,7 @@
UpdateUser.new(user: user, attributes: { email: 'new@example.com' }).call
expect(UserMailer).to receive(:email_changed).with(old_email).and_return(mailer)
- expect(mailer).to receive(:deliver_later)
+ expect(mailer).to receive(:deliver_now)
EmailNotifier.new(user).send_email_changed_email
end
diff --git a/spec/services/ial_context_spec.rb b/spec/services/ial_context_spec.rb
index 4f573dac3a7..57ac16f23c4 100644
--- a/spec/services/ial_context_spec.rb
+++ b/spec/services/ial_context_spec.rb
@@ -55,6 +55,56 @@
end
end
+ describe '#default_to_ial2?' do
+ context 'when the service provider is ial1 and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL1 }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial1 and ial is not requested' do
+ let(:sp_ial) { Idp::Constants::IAL1 }
+ let(:ial) { nil }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { Idp::Constants::IAL2 }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 and ial is not requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { nil }
+ it { expect(ial_context.default_to_ial2?).to eq(true) }
+ end
+
+ context 'when the service provider is ial2 and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 strict and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 strict and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { Idp::Constants::IAL2 }
+ it { expect(ial_context.default_to_ial2?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 strict and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { nil }
+ it { expect(ial_context.default_to_ial2?).to eq(true) }
+ end
+ end
+
describe '#ialmax_requested?' do
context 'when ialmax is requested' do
let(:ial) { Idp::Constants::IAL_MAX }
@@ -78,6 +128,54 @@
end
describe '#ial2_or_greater?' do
+ context 'when the service provider is ial1 and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL1 }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.ial2_or_greater?).to eq(false) }
+ end
+
+ context 'when the service provider is ial1 and ial is not requested' do
+ let(:sp_ial) { Idp::Constants::IAL1 }
+ let(:ial) { nil }
+ it { expect(ial_context.ial2_or_greater?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { Idp::Constants::IAL2 }
+ it { expect(ial_context.ial2_or_greater?).to eq(true) }
+ end
+
+ context 'when the service provider is ial2 and ial is not requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { nil }
+ it { expect(ial_context.ial2_or_greater?).to eq(true) }
+ end
+
+ context 'when the service provider is ial2 and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2 }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.ial2_or_greater?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 strict and ial1 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { Idp::Constants::IAL1 }
+ it { expect(ial_context.ial2_or_greater?).to eq(false) }
+ end
+
+ context 'when the service provider is ial2 strict and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { Idp::Constants::IAL2 }
+ it { expect(ial_context.ial2_or_greater?).to eq(true) }
+ end
+
+ context 'when the service provider is ial2 strict and ial2 is requested' do
+ let(:sp_ial) { Idp::Constants::IAL2_STRICT }
+ let(:ial) { nil }
+ it { expect(ial_context.ial2_or_greater?).to eq(true) }
+ end
+
context 'when ialmax is requested' do
let(:ial) { Idp::Constants::IAL_MAX }
it { expect(ial_context.ial2_or_greater?).to eq(false) }
diff --git a/spec/services/idv/agent_spec.rb b/spec/services/idv/agent_spec.rb
index 1e5fb6f7397..39f30242f63 100644
--- a/spec/services/idv/agent_spec.rb
+++ b/spec/services/idv/agent_spec.rb
@@ -2,6 +2,8 @@
require 'ostruct'
describe Idv::Agent do
+ include IdvHelper
+
let(:bad_phone) do
IdentityIdpFunctions::AddressMockClient::UNVERIFIABLE_PHONE_NUMBER
end
@@ -22,6 +24,7 @@
agent.proof_resolution(
document_capture_session, should_proof_state_id: true, trace_id: trace_id
)
+
result = document_capture_session.load_proofing_result.result
expect(result[:errors][:ssn]).to eq ['Unverified SSN.']
expect(result[:context][:stages]).to_not include(
@@ -52,6 +55,8 @@
context 'proofing partial date of birth' do
before do
allow(AppConfig.env).to receive(:proofing_send_partial_dob).and_return('true')
+ allow(AppConfig.env).to receive(:ruby_workers_enabled).
+ and_return('false')
end
it 'passes dob_year_only to the proofing function' do
@@ -87,6 +92,7 @@
agent.proof_resolution(
document_capture_session, should_proof_state_id: false, trace_id: trace_id
)
+
result = document_capture_session.load_proofing_result.result
expect(result[:context][:stages]).to_not include(
state_id: 'StateIdMock',
@@ -110,20 +116,6 @@
timed_out: true,
)
end
-
- it 'passes the right lexisnexis configs' do
- expect(LambdaJobs::Runner).to receive(:new).and_wrap_original do |impl, args|
- lexisnexis_config = args.dig(:in_process_config, :lexisnexis_config)
- expect(lexisnexis_config).to include(:instant_verify_workflow)
- expect(lexisnexis_config).to_not include(:phone_finder_workflow)
-
- impl.call(args)
- end
-
- agent.proof_resolution(
- document_capture_session, should_proof_state_id: true, trace_id: trace_id
- )
- end
end
describe '#proof_address' do
@@ -144,18 +136,6 @@
expect(result[:context][:stages]).to include({ address: 'AddressMock' })
expect(result[:success]).to eq false
end
-
- it 'passes the right lexisnexis configs' do
- expect(LambdaJobs::Runner).to receive(:new).and_wrap_original do |impl, args|
- lexisnexis_config = args.dig(:in_process_config, :lexisnexis_config)
- expect(lexisnexis_config).to_not include(:instant_verify_workflow)
- expect(lexisnexis_config).to include(:phone_finder_workflow)
-
- impl.call(args)
- end
-
- agent.proof_address(document_capture_session, trace_id: trace_id)
- end
end
end
end
diff --git a/spec/services/push_notification/http_push_spec.rb b/spec/services/push_notification/http_push_spec.rb
index 13a99a693fe..efa4b44ac36 100644
--- a/spec/services/push_notification/http_push_spec.rb
+++ b/spec/services/push_notification/http_push_spec.rb
@@ -1,4 +1,4 @@
-require 'spec_helper'
+require 'rails_helper'
RSpec.describe PushNotification::HttpPush do
include Rails.application.routes.url_helpers
@@ -22,20 +22,73 @@
)
end
let(:now) { Time.zone.now }
+ let(:risc_notifications_sqs_enabled) { 'false' }
+ let(:sqs_client) { Aws::SQS::Client.new(stub_responses: true) }
+ let(:sqs_queue_url) { 'https://some-queue-url.example.com/example' }
subject(:http_push) { PushNotification::HttpPush.new(event, now: now) }
+ before do
+ allow(AppConfig.env).to receive(:risc_notifications_sqs_enabled).
+ and_return(risc_notifications_sqs_enabled)
+ allow(Identity::Hostdata).to receive(:env).and_return('dev')
+
+ allow(http_push).to receive(:sqs_client).and_return(sqs_client)
+
+ sqs_client.stub_responses(
+ :get_queue_url,
+ { queue_url: sqs_queue_url },
+ )
+ end
+
describe '#deliver' do
subject(:deliver) { http_push.deliver }
- it 'makes an HTTP post to service providers with a push_notification_url' do
- stub_request(:post, sp_with_push_url.push_notification_url).
- with do |request|
- expect(request.headers['Content-Type']).to eq('application/secevent+jwt')
- expect(request.headers['Accept']).to eq('application/json')
+ context 'when the SQS queue is disabled' do
+ let(:risc_notifications_sqs_enabled) { 'false' }
+
+ it 'makes an HTTP post to service providers with a push_notification_url' do
+ stub_request(:post, sp_with_push_url.push_notification_url).
+ with do |request|
+ expect(request.headers['Content-Type']).to eq('application/secevent+jwt')
+ expect(request.headers['Accept']).to eq('application/json')
+
+ payload, headers = JWT.decode(
+ request.body,
+ AppArtifacts.store.oidc_public_key,
+ true,
+ algorithm: 'RS256',
+ )
+
+ expect(headers['typ']).to eq('secevent+jwt')
+
+ expect(payload['iss']).to eq(root_url)
+ expect(payload['iat']).to eq(now.to_i)
+ expect(payload['exp']).to eq((now + 12.hours).to_i)
+ expect(payload['aud']).to eq(sp_with_push_url.push_notification_url)
+ expect(payload['events']).to eq(event.event_type => event.payload.as_json)
+ end
+
+ deliver
+ end
+ end
+
+ context 'when the SQS queue is enabled' do
+ let(:risc_notifications_sqs_enabled) { 'true' }
+
+ it 'posts to the SQS queue' do
+ expect(sqs_client).to receive(:get_queue_url).
+ with(queue_name: 'dev-risc-notifications').
+ and_call_original
+
+ expect(sqs_client).to receive(:send_message) do |queue_url:, message_body:|
+ expect(queue_url).to eq(sqs_queue_url)
+
+ message = JSON.parse(message_body, symbolize_names: true)
+ expect(message[:push_notification_url]).to eq(sp_with_push_url.push_notification_url)
payload, headers = JWT.decode(
- request.body,
+ message[:jwt],
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
@@ -50,7 +103,8 @@
expect(payload['events']).to eq(event.event_type => event.payload.as_json)
end
- deliver
+ deliver
+ end
end
context 'with an event that sends agency-specific iss_sub' do
@@ -58,11 +112,35 @@
let(:agency_uuid) { AgencyIdentityLinker.new(sp_with_push_url_identity).link_identity.uuid }
- it 'sends the agency-specific uuid' do
- stub_request(:post, sp_with_push_url.push_notification_url).
- with do |request|
+ context 'when the SQS queue is disabled' do
+ let(:risc_notifications_sqs_enabled) { 'false' }
+
+ it 'sends the agency-specific uuid' do
+ stub_request(:post, sp_with_push_url.push_notification_url).
+ with do |request|
+ payload, _headers = JWT.decode(
+ request.body,
+ AppArtifacts.store.oidc_public_key,
+ true,
+ algorithm: 'RS256',
+ )
+
+ expect(payload['events'][event.event_type]['subject']['sub']).to eq(agency_uuid)
+ end
+
+ deliver
+ end
+ end
+
+ context 'when the SQS queue is enabled' do
+ let(:risc_notifications_sqs_enabled) { 'true' }
+
+ it 'sends the agency-specific uuid' do
+ expect(sqs_client).to receive(:send_message) do |queue_url:, message_body:|
+ message = JSON.parse(message_body, symbolize_names: true)
+
payload, _headers = JWT.decode(
- request.body,
+ message[:jwt],
AppArtifacts.store.oidc_public_key,
true,
algorithm: 'RS256',
@@ -71,7 +149,8 @@
expect(payload['events'][event.event_type]['subject']['sub']).to eq(agency_uuid)
end
- deliver
+ deliver
+ end
end
end
@@ -119,10 +198,24 @@
RevokeServiceProviderConsent.new(identity).call
end
- it 'does not notify that SP' do
- deliver
+ context 'when the SQS queue is disabled' do
+ let(:risc_notifications_sqs_enabled) { 'false' }
+
+ it 'does not notify that SP' do
+ deliver
- expect(WebMock).not_to have_requested(:get, sp_with_push_url.push_notification_url)
+ expect(WebMock).not_to have_requested(:get, sp_with_push_url.push_notification_url)
+ end
+ end
+
+ context 'when the SQS queue is enabled' do
+ let(:risc_notifications_sqs_enabled) { 'true' }
+
+ it 'does not notify that SP' do
+ expect(sqs_client).to_not receive(:send_message)
+
+ deliver
+ end
end
end
end
diff --git a/spec/services/send_sign_up_email_confirmation_spec.rb b/spec/services/send_sign_up_email_confirmation_spec.rb
index 334327f299e..d1f82c7c831 100644
--- a/spec/services/send_sign_up_email_confirmation_spec.rb
+++ b/spec/services/send_sign_up_email_confirmation_spec.rb
@@ -23,7 +23,7 @@
it 'sends the user an email with a confirmation link and the request id' do
email_address.update!(confirmed_at: Time.zone.now)
mail = double
- expect(mail).to receive(:deliver_later)
+ expect(mail).to receive(:deliver_now)
expect(UserMailer).to receive(:email_confirmation_instructions).with(
user,
email_address.email,
@@ -38,7 +38,7 @@
context 'when resetting a password' do
it 'sends an email with a link to try another email if the current email is unconfirmed' do
mail = double
- expect(mail).to receive(:deliver_later)
+ expect(mail).to receive(:deliver_now)
expect(UserMailer).to receive(:unconfirmed_email_instructions).with(
user,
email_address.email,
@@ -79,7 +79,7 @@
user.reload
mail = double
- expect(mail).to receive(:deliver_later)
+ expect(mail).to receive(:deliver_now)
expect(UserMailer).to receive(:email_confirmation_instructions).with(
user, email_address.email, confirmation_token, instance_of(Hash)
).and_return(mail)
diff --git a/spec/support/features/document_capture_step_helper.rb b/spec/support/features/document_capture_step_helper.rb
index 68d338da51b..abe5b1cb704 100644
--- a/spec/support/features/document_capture_step_helper.rb
+++ b/spec/support/features/document_capture_step_helper.rb
@@ -9,6 +9,8 @@ def attach_and_submit_images
if javascript_enabled? && !selfie_required?
click_on 'Submit'
# Wait for the background image job to finish and success flash to appear before continuing
+ expect(page).to have_content(t('doc_auth.headings.interstitial'))
+
Capybara.using_wait_time(3) do
expect(page).to have_content(t('doc_auth.headings.capture_complete'))
end
diff --git a/spec/support/features/idv_helper.rb b/spec/support/features/idv_helper.rb
index aa8012bf6a2..47a82fa609d 100644
--- a/spec/support/features/idv_helper.rb
+++ b/spec/support/features/idv_helper.rb
@@ -1,4 +1,6 @@
module IdvHelper
+ include ActiveJob::TestHelper
+
def self.included(base)
base.class_eval { include JavascriptDriverHelper }
end
diff --git a/spec/views/idv/doc_auth/_back.html.erb_spec.rb b/spec/views/idv/doc_auth/_back.html.erb_spec.rb
index d958670d545..75e397c1072 100644
--- a/spec/views/idv/doc_auth/_back.html.erb_spec.rb
+++ b/spec/views/idv/doc_auth/_back.html.erb_spec.rb
@@ -29,9 +29,7 @@
it 'renders' do
expect(subject).to have_selector("form[action='#{idv_doc_auth_step_path(step: 'redo_ssn')}']")
expect(subject).to have_selector('input[name="_method"][value="put"]', visible: false)
- expect(subject).to have_selector(
- ".btn[type='submit'][value='#{'‹ ' + t('forms.buttons.back')}']",
- )
+ expect(subject).to have_selector("[type='submit'][value='#{'‹ ' + t('forms.buttons.back')}']")
end
it_behaves_like 'back link with class'
diff --git a/yarn.lock b/yarn.lock
index fc2984c9ebd..f431ae34b34 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4834,10 +4834,10 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
dependencies:
postcss "^7.0.14"
-identity-style-guide@^5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/identity-style-guide/-/identity-style-guide-5.0.1.tgz#ada95337b503ed8ef46091696ba8a44a6061f0ac"
- integrity sha512-eailVodxMJCw51dFFUXOwqNvvKgCHnZt6xXYn+9Cizx2YrbzFcOkGu/OIiOGes/jBINnq4upxyUZStkv9IhVFw==
+identity-style-guide@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/identity-style-guide/-/identity-style-guide-5.0.3.tgz#2cc1391683a729749a8606dcc6a2bf69b58f7e45"
+ integrity sha512-OSoYQ+2yCE6zERwjSzkvp9XR/aEAvQzzPUQGCUnGskroa2O/FSM9p38w0YO0zH5+GGZUZqMWKYzeph96r01j2g==
dependencies:
uswds "^2.9.0"