Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
de34d61
LG-5184: Full page error on Acuant/LexisNexis outage
solipet Oct 19, 2021
ec1a7b8
go back to the root path
solipet Oct 26, 2021
80396db
vendor outage spec
solipet Oct 27, 2021
8d6fb7e
refactoring to better align with work from #5550
solipet Oct 27, 2021
245713d
update locales
solipet Oct 27, 2021
46cc2ae
rubocop
solipet Oct 27, 2021
21bf47e
removed unused locale keys
solipet Oct 27, 2021
982ab69
make normalize_yaml
solipet Oct 27, 2021
ccc783a
vendor statuses are stored as symbols
solipet Oct 27, 2021
47c221c
separate messaging if existing user hits idv flow
solipet Oct 28, 2021
c9b1835
refactor vendor_outage_concern
solipet Oct 29, 2021
a3eee3f
convert concern into a service object
solipet Oct 29, 2021
ac9a31f
final specs
solipet Oct 29, 2021
d7565c8
removed unused code
solipet Oct 29, 2021
2b43f2f
remove extra check for unknown vendor
solipet Oct 29, 2021
e5bda74
freeze vendor lists
solipet Oct 29, 2021
08df70d
don't need conditional check on `else`
solipet Oct 29, 2021
436de09
YARD comment for outage_message
solipet Oct 29, 2021
a7f78bc
add the sms/voice configs
solipet Oct 29, 2021
a731c55
don't need to specify title if same as heading
solipet Oct 29, 2021
d1c1cb6
remove fallback path on back partial
solipet Oct 29, 2021
a7ebd67
flatten the i18n keys
solipet Oct 29, 2021
c60cafe
record that vendor outage came from idv in separate session key
solipet Oct 29, 2021
f7f8d74
inline VendorStatus usages
solipet Oct 29, 2021
bb76f75
tighten up the guard clause
solipet Nov 1, 2021
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
11 changes: 11 additions & 0 deletions app/controllers/idv/doc_auth_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class DocAuthController < ApplicationController

before_action :override_document_capture_step_csp
before_action :update_if_skipping_upload
# rubocop:disable Rails/LexicallyScopedActionFilter
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

had to disable this cop since :show is not explicitly defined on this controller but via magic in the FSM

before_action :check_for_outage, only: :show
# rubocop:enable Rails/LexicallyScopedActionFilter

FSM_SETTINGS = {
step_url: :idv_doc_auth_step_url,
Expand Down Expand Up @@ -54,5 +57,13 @@ def do_meta_refresh(meta_refresh_count)
def flow_session
user_session['idv/doc_auth']
end

def check_for_outage
if VendorStatus.new.any_ial2_vendor_outage?
session[:vendor_outage_redirect] = current_step
session[:vendor_outage_redirect_from_idv] = true
redirect_to vendor_outage_url
end
end
end
end
11 changes: 11 additions & 0 deletions app/controllers/sign_up/registrations_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
module SignUp
class RegistrationsController < ApplicationController
include PhoneConfirmation
include ApplicationHelper # for ial2_requested?

before_action :confirm_two_factor_authenticated, only: [:destroy_confirm]
before_action :require_no_authentication
before_action :redirect_if_ial2_and_vendor_outage

CREATE_ACCOUNT = 'create_account'

def new
@register_user_email_form = RegisterUserEmailForm.new(analytics: analytics)
Expand Down Expand Up @@ -62,5 +66,12 @@ def sp_request_id

ServiceProviderRequestProxy.from_uuid(request_id).uuid
end

def redirect_if_ial2_and_vendor_outage
return unless ial2_requested? && VendorStatus.new.any_ial2_vendor_outage?

session[:vendor_outage_redirect] = CREATE_ACCOUNT
return redirect_to vendor_outage_url
end
end
end
11 changes: 11 additions & 0 deletions app/controllers/vendor_outage_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class VendorOutageController < ApplicationController
def show
vendor_status = VendorStatus.new(
sp: current_sp,
from: session.delete(:vendor_outage_redirect),
from_idv: session.delete(:vendor_outage_redirect_from_idv),
)
@specific_message = vendor_status.outage_message
vendor_status.track_event(analytics)
end
end
1 change: 1 addition & 0 deletions app/services/analytics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ def browser_attributes
USER_REGISTRATION_PERSONAL_KEY_VISIT = 'User Registration: personal key visited'.freeze
USER_REGISTRATION_PIV_CAC_DISABLED = 'User Registration: piv cac disabled'.freeze
USER_REGISTRATION_PIV_CAC_SETUP_VISIT = 'User Registration: piv cac setup visited'.freeze
VENDOR_OUTAGE = 'Vendor Outage'.freeze
WEBAUTHN_DELETED = 'WebAuthn Deleted'.freeze
WEBAUTHN_SETUP_VISIT = 'WebAuthn Setup Visited'.freeze
end
Expand Down
7 changes: 7 additions & 0 deletions app/services/status_page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class StatusPage
BASE_URL = 'https://logingov.statuspage.io/'.freeze

def self.base_url
BASE_URL
end
end
79 changes: 79 additions & 0 deletions app/services/vendor_status.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
class VendorStatus
def initialize(from: nil, from_idv: nil, sp: nil)
@from = from
@from_idv = from_idv
@sp = sp
end

IAL2_VENDORS = %i[acuant lexisnexis_instant_verify lexisnexis_trueid].freeze
ALL_VENDORS = (IAL2_VENDORS + %i[sms voice]).freeze

def vendor_outage?(vendor)
status = case vendor
when :acuant
IdentityConfig.store.vendor_status_acuant
when :lexisnexis_instant_verify
IdentityConfig.store.vendor_status_lexisnexis_instant_verify
when :lexisnexis_trueid
IdentityConfig.store.vendor_status_lexisnexis_trueid
when :sms
IdentityConfig.store.vendor_status_sms
when :voice
IdentityConfig.store.vendor_status_voice
Comment thread
solipet marked this conversation as resolved.
else
raise ArgumentError, "invalid vendor #{vendor}"
end
status != :operational
end

def any_vendor_outage?(vendors = ALL_VENDORS)
vendors.any? { |vendor| vendor_outage?(vendor) }
end

def any_ial2_vendor_outage?
any_vendor_outage?(IAL2_VENDORS)
end

def from_idv?
from_idv
end

# Returns an appropriate error message based upon the type of outage or what the user was doing
# when they encountered the outage.
#
# @return [String, nil] the localized message.
def outage_message
Comment thread
solipet marked this conversation as resolved.
if any_ial2_vendor_outage?
if from_idv?
if sp
return I18n.t(
'vendor_outage.idv_blocked.with_sp',
service_provider: sp.friendly_name,
)
else
return I18n.t('vendor_outage.idv_blocked.without_sp')
end
end

return I18n.t('vendor_outage.idv_blocked.generic')
end
end

def track_event(analytics)
raise ArgumentError, 'analytics instance required' if analytics.nil?

tracking_data = {
vendor_status: {
acuant: IdentityConfig.store.vendor_status_acuant,
lexisnexis_instant_verify: IdentityConfig.store.vendor_status_lexisnexis_instant_verify,
lexisnexis_trueid: IdentityConfig.store.vendor_status_lexisnexis_trueid,
},
redirect_from: from,
}
analytics.track_event(Analytics::VENDOR_OUTAGE, tracking_data)
end

private

attr_reader :from, :from_idv, :sp
end
22 changes: 22 additions & 0 deletions app/views/vendor_outage/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<%= render(
'idv/shared/error',
heading: t('vendor_outage.working'),
options: [
{
text: t('vendor_outage.get_updates_on_status_page'),
url: StatusPage.base_url,
new_tab: true,
},
{
text: t('idv.troubleshooting.options.contact_support', app_name: APP_NAME),
url: MarketingSite.contact_url,
new_tab: true,
},
],
) do %>
<p><%= @specific_message %></p>
<% end %>

<%= render(
'idv/doc_auth/back',
) %>
5 changes: 5 additions & 0 deletions config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ unauthorized_scope_enabled: 'false'
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"]'
vendor_status_acuant: 'operational'
vendor_status_lexisnexis_instant_verify: 'operational'
vendor_status_lexisnexis_trueid: 'operational'
vendor_status_sms: 'operational'
vendor_status_voice: 'operational'
verify_gpo_key_attempt_window_in_minutes: '10'
verify_gpo_key_max_attempts: '3'
verify_personal_key_attempt_window_in_minutes: '15'
Expand Down
16 changes: 16 additions & 0 deletions config/locales/vendor_outage/en.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
en:
vendor_outage:
get_updates_on_status_page: Get updates on our status page
idv_blocked:
generic: We are having technical difficulties on our end and cannot verify your
identity at this time. Please try again later.
with_sp: '%{service_provider} needs to make sure you are you — not someone
pretending to be you. Unfortunately, we are having technical
difficulties and cannot verify your identity at this time. Please try
again later.'
without_sp: The agency that you are trying to access needs to make sure you are
you — not someone pretending to be you. Unfortunately, we are having
technical difficulties and cannot verify your identity at this time.
Please try again later.
working: We are working to resolve an error
17 changes: 17 additions & 0 deletions config/locales/vendor_outage/es.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
es:
vendor_outage:
get_updates_on_status_page: Reciba actualizaciones en nuestra página de estado
idv_blocked:
generic: Debido a problemas técnicos por nuestra parte, no podemos verificar su
identidad en estos momentos. Por favor, inténtelo nuevamente más tarde.
with_sp: '%{service_provider} necesita asegurarse de que es usted realmente y no
alguien que se hace pasar por usted. Lamentablemente, debido a problemas
técnicos por nuestra parte, tal vez no podamos verificar su identidad en
estos momentos. Por favor, inténtelo nuevamente más tarde.'
without_sp: La agencia a la que está intentando acceder debe asegurarse de que
usted sea quien dice ser, y no alguien que se hace pasar por usted.
Lamentablemente, debido a problemas técnicos por nuestra parte, tal vez
no podamos verificar su identidad en estos momentos. Por favor,
inténtelo nuevamente más tarde.
working: Estamos trabajando para corregir un error
17 changes: 17 additions & 0 deletions config/locales/vendor_outage/fr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
fr:
vendor_outage:
get_updates_on_status_page: Obtenez des mises à jour sur notre page de statut
idv_blocked:
generic: Nous rencontrons des difficultés techniques et ne pouvons pas vérifier
votre identité pour le moment. Veuillez réessayer plus tard.
with_sp: "%{service_provider} doit s'assurer que c'est bien vous — et non
quelqu'un qui se fait passer pour vous. Malheureusement, nous
rencontrons des difficultés techniques et ne pouvons pas vérifier votre
identité pour le moment. Veuillez réessayer plus tard."
without_sp: L’agence à laquelle vous essayez d’accéder doit s’assurer qu’il
s’agit bien de vous, et non de quelqu’un qui se fait passer pour vous.
Malheureusement, nous rencontrons des difficultés techniques et ne
pouvons pas vérifier votre identité pour le moment. Veuillez réessayer
plus tard.
working: "Nous travaillons à la résolution d'une erreur"
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@

get '/errors/service_provider_inactive' => 'users/service_provider_inactive#index',
as: :sp_inactive_error
get '/errors/vendor' => 'vendor_outage#show', as: :vendor_outage

get '/events/disavow' => 'event_disavowal#new', as: :event_disavowal
post '/events/disavow' => 'event_disavowal#create', as: :events_disavowal
Expand Down
13 changes: 12 additions & 1 deletion lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ class IdentityConfig
GIT_TAG = `git tag --points-at HEAD`.chomp.split("\n").first
GIT_BRANCH = `git rev-parse --abbrev-ref HEAD`.chomp

VENDOR_STATUS_OPTIONS = %i[operational partial_outage full_outage]

class << self
attr_reader :store
end

CONVERTERS = {
string: proc { |value| value.to_s },
symbol: proc { |value| value.to_sym },
comma_separated_string_list: proc do |value|
value.split(',')
end,
Expand Down Expand Up @@ -44,11 +47,14 @@ def initialize(read_env)
@written_env = {}
end

def add(key, type: :string, is_sensitive: false, allow_nil: false, options: {})
def add(key, type: :string, is_sensitive: false, allow_nil: false, enum: nil, options: {})
value = @read_env[key]

converted_value = CONVERTERS.fetch(type).call(value, options: options) if !value.nil?
raise "#{key} is required but is not present" if converted_value.nil? && !allow_nil
if enum && !enum.include?(converted_value)
raise "unexpected #{key}: #{value}, expected one of #{enum}"
end
Comment on lines +55 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

codeclimate is warning this raise is not covered, can we add some tests in identity_config_spec that exercise this new enum logic?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Erm... I don't see identity_config_spec.rb?! 😕

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Oh dang I thought we had one 😭


@written_env[key] = converted_value
@written_env
Expand Down Expand Up @@ -199,6 +205,11 @@ def self.build_store(config_map)
config.add(:otps_per_ip_limit, type: :integer)
config.add(:otps_per_ip_period, type: :integer)
config.add(:otps_per_ip_track_only_mode, type: :boolean)
config.add(:vendor_status_acuant, type: :symbol, enum: VENDOR_STATUS_OPTIONS)
Comment thread
solipet marked this conversation as resolved.
config.add(:vendor_status_lexisnexis_instant_verify, type: :symbol, enum: VENDOR_STATUS_OPTIONS)
config.add(:vendor_status_lexisnexis_trueid, type: :symbol, enum: VENDOR_STATUS_OPTIONS)
config.add(:vendor_status_sms, type: :symbol, enum: VENDOR_STATUS_OPTIONS)
config.add(:vendor_status_voice, type: :symbol, enum: VENDOR_STATUS_OPTIONS)
config.add(:outbound_connection_check_retry_count, type: :integer)
config.add(:outbound_connection_check_timeout, type: :integer)
config.add(:outbound_connection_check_url)
Expand Down
29 changes: 29 additions & 0 deletions spec/controllers/vendor_outage_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
require 'rails_helper'

describe VendorOutageController do
before do
stub_analytics
allow(@analytics).to receive(:track_event)
end

let(:redirect_from) { nil }
let(:tracking_data) do
{
vendor_status: {
acuant: IdentityConfig.store.vendor_status_acuant,
lexisnexis_instant_verify: IdentityConfig.store.vendor_status_lexisnexis_instant_verify,
lexisnexis_trueid: IdentityConfig.store.vendor_status_lexisnexis_trueid,
},
redirect_from: redirect_from,
}
end

it 'tracks an analytics event' do
get :show

expect(@analytics).to have_received(:track_event).with(
Analytics::VENDOR_OUTAGE,
tracking_data,
)
end
end
56 changes: 56 additions & 0 deletions spec/features/idv/vendor_outage_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
require 'rails_helper'

feature 'vendor_outage_spec' do
include PersonalKeyHelper
include IdvStepHelper

let(:user) { create(:user, :signed_up) }
let(:new_password) { 'some really awesome new password' }
let(:pii) { { ssn: '666-66-1234', dob: '1920-01-01', first_name: 'alice' } }

%w[acuant lexisnexis_instant_verify lexisnexis_trueid].each do |service|
context "full outage on #{service}" do
before do
allow(IdentityConfig.store).to receive("vendor_status_#{service}".to_sym).
and_return(:full_outage)
end

it 'prevents an existing ial1 user from verifying their identity' do
visit_idp_from_sp_with_ial2(:oidc)
sign_in_user(user_with_2fa)
fill_in_code_with_last_phone_otp
click_submit_default
expect(current_path).to eq vendor_outage_path
expect(page).to have_content(
t('vendor_outage.idv_blocked.with_sp', service_provider: 'Test SP'),
)
end

it 'prevents a user who reset their password from reactivating profile with no personal key',
email: true, js: true do
personal_key_from_pii(user, pii)
trigger_reset_password_and_click_email_link(user.email)
reset_password(user, new_password)

visit new_user_session_path
signin(user.email, new_password)
fill_in_code_with_last_phone_otp
click_submit_default

click_link t('account.index.reactivation.link')
click_on t('links.account.reactivate.without_key')
click_on t('forms.buttons.continue')

expect(current_path).to eq vendor_outage_path
expect(page).to have_content(t('vendor_outage.idv_blocked.without_sp'))
end

it 'prevents a user from creating an account' do
visit_idp_from_sp_with_ial2(:oidc)
click_link t('links.create_account')
expect(current_path).to eq vendor_outage_path
expect(page).to have_content(t('vendor_outage.idv_blocked.generic'))
end
end
end
end
Loading