diff --git a/.reek b/.reek
index 37bd18db79d..4eee4bbb042 100644
--- a/.reek
+++ b/.reek
@@ -24,6 +24,7 @@ FeatureEnvy:
- Pii::Attributes#[]=
- OpenidConnectLogoutForm#load_identity
- Idv::ProfileMaker#pii_from_applicant
+ - Idv::Step#vendor_validator_result
InstanceVariableAssumption:
exclude:
- User
@@ -41,6 +42,7 @@ NilCheck:
LongParameterList:
exclude:
- IdentityLinker#optional_attributes
+ - VendorValidatorJob#perform
RepeatedConditional:
exclude:
- Users::ResetPasswordsController
@@ -53,6 +55,7 @@ TooManyInstanceVariables:
exclude:
- OpenidConnectAuthorizeForm
- OpenidConnectRedirector
+ - Idv::VendorResult
TooManyStatements:
max_statements: 6
exclude:
@@ -72,6 +75,7 @@ TooManyMethods:
- OpenidConnect::AuthorizationController
- Idv::Session
- User
+ - Verify::SessionsController
UncommunicativeMethodName:
exclude:
- PhoneConfirmationFlow
@@ -89,6 +93,7 @@ UtilityFunction:
public_methods_only: true
exclude:
- AnalyticsEventJob#perform
+ - ApplicationController#default_url_options
- ApplicationHelper#step_class
- PersonalKeyFormatter#regexp
- SessionTimeoutWarningHelper#frequency
@@ -97,6 +102,7 @@ UtilityFunction:
- SessionDecorator
- WorkerHealthChecker::Middleware#call
- UserEncryptedAttributeOverrides#create_fingerprint
+ - LocaleHelper#locale_url_param
'app/controllers':
InstanceVariableAssumption:
enabled: false
diff --git a/.rubocop.yml b/.rubocop.yml
index b2afe411f82..a8a5847908d 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -93,6 +93,9 @@ Metrics/ModuleLength:
- spec/**/*
- 'app/controllers/concerns/two_factor_authenticatable.rb'
+Metrics/ParameterLists:
+ CountKeywordArgs: false
+
# This is a Rails 5 feature, so it should be disabled until we upgrade
Rails/HttpPositionalArguments:
Description: 'Use keyword arguments instead of positional arguments in http method calls.'
diff --git a/app/assets/images/sp-logos/cbp-ttp.png b/app/assets/images/sp-logos/cbp-ttp.png
new file mode 100644
index 00000000000..2beaf6fc93a
Binary files /dev/null and b/app/assets/images/sp-logos/cbp-ttp.png differ
diff --git a/app/assets/javascripts/app/components/accordion.js b/app/assets/javascripts/app/components/accordion.js
index 2724585c6e5..14f5da6239d 100644
--- a/app/assets/javascripts/app/components/accordion.js
+++ b/app/assets/javascripts/app/components/accordion.js
@@ -72,6 +72,7 @@ class Accordion extends Events {
this.content.classList.add('shown');
this.content.classList.remove('animate-out');
this.content.classList.add('animate-in');
+ this.content.setAttribute('aria-hidden', 'false');
this.emit('accordion.show');
}
@@ -81,6 +82,7 @@ class Accordion extends Events {
this.shownIcon.classList.add('display-none');
this.content.classList.remove('animate-in');
this.content.classList.add('animate-out');
+ this.content.setAttribute('aria-hidden', 'true');
this.emit('accordion.hide');
this.header.focus();
}
diff --git a/app/assets/javascripts/app/form-validation.js b/app/assets/javascripts/app/form-validation.js
index 7d82dec7729..7d534d6e6c0 100644
--- a/app/assets/javascripts/app/form-validation.js
+++ b/app/assets/javascripts/app/form-validation.js
@@ -10,7 +10,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (input) {
input.addEventListener('input', () => {
if (input.validity.patternMismatch) {
- input.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${f}`));
+ input.setCustomValidity(I18n.t(`idv.errors.pattern_mismatch.${I18n.key(f)}`));
} else {
input.setCustomValidity('');
}
diff --git a/app/assets/javascripts/misc/i18n-strings.js.erb b/app/assets/javascripts/misc/i18n-strings.js.erb
index 4c5fa4c198d..99bab39214d 100644
--- a/app/assets/javascripts/misc/i18n-strings.js.erb
+++ b/app/assets/javascripts/misc/i18n-strings.js.erb
@@ -5,7 +5,7 @@ window.LoginGov = window.LoginGov || {};
'errors.messages.missing_field',
'forms.passwords.show',
'idv.errors.pattern_mismatch.dob',
- 'idv.errors.pattern_mismatch.personal-key',
+ 'idv.errors.pattern_mismatch.personal_key',
'idv.errors.pattern_mismatch.ssn',
'idv.errors.pattern_mismatch.zipcode',
'idv.modal.button.warning',
@@ -16,40 +16,45 @@ window.LoginGov = window.LoginGov || {};
'instructions.password.strength.v',
'links.remove',
'valid_email.validations.email.invalid',
- 'zxcvbn.feedback.Use a few words, avoid common phrases',
- 'zxcvbn.feedback.No need for symbols, digits, or uppercase letters',
- 'zxcvbn.feedback.Add another word or two_ Uncommon words are better_',
- 'zxcvbn.feedback.Straight rows of keys are easy to guess',
- 'zxcvbn.feedback.Short keyboard patterns are easy to guess',
- 'zxcvbn.feedback.Use a longer keyboard pattern with more turns',
- 'zxcvbn.feedback.Repeats like "aaa" are easy to guess',
- 'zxcvbn.feedback.Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
- 'zxcvbn.feedback.Avoid repeated words and characters',
- 'zxcvbn.feedback.Sequences like abc or 6543 are easy to guess',
- 'zxcvbn.feedback.Avoid sequences',
- 'zxcvbn.feedback.Recent years are easy to guess',
- 'zxcvbn.feedback.Avoid recent years',
- 'zxcvbn.feedback.Avoid years that are associated with you',
- 'zxcvbn.feedback.Dates are often easy to guess',
- 'zxcvbn.feedback.Avoid dates and years that are associated with you',
- 'zxcvbn.feedback.This is a top-10 common password',
- 'zxcvbn.feedback.This is a top-100 common password',
- 'zxcvbn.feedback.This is a very common password',
- 'zxcvbn.feedback.This is similar to a commonly used password',
- 'zxcvbn.feedback.A word by itself is easy to guess',
- 'zxcvbn.feedback.Names and surnames by themselves are easy to guess',
- 'zxcvbn.feedback.Common names and surnames are easy to guess',
- 'zxcvbn.feedback.Capitalization doesn\'t help very much',
- 'zxcvbn.feedback.All-uppercase is almost as easy to guess as all-lowercase',
- 'zxcvbn.feedback.Reversed words aren\'t much harder to guess',
- 'zxcvbn.feedback.Predictable substitutions like \'@\' instead of \'a\' don\'t help very much'
+ 'zxcvbn.feedback.a_word_by_itself_is_easy_to_guess',
+ 'zxcvbn.feedback.add_another_word_or_two_uncommon_words_are_better',
+ 'zxcvbn.feedback.all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase',
+ 'zxcvbn.feedback.avoid_dates_and_years_that_are_associated_with_you',
+ 'zxcvbn.feedback.avoid_recent_years',
+ 'zxcvbn.feedback.avoid_repeated_words_and_characters',
+ 'zxcvbn.feedback.avoid_sequences',
+ 'zxcvbn.feedback.avoid_years_that_are_associated_with_you',
+ 'zxcvbn.feedback.capitalization_doesnt_help_very_much',
+ 'zxcvbn.feedback.common_names_and_surnames_are_easy_to_guess',
+ 'zxcvbn.feedback.dates_are_often_easy_to_guess',
+ 'zxcvbn.feedback.names_and_surnames_by_themselves_are_easy_to_guess',
+ 'zxcvbn.feedback.there_is_no_need_for_symbols_digits_or_uppercase_letters',
+ 'zxcvbn.feedback.predictable_substitutions_like__instead_of_a_dont_help_very_much',
+ 'zxcvbn.feedback.recent_years_are_easy_to_guess',
+ 'zxcvbn.feedback.repeats_like_aaa_are_easy_to_guess',
+ 'zxcvbn.feedback.repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc',
+ 'zxcvbn.feedback.reversed_words_arent_much_harder_to_guess',
+ 'zxcvbn.feedback.sequences_like_abc_or_6543_are_easy_to_guess',
+ 'zxcvbn.feedback.short_keyboard_patterns_are_easy_to_guess',
+ 'zxcvbn.feedback.straight_rows_of_keys_are_easy_to_guess',
+ 'zxcvbn.feedback.this_is_a_top_10_common_password',
+ 'zxcvbn.feedback.this_is_a_top_100_common_password',
+ 'zxcvbn.feedback.this_is_a_very_common_password',
+ 'zxcvbn.feedback.this_is_similar_to_a_commonly_used_password',
+ 'zxcvbn.feedback.for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases',
+ 'zxcvbn.feedback.use_a_longer_keyboard_pattern_with_more_turns'
] %>
window.LoginGov.I18n = {
+ currentLocale: function() { return this.__currentLocale || (this.__currentLocale = document.querySelector('html').lang); },
strings: {},
- t: function(key) { return this.strings[key]; }
+ t: function(key) { return this.strings[this.currentLocale()][key]; },
+ key: function(key) { return key.replace(/[ -]/g, '_').replace(/\W/g, '').toLowerCase(); }
};
-<% keys.each do |key| %>
-window.LoginGov.I18n.strings['<%= ActionController::Base.helpers.j key %>'] = '<%= ActionController::Base.helpers.j I18n.t(key) %>';
+<% I18n.available_locales.each do |locale| %>
+ window.LoginGov.I18n.strings['<%= ActionController::Base.helpers.j locale.to_s %>'] = {};
+ <% keys.each do |key| %>
+ window.LoginGov.I18n.strings['<%= ActionController::Base.helpers.j locale.to_s %>']['<%= ActionController::Base.helpers.j key %>'] = '<%= ActionController::Base.helpers.j I18n.t(key, locale: locale) %>';
+ <% end %>
<% end %>
diff --git a/app/assets/javascripts/misc/pw-strength.js b/app/assets/javascripts/misc/pw-strength.js
index ae0b444be1b..82c0a79c92a 100644
--- a/app/assets/javascripts/misc/pw-strength.js
+++ b/app/assets/javascripts/misc/pw-strength.js
@@ -27,8 +27,7 @@ function getFeedback(z) {
const { warning, suggestions } = z.feedback;
function lookup(str) {
- const strFormatted = str.replace(/\./g, '_');
- return I18n.t(`zxcvbn.feedback.${strFormatted}`);
+ return I18n.t(`zxcvbn.feedback.${I18n.key(str)}`);
}
if (!warning && !suggestions.length) return '';
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ec5518ca0a3..c623af396b6 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base
include UserSessionContext
include VerifyProfileConcern
+ include LocaleHelper
FLASH_KEYS = %w[alert error notice success warning].freeze
@@ -46,10 +47,17 @@ def create_user_event(event_type, user = current_user)
def decorated_session
@_decorated_session ||= DecoratedSession.new(
- sp: current_sp, view_context: view_context, sp_session: sp_session
+ sp: current_sp,
+ view_context: view_context,
+ sp_session: sp_session,
+ service_provider_request: service_provider_request
).call
end
+ def default_url_options
+ { locale: locale_url_param }
+ end
+
private
def disable_caching
@@ -74,11 +82,14 @@ def sp_from_sp_session
end
def sp_from_request_id
- issuer = ServiceProviderRequest.from_uuid(params[:request_id]).issuer
- sp = ServiceProvider.from_issuer(issuer)
+ sp = ServiceProvider.from_issuer(service_provider_request.issuer)
sp if sp.is_a? ServiceProvider
end
+ def service_provider_request
+ @service_provider_request ||= ServiceProviderRequest.from_uuid(params[:request_id])
+ end
+
def after_sign_in_path_for(user)
stored_location_for(user) || sp_session[:request_url] || signed_in_path
end
@@ -130,9 +141,7 @@ def skip_session_expiration
end
def set_locale
- I18n.locale =
- http_accept_language.compatible_language_from(I18n.available_locales) ||
- I18n.default_locale
+ I18n.locale = LocaleChooser.new(params[:locale], request).locale
end
def sp_session
diff --git a/app/controllers/concerns/account_recovery_concern.rb b/app/controllers/concerns/account_reactivation_concern.rb
similarity index 90%
rename from app/controllers/concerns/account_recovery_concern.rb
rename to app/controllers/concerns/account_reactivation_concern.rb
index 46a36b11026..4c0140fcbcf 100644
--- a/app/controllers/concerns/account_recovery_concern.rb
+++ b/app/controllers/concerns/account_reactivation_concern.rb
@@ -1,4 +1,4 @@
-module AccountRecoveryConcern
+module AccountReactivationConcern
extend ActiveSupport::Concern
def confirm_password_reset_profile
diff --git a/app/controllers/concerns/idv_failure_concern.rb b/app/controllers/concerns/idv_failure_concern.rb
index 689a3e61205..be6348ad420 100644
--- a/app/controllers/concerns/idv_failure_concern.rb
+++ b/app/controllers/concerns/idv_failure_concern.rb
@@ -5,7 +5,7 @@ def render_failure
if step_attempts_exceeded?
@view_model = view_model(error: 'fail')
flash_message(type: :error)
- elsif step.form_valid_but_vendor_validation_failed?
+ elsif form_valid_but_vendor_validation_failed?
@view_model = view_model(error: 'warning')
flash_message(type: :warning)
else
@@ -13,6 +13,10 @@ def render_failure
end
end
+ def form_valid_but_vendor_validation_failed?
+ idv_form.valid? && !step.vendor_validation_passed?
+ end
+
def flash_message(type:)
flash.now[type.to_sym] = @view_model.flash_message
end
diff --git a/app/controllers/concerns/idv_session.rb b/app/controllers/concerns/idv_session.rb
index 271de8555f2..eee58296772 100644
--- a/app/controllers/concerns/idv_session.rb
+++ b/app/controllers/concerns/idv_session.rb
@@ -2,6 +2,7 @@ module IdvSession
extend ActiveSupport::Concern
def confirm_idv_session_started
+ return if current_user.decorate.needs_profile_usps_verification?
redirect_to verify_session_url if idv_session.params.blank?
end
@@ -39,4 +40,8 @@ def idv_vendor
def idv_attempter
@_idv_attempter ||= Idv::Attempter.new(current_user)
end
+
+ def vendor_validator_result
+ VendorValidatorResultStorage.new.load(idv_session.async_result_id)
+ end
end
diff --git a/app/controllers/reactivate_account_controller.rb b/app/controllers/reactivate_account_controller.rb
index a9eff6cc5e4..dbb0eccba49 100644
--- a/app/controllers/reactivate_account_controller.rb
+++ b/app/controllers/reactivate_account_controller.rb
@@ -1,5 +1,5 @@
class ReactivateAccountController < ApplicationController
- include AccountRecoveryConcern
+ include AccountReactivationConcern
before_action :confirm_two_factor_authenticated
before_action :confirm_password_reset_profile
diff --git a/app/controllers/sign_out_controller.rb b/app/controllers/sign_out_controller.rb
new file mode 100644
index 00000000000..84c24226cfe
--- /dev/null
+++ b/app/controllers/sign_out_controller.rb
@@ -0,0 +1,13 @@
+class SignOutController < ApplicationController
+ include FullyAuthenticatable
+
+ skip_before_action :handle_two_factor_authentication
+
+ def destroy
+ path_after_cancellation = decorated_session.cancel_link_path
+ sign_out
+ flash[:success] = t('devise.sessions.signed_out')
+ redirect_to path_after_cancellation
+ delete_branded_experience
+ end
+end
diff --git a/app/controllers/users/phones_controller.rb b/app/controllers/users/phones_controller.rb
index 3fbeaadf54f..564ff586ce6 100644
--- a/app/controllers/users/phones_controller.rb
+++ b/app/controllers/users/phones_controller.rb
@@ -11,7 +11,7 @@ def edit
def update
@update_user_phone_form = UpdateUserPhoneForm.new(current_user)
- if @update_user_phone_form.submit(user_params)
+ if @update_user_phone_form.submit(user_params).success?
process_updates
bypass_sign_in current_user
else
diff --git a/app/controllers/users/verify_password_controller.rb b/app/controllers/users/verify_password_controller.rb
index 67bea6d230f..2dc268cb5e4 100644
--- a/app/controllers/users/verify_password_controller.rb
+++ b/app/controllers/users/verify_password_controller.rb
@@ -1,6 +1,6 @@
module Users
class VerifyPasswordController < ApplicationController
- include AccountRecoveryConcern
+ include AccountReactivationConcern
before_action :confirm_two_factor_authenticated
before_action :confirm_password_reset_profile
diff --git a/app/controllers/users/verify_personal_key_controller.rb b/app/controllers/users/verify_personal_key_controller.rb
index 6316fc9441b..0e7fbe39643 100644
--- a/app/controllers/users/verify_personal_key_controller.rb
+++ b/app/controllers/users/verify_personal_key_controller.rb
@@ -1,10 +1,10 @@
module Users
class VerifyPersonalKeyController < ApplicationController
- include AccountRecoveryConcern
+ include AccountReactivationConcern
before_action :confirm_two_factor_authenticated
before_action :confirm_password_reset_profile
- before_action :init_account_recovery, only: [:new]
+ before_action :init_account_reactivation, only: [:new]
def new
@personal_key_form = VerifyPersonalKeyForm.new(
@@ -25,10 +25,10 @@ def create
private
- def init_account_recovery
+ def init_account_reactivation
return if reactivate_account_session.started?
- flash.now[:notice] = t('notices.account_recovery')
+ flash.now[:notice] = t('notices.account_reactivation')
reactivate_account_session.start
end
diff --git a/app/controllers/verify/finance_controller.rb b/app/controllers/verify/finance_controller.rb
index e41a5af7c4f..258a96be46a 100644
--- a/app/controllers/verify/finance_controller.rb
+++ b/app/controllers/verify/finance_controller.rb
@@ -5,6 +5,8 @@ class FinanceController < ApplicationController
before_action :confirm_step_needed
before_action :confirm_step_allowed
+ before_action :submit_idv_form, only: [:create]
+ before_action :submit_idv_job, only: [:create]
def new
@view_model = view_model
@@ -13,7 +15,7 @@ def new
def create
result = step.submit
- analytics.track_event(Analytics::IDV_FINANCE_CONFIRMATION, result.to_h)
+ analytics.track_event(Analytics::IDV_FINANCE_CONFIRMATION_VENDOR, result.to_h)
increment_step_attempts
if result.success?
@@ -26,6 +28,24 @@ def create
private
+ def submit_idv_form
+ result = idv_form.submit(step_params)
+ analytics.track_event(Analytics::IDV_FINANCE_CONFIRMATION_FORM, result.to_h)
+
+ return if result.success?
+
+ @view_model = view_model
+ render_form
+ end
+
+ def submit_idv_job
+ SubmitIdvJob.new(
+ vendor_validator_class: Idv::FinancialsValidator,
+ idv_session: idv_session,
+ vendor_params: vendor_params
+ ).call
+ end
+
def step_name
:financials
end
@@ -38,12 +58,12 @@ def view_model(error: nil)
Verify::FinancialsNew.new(
error: error,
remaining_attempts: remaining_step_attempts,
- idv_form: idv_finance_form
+ idv_form: idv_form
)
end
- def idv_finance_form
- @_idv_finance_form ||= Idv::FinanceForm.new(idv_session.params)
+ def idv_form
+ @_idv_form ||= Idv::FinanceForm.new(idv_session.params)
end
def handle_success
@@ -53,9 +73,9 @@ def handle_success
def step
@_step ||= Idv::FinancialsStep.new(
- idv_form: idv_finance_form,
+ idv_form_params: idv_form.idv_params,
idv_session: idv_session,
- params: step_params
+ vendor_validator_result: vendor_validator_result
)
end
@@ -70,5 +90,10 @@ def render_form
render 'verify/finance_other/new'
end
end
+
+ def vendor_params
+ finance_type = idv_form.finance_type
+ { finance_type => idv_form.idv_params[finance_type] }
+ end
end
end
diff --git a/app/controllers/verify/phone_controller.rb b/app/controllers/verify/phone_controller.rb
index 0a941349b2f..1f498df83e5 100644
--- a/app/controllers/verify/phone_controller.rb
+++ b/app/controllers/verify/phone_controller.rb
@@ -5,6 +5,8 @@ class PhoneController < ApplicationController
before_action :confirm_step_needed
before_action :confirm_step_allowed
+ before_action :submit_idv_form, only: [:create]
+ before_action :submit_idv_job, only: [:create]
def new
@view_model = view_model
@@ -13,7 +15,7 @@ def new
def create
result = step.submit
- analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION, result.to_h)
+ analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION_VENDOR, result.to_h)
increment_step_attempts
if result.success?
@@ -26,15 +28,33 @@ def create
private
+ def submit_idv_form
+ result = idv_form.submit(step_params)
+ analytics.track_event(Analytics::IDV_PHONE_CONFIRMATION_FORM, result.to_h)
+
+ return if result.success?
+
+ @view_model = view_model
+ render :new
+ end
+
+ def submit_idv_job
+ SubmitIdvJob.new(
+ vendor_validator_class: Idv::PhoneValidator,
+ idv_session: idv_session,
+ vendor_params: idv_form.phone
+ ).call
+ end
+
def step_name
:phone
end
def step
@_step ||= Idv::PhoneStep.new(
- idv_form: idv_phone_form,
idv_session: idv_session,
- params: step_params
+ idv_form_params: idv_form.idv_params,
+ vendor_validator_result: vendor_validator_result
)
end
@@ -42,7 +62,7 @@ def view_model(error: nil)
Verify::PhoneNew.new(
error: error,
remaining_attempts: remaining_step_attempts,
- idv_form: idv_phone_form
+ idv_form: idv_form
)
end
@@ -54,8 +74,8 @@ def confirm_step_needed
redirect_to verify_review_path if idv_session.phone_confirmation == true
end
- def idv_phone_form
- @_idv_phone_form ||= Idv::PhoneForm.new(idv_session.params, current_user)
+ def idv_form
+ @_idv_form ||= Idv::PhoneForm.new(idv_session.params, current_user)
end
end
end
diff --git a/app/controllers/verify/sessions_controller.rb b/app/controllers/verify/sessions_controller.rb
index 6d944c73fea..191507de2dc 100644
--- a/app/controllers/verify/sessions_controller.rb
+++ b/app/controllers/verify/sessions_controller.rb
@@ -7,6 +7,9 @@ class SessionsController < ApplicationController
before_action :confirm_idv_attempts_allowed
before_action :confirm_idv_needed
before_action :confirm_step_needed, except: [:destroy]
+ before_action :initialize_idv_session, only: [:create]
+ before_action :submit_idv_form, only: [:create]
+ before_action :submit_idv_job, only: [:create]
delegate :attempts_exceeded?, to: :step, prefix: true
@@ -18,7 +21,7 @@ def new
def create
result = step.submit
- analytics.track_event(Analytics::IDV_BASIC_INFO_SUBMITTED, result.to_h)
+ analytics.track_event(Analytics::IDV_BASIC_INFO_SUBMITTED_VENDOR, result.to_h)
if result.success?
process_success
@@ -35,6 +38,21 @@ def destroy
private
+ def submit_idv_form
+ result = idv_form.submit(profile_params)
+ analytics.track_event(Analytics::IDV_BASIC_INFO_SUBMITTED_FORM, result.to_h)
+
+ process_failure unless result.success?
+ end
+
+ def submit_idv_job
+ SubmitIdvJob.new(
+ vendor_validator_class: Idv::ProfileValidator,
+ idv_session: idv_session,
+ vendor_params: idv_session.vendor_params
+ ).call
+ end
+
def step_name
:sessions
end
@@ -45,9 +63,9 @@ def confirm_step_needed
def step
@_step ||= Idv::ProfileStep.new(
- idv_form: idv_profile_form,
+ idv_form_params: profile_params,
idv_session: idv_session,
- params: profile_params
+ vendor_validator_result: vendor_validator_result
)
end
@@ -68,7 +86,7 @@ def process_success
end
def process_failure
- if step.duplicate_ssn?
+ if idv_form.duplicate_ssn?
flash[:error] = t('idv.errors.duplicate_ssn')
redirect_to verify_session_dupe_path
else
@@ -81,7 +99,7 @@ def view_model(error: nil)
Verify::SessionsNew.new(
error: error,
remaining_attempts: remaining_idv_attempts,
- idv_form: idv_profile_form
+ idv_form: idv_form
)
end
@@ -89,8 +107,13 @@ def remaining_idv_attempts
Idv::Attempter.idv_max_attempts - current_user.idv_attempts
end
- def idv_profile_form
- @_idv_profile_form ||= Idv::ProfileForm.new((idv_session.params || {}), current_user)
+ def idv_form
+ @_idv_form ||= Idv::ProfileForm.new((idv_session.params || {}), current_user)
+ end
+
+ def initialize_idv_session
+ idv_session.params.merge!(profile_params)
+ idv_session.applicant = idv_session.vendor_params
end
def profile_params
diff --git a/app/controllers/verify/usps_controller.rb b/app/controllers/verify/usps_controller.rb
index 5a72c9b68b5..bc850e6c921 100644
--- a/app/controllers/verify/usps_controller.rb
+++ b/app/controllers/verify/usps_controller.rb
@@ -5,10 +5,7 @@ class UspsController < ApplicationController
before_action :confirm_mail_not_spammed
def index
- @applicant = idv_session.normalized_applicant_params
- decorated_usps = UspsDecorator.new(idv_session)
- @title = decorated_usps.title
- @button = decorated_usps.button
+ @decorated_usps = UspsDecorator.new(usps_mail_service)
end
def create
diff --git a/app/controllers/verify_controller.rb b/app/controllers/verify_controller.rb
index 1f62949184a..b4ac0e88992 100644
--- a/app/controllers/verify_controller.rb
+++ b/app/controllers/verify_controller.rb
@@ -1,6 +1,6 @@
class VerifyController < ApplicationController
include IdvSession
- include AccountRecoveryConcern
+ include AccountReactivationConcern
before_action :confirm_two_factor_authenticated
before_action :confirm_idv_needed, only: %i[cancel fail]
diff --git a/app/decorators/service_provider_session_decorator.rb b/app/decorators/service_provider_session_decorator.rb
index d2848e92495..d24b946301e 100644
--- a/app/decorators/service_provider_session_decorator.rb
+++ b/app/decorators/service_provider_session_decorator.rb
@@ -3,10 +3,11 @@ class ServiceProviderSessionDecorator
DEFAULT_LOGO = 'generic.svg'.freeze
- def initialize(sp:, view_context:, sp_session:)
+ def initialize(sp:, view_context:, sp_session:, service_provider_request:)
@sp = sp
@view_context = view_context
@sp_session = sp_session
+ @service_provider_request = service_provider_request
end
def sp_logo
@@ -71,10 +72,10 @@ def cancel_link_path
private
- attr_reader :sp, :view_context, :sp_session
+ attr_reader :sp, :view_context, :sp_session, :service_provider_request
def request_url
- sp_session[:request_url]
+ sp_session[:request_url] || service_provider_request.url
end
def openid_connect_redirector
diff --git a/app/decorators/usps_decorator.rb b/app/decorators/usps_decorator.rb
index 6777bc95cc5..31e3845a556 100644
--- a/app/decorators/usps_decorator.rb
+++ b/app/decorators/usps_decorator.rb
@@ -1,8 +1,8 @@
class UspsDecorator
- attr_reader :idv_session
+ attr_reader :usps_mail_service
- def initialize(idv_session)
- @idv_session = idv_session
+ def initialize(usps_mail_service)
+ @usps_mail_service = usps_mail_service
end
def title
@@ -16,6 +16,6 @@ def button
private
def letter_already_sent?
- @idv_session.address_verification_mechanism == 'usps'
+ @usps_mail_service.any_mail_sent?
end
end
diff --git a/app/forms/idv/finance_form.rb b/app/forms/idv/finance_form.rb
index 8746f69e46d..51f1a72af65 100644
--- a/app/forms/idv/finance_form.rb
+++ b/app/forms/idv/finance_form.rb
@@ -44,11 +44,14 @@ def initialize(idv_params)
def submit(params)
@params = params
finance_value = update_finance_values(params)
- return false unless valid?
- clear_idv_params_finance
- idv_params[finance_type] = finance_value
- true
+ success = valid?
+ if success
+ clear_idv_params_finance
+ idv_params[finance_type] = finance_value
+ end
+
+ FormResponse.new(success: success, errors: errors.messages)
end
def self.finance_other_type_choices
diff --git a/app/forms/idv/phone_form.rb b/app/forms/idv/phone_form.rb
index c6161e7947f..61266f96da9 100644
--- a/app/forms/idv/phone_form.rb
+++ b/app/forms/idv/phone_form.rb
@@ -20,11 +20,10 @@ def submit(params)
self.phone = formatted_phone
- return false unless valid?
+ success = valid?
+ update_idv_params(formatted_phone) if success
- update_idv_params(formatted_phone)
-
- true
+ FormResponse.new(success: success, errors: errors.messages)
end
private
diff --git a/app/forms/idv/profile_form.rb b/app/forms/idv/profile_form.rb
index 720b26f6b1b..7d2912f44b5 100644
--- a/app/forms/idv/profile_form.rb
+++ b/app/forms/idv/profile_form.rb
@@ -40,6 +40,13 @@ def pii_attributes
def submit(params)
initialize_params(params)
profile.ssn_signature = ssn_signature
+
+ FormResponse.new(success: valid?, errors: errors.messages)
+ end
+
+ def duplicate_ssn?
+ return true if any_matching_ssn_signatures?(ssn_signature)
+ return true if ssn_is_duplicate_with_old_key?
end
private
@@ -59,12 +66,7 @@ def ssn_signature(key = Pii::Fingerprinter.current_key)
end
def ssn_is_unique
- errors.add :ssn, I18n.t('idv.errors.duplicate_ssn') if ssn_is_duplicate?
- end
-
- def ssn_is_duplicate?
- return true if any_matching_ssn_signatures?(ssn_signature)
- return true if ssn_is_duplicate_with_old_key?
+ errors.add :ssn, I18n.t('idv.errors.duplicate_ssn') if duplicate_ssn?
end
def ssn_is_duplicate_with_old_key?
diff --git a/app/forms/update_user_phone_form.rb b/app/forms/update_user_phone_form.rb
index 538cf17b310..b6256895a60 100644
--- a/app/forms/update_user_phone_form.rb
+++ b/app/forms/update_user_phone_form.rb
@@ -17,7 +17,7 @@ def initialize(user)
def submit(params)
check_phone_change(params)
- valid?
+ FormResponse.new(success: valid?, errors: errors.messages)
end
def phone_changed?
diff --git a/app/helpers/locale_helper.rb b/app/helpers/locale_helper.rb
new file mode 100644
index 00000000000..ff18d64724e
--- /dev/null
+++ b/app/helpers/locale_helper.rb
@@ -0,0 +1,6 @@
+module LocaleHelper
+ def locale_url_param
+ active_locale = I18n.locale
+ active_locale == I18n.default_locale ? nil : active_locale
+ end
+end
diff --git a/app/jobs/sms_otp_sender_job.rb b/app/jobs/sms_otp_sender_job.rb
index af203aafec0..1bfd2a8e2f8 100644
--- a/app/jobs/sms_otp_sender_job.rb
+++ b/app/jobs/sms_otp_sender_job.rb
@@ -15,7 +15,7 @@ def otp_valid?(otp_created_at)
def send_otp(twilio_service, code, phone)
twilio_service.send_sms(
to: phone,
- body: "#{code} is your #{APP_NAME} one-time security code."
+ body: I18n.t('jobs.sms_otp_sender_job.message', code: code, app: APP_NAME)
)
end
end
diff --git a/app/jobs/vendor_validator_job.rb b/app/jobs/vendor_validator_job.rb
new file mode 100644
index 00000000000..b78784082f8
--- /dev/null
+++ b/app/jobs/vendor_validator_job.rb
@@ -0,0 +1,37 @@
+class VendorValidatorJob < ActiveJob::Base
+ queue_as :idv
+
+ def perform(result_id:, vendor_validator_class:, vendor:, vendor_params:, applicant_json:,
+ vendor_session_id:)
+ vendor_validator = vendor_validator_class.constantize.new(
+ applicant: Proofer::Applicant.new(JSON.parse(applicant_json, symbolize_names: true)),
+ vendor: vendor,
+ vendor_params: indifferent_access(vendor_params),
+ vendor_session_id: vendor_session_id
+ )
+
+ VendorValidatorResultStorage.new.store(
+ result_id: result_id,
+ result: extract_result(vendor_validator.result)
+ )
+ end
+
+ private
+
+ def extract_result(result)
+ vendor_resp = result.vendor_resp
+
+ Idv::VendorResult.new(
+ success: result.success?,
+ errors: result.errors,
+ reasons: vendor_resp.reasons,
+ normalized_applicant: vendor_resp.try(:normalized_applicant),
+ session_id: result.try(:session_id)
+ )
+ end
+
+ def indifferent_access(params)
+ return params if params.is_a?(String)
+ params.with_indifferent_access
+ end
+end
diff --git a/app/mailers/custom_devise_mailer.rb b/app/mailers/custom_devise_mailer.rb
index efed6f6b003..ca9cfb44757 100644
--- a/app/mailers/custom_devise_mailer.rb
+++ b/app/mailers/custom_devise_mailer.rb
@@ -1,14 +1,21 @@
class CustomDeviseMailer < Devise::Mailer
include Mailable
+ include LocaleHelper
before_action :attach_images
layout 'layouts/user_mailer'
default from: email_with_name(Figaro.env.email_from, Figaro.env.email_from)
+ def reset_password_instructions(*)
+ @locale = locale_url_param
+ super
+ end
+
def confirmation_instructions(record, token, options = {})
presenter = ConfirmationEmailPresenter.new(record, view_context)
@first_sentence = presenter.first_sentence
@confirmation_period = presenter.confirmation_period
@request_id = options[:request_id]
+ @locale = locale_url_param
super
end
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
index 6be2598e599..aff54835ba5 100644
--- a/app/mailers/user_mailer.rb
+++ b/app/mailers/user_mailer.rb
@@ -1,5 +1,6 @@
class UserMailer < ActionMailer::Base
include Mailable
+ include LocaleHelper
before_action :attach_images
default from: email_with_name(Figaro.env.email_from, Figaro.env.email_from)
@@ -8,8 +9,7 @@ def email_changed(old_email)
end
def signup_with_your_email(email)
- @root_url = root_url
- @new_user_password_url = new_user_password_url
+ @root_url = root_url(locale: locale_url_param)
mail(to: email, subject: t('mailer.email_reuse_notice.subject'))
end
diff --git a/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb b/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
index 0f27b0959bf..fbf6a2736e3 100644
--- a/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/authenticator_delivery_presenter.rb
@@ -22,7 +22,7 @@ def cancel_link
if reauthn
account_path
else
- destroy_user_session_path
+ sign_out_path
end
end
diff --git a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
index 85e805bdc34..411e52f9019 100644
--- a/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
+++ b/app/presenters/two_factor_auth_code/phone_delivery_presenter.rb
@@ -22,7 +22,7 @@ def cancel_link
if confirmation_for_phone_change || reauthn
account_path
else
- destroy_user_session_path
+ sign_out_path
end
end
diff --git a/app/services/analytics.rb b/app/services/analytics.rb
index 96811367afd..6d59735fcbe 100644
--- a/app/services/analytics.rb
+++ b/app/services/analytics.rb
@@ -38,14 +38,17 @@ def uuid
EMAIL_CONFIRMATION = 'Email Confirmation'.freeze
EMAIL_CONFIRMATION_RESEND = 'Email Confirmation requested due to invalid token'.freeze
IDV_BASIC_INFO_VISIT = 'IdV: basic info visited'.freeze
- IDV_BASIC_INFO_SUBMITTED = 'IdV: basic info submitted'.freeze
+ IDV_BASIC_INFO_SUBMITTED_FORM = 'IdV: basic info form submitted'.freeze
+ IDV_BASIC_INFO_SUBMITTED_VENDOR = 'IdV: basic info vendor submitted'.freeze
IDV_MAX_ATTEMPTS_EXCEEDED = 'IdV: max attempts exceeded'.freeze
IDV_FINAL = 'IdV: final resolution'.freeze
IDV_FINANCE_CCN_VISIT = 'IdV: finance ccn visited'.freeze
- IDV_FINANCE_CONFIRMATION = 'IdV: finance confirmation'.freeze
+ IDV_FINANCE_CONFIRMATION_FORM = 'IdV: finance confirmation form'.freeze
+ IDV_FINANCE_CONFIRMATION_VENDOR = 'IdV: finance confirmation vendor'.freeze
IDV_FINANCE_OTHER_VISIT = 'IdV: finance other visited'.freeze
IDV_INTRO_VISIT = 'IdV: intro visited'.freeze
- IDV_PHONE_CONFIRMATION = 'IdV: phone confirmation'.freeze
+ IDV_PHONE_CONFIRMATION_FORM = 'IdV: phone confirmation form'.freeze
+ IDV_PHONE_CONFIRMATION_VENDOR = 'IdV: phone confirmation vendor'.freeze
IDV_PHONE_RECORD_VISIT = 'IdV: phone of record visited'.freeze
IDV_REVIEW_COMPLETE = 'IdV: review complete'.freeze
IDV_REVIEW_VISIT = 'IdV: review info visited'.freeze
diff --git a/app/services/decorated_session.rb b/app/services/decorated_session.rb
index ebe7829e338..5a51d68fee7 100644
--- a/app/services/decorated_session.rb
+++ b/app/services/decorated_session.rb
@@ -1,14 +1,18 @@
class DecoratedSession
- def initialize(sp:, view_context:, sp_session:)
+ def initialize(sp:, view_context:, sp_session:, service_provider_request:)
@sp = sp
@view_context = view_context
@sp_session = sp_session
+ @service_provider_request = service_provider_request
end
def call
if sp.is_a? ServiceProvider
ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: sp_session
+ sp: sp,
+ view_context: view_context,
+ sp_session: sp_session,
+ service_provider_request: service_provider_request
)
else
SessionDecorator.new
@@ -17,5 +21,5 @@ def call
private
- attr_reader :sp, :view_context, :sp_session
+ attr_reader :sp, :view_context, :sp_session, :service_provider_request
end
diff --git a/app/services/idv/financials_step.rb b/app/services/idv/financials_step.rb
index cffa4264dd0..0979372b0b4 100644
--- a/app/services/idv/financials_step.rb
+++ b/app/services/idv/financials_step.rb
@@ -4,7 +4,7 @@ def submit
if complete?
@success = true
idv_session.financials_confirmation = true
- idv_session.params = idv_form.idv_params
+ idv_session.params = idv_form_params
else
@success = false
idv_session.financials_confirmation = false
@@ -13,29 +13,12 @@ def submit
FormResponse.new(success: success, errors: errors)
end
- def form_valid_but_vendor_validation_failed?
- form_valid? && !vendor_validation_passed?
- end
-
private
attr_reader :success
def complete?
- form_valid? && vendor_validation_passed?
- end
-
- def vendor_validator_class
- Idv::FinancialsValidator
- end
-
- def vendor_reasons
- vendor_validator.reasons if form_valid?
- end
-
- def vendor_params
- finance_type = idv_form.finance_type
- { finance_type => idv_form.idv_params[finance_type] }
+ vendor_validation_passed?
end
end
end
diff --git a/app/services/idv/financials_validator.rb b/app/services/idv/financials_validator.rb
index d4c4746b0c2..187dcabd348 100644
--- a/app/services/idv/financials_validator.rb
+++ b/app/services/idv/financials_validator.rb
@@ -2,13 +2,9 @@ module Idv
class FinancialsValidator < VendorValidator
private
- def session_id
- idv_session.vendor_session_id
- end
-
def try_submit
try_agent_action do
- idv_agent.submit_financials(vendor_params, session_id)
+ idv_agent.submit_financials(vendor_params, vendor_session_id)
end
end
end
diff --git a/app/services/idv/phone_step.rb b/app/services/idv/phone_step.rb
index 252b6f86f5d..49be5cfdd04 100644
--- a/app/services/idv/phone_step.rb
+++ b/app/services/idv/phone_step.rb
@@ -10,32 +10,16 @@ def submit
FormResponse.new(success: complete?, errors: errors)
end
- def form_valid_but_vendor_validation_failed?
- form_valid? && !vendor_validation_passed?
- end
-
private
def complete?
- form_valid? && vendor_validation_passed?
- end
-
- def vendor_validator_class
- Idv::PhoneValidator
- end
-
- def vendor_params
- idv_form.phone
- end
-
- def vendor_reasons
- vendor_validator.reasons if form_valid?
+ vendor_validation_passed?
end
def update_idv_session
idv_session.phone_confirmation = true
idv_session.address_verification_mechanism = :phone
- idv_session.params = idv_form.idv_params
+ idv_session.params = idv_form_params
end
end
end
diff --git a/app/services/idv/phone_validator.rb b/app/services/idv/phone_validator.rb
index e6bb1d3188a..7e4b26cc009 100644
--- a/app/services/idv/phone_validator.rb
+++ b/app/services/idv/phone_validator.rb
@@ -2,13 +2,9 @@ module Idv
class PhoneValidator < VendorValidator
private
- def session_id
- idv_session.vendor_session_id
- end
-
def try_submit
try_agent_action do
- idv_agent.submit_phone(vendor_params, session_id)
+ idv_agent.submit_phone(vendor_params, vendor_session_id)
end
end
end
diff --git a/app/services/idv/profile_step.rb b/app/services/idv/profile_step.rb
index 3e92d2314e6..371e6d2a71a 100644
--- a/app/services/idv/profile_step.rb
+++ b/app/services/idv/profile_step.rb
@@ -1,12 +1,9 @@
module Idv
class ProfileStep < Step
def submit
- initialize_idv_session
- submit_idv_form
-
@success = complete?
- increment_attempts_count if form_valid?
+ increment_attempts_count
update_idv_session if success
FormResponse.new(success: success, errors: errors, extra: extra_analytics_attributes)
@@ -16,55 +13,26 @@ def attempts_exceeded?
attempter.exceeded?
end
- def duplicate_ssn?
- errors.key?(:ssn) && errors[:ssn].include?(I18n.t('idv.errors.duplicate_ssn'))
- end
-
- def form_valid_but_vendor_validation_failed?
- form_valid? && !vendor_validation_passed?
- end
-
private
attr_reader :success
- def initialize_idv_session
- idv_session.params.merge!(params)
- idv_session.applicant = vendor_params
- end
-
- def vendor_params
- idv_session.vendor_params
- end
-
- def submit_idv_form
- idv_form.submit(params)
- end
-
def complete?
- !attempts_exceeded? && form_valid? && vendor_validation_passed?
+ !attempts_exceeded? && vendor_validation_passed?
end
def attempter
- @_idv_attempter ||= Idv::Attempter.new(idv_form.user)
+ @_idv_attempter ||= Idv::Attempter.new(idv_session.current_user)
end
def increment_attempts_count
attempter.increment
end
- def form_valid?
- @_form_valid ||= idv_form.valid?
- end
-
- def vendor_validator_class
- Idv::ProfileValidator
- end
-
def update_idv_session
idv_session.profile_confirmation = true
- idv_session.vendor_session_id = vendor_validator.session_id
- idv_session.normalized_applicant_params = vendor_validator.normalized_applicant.to_hash
+ idv_session.vendor_session_id = vendor_validator_result.session_id
+ idv_session.normalized_applicant_params = vendor_validator_result.normalized_applicant.to_hash
idv_session.resolution_successful = true
end
@@ -76,7 +44,7 @@ def extra_analytics_attributes
end
def vendor_reasons
- vendor_validator.reasons if form_valid?
+ vendor_validator_result.reasons
end
end
end
diff --git a/app/services/idv/profile_validator.rb b/app/services/idv/profile_validator.rb
index eb1cd5ce051..f3c72a064e3 100644
--- a/app/services/idv/profile_validator.rb
+++ b/app/services/idv/profile_validator.rb
@@ -1,15 +1,9 @@
module Idv
class ProfileValidator < VendorValidator
- delegate :session_id, to: :result
-
def result
@_result ||= try_start
end
- def normalized_applicant
- result.vendor_resp.normalized_applicant
- end
-
private
def try_start
diff --git a/app/services/idv/session.rb b/app/services/idv/session.rb
index 188ef878467..2467d7a7d03 100644
--- a/app/services/idv/session.rb
+++ b/app/services/idv/session.rb
@@ -1,6 +1,7 @@
module Idv
class Session
VALID_SESSION_ATTRIBUTES = %i[
+ async_result_id
address_verification_mechanism
applicant
financials_confirmation
@@ -17,6 +18,8 @@ class Session
vendor_session_id
].freeze
+ attr_reader :current_user
+
def initialize(user_session:, current_user:, issuer:)
@user_session = user_session
@current_user = current_user
@@ -96,7 +99,7 @@ def address_mechanism_chosen?
private
- attr_accessor :user_session, :current_user, :issuer
+ attr_accessor :user_session, :issuer
def new_idv_session
{ params: {}, step_attempts: { financials: 0, phone: 0 } }
diff --git a/app/services/idv/step.rb b/app/services/idv/step.rb
index 72e93cd0924..355fde1cf3c 100644
--- a/app/services/idv/step.rb
+++ b/app/services/idv/step.rb
@@ -1,51 +1,27 @@
# abstract base class for Idv Steps
module Idv
class Step
- def initialize(idv_form:, idv_session:, params:)
- @idv_form = idv_form
+ def initialize(idv_session:, idv_form_params:, vendor_validator_result:)
+ @idv_form_params = idv_form_params
@idv_session = idv_session
- @params = params
+ @vendor_validator_result = vendor_validator_result
end
- def form_valid?
- form_validate(params)
+ def vendor_validation_passed?
+ vendor_validator_result.success?
end
private
attr_accessor :idv_session
- attr_reader :idv_form, :params
-
- def form_validate(params)
- @form_result ||= idv_form.submit(params)
- end
-
- def vendor_validation_passed?
- vendor_validator.success?
- end
+ attr_reader :idv_form_params, :vendor_validator_result
def errors
- errors = idv_form.errors.messages.dup
- return errors unless form_valid? && vendor_errors
- merge_vendor_errors(errors)
- end
-
- def merge_vendor_errors(errors)
- vendor_errors.each_with_object(errors) do |(key, value), errs|
- value = [value] unless value.is_a?(Array)
- errs[key] = value
+ @_errors ||= begin
+ vendor_validator_result.errors.each_with_object({}) do |(key, value), errs|
+ errs[key] = Array(value)
+ end
end
end
-
- def vendor_errors
- @_vendor_errors ||= vendor_validator.errors
- end
-
- def vendor_validator
- @_vendor_validator ||= vendor_validator_class.new(
- idv_session: idv_session,
- vendor_params: vendor_params
- )
- end
end
end
diff --git a/app/services/idv/usps_mail.rb b/app/services/idv/usps_mail.rb
index dadb08f4b73..26b9193049f 100644
--- a/app/services/idv/usps_mail.rb
+++ b/app/services/idv/usps_mail.rb
@@ -12,6 +12,10 @@ def mail_spammed?
max_events? && updated_within_last_month?
end
+ def any_mail_sent?
+ user_mail_events.any?
+ end
+
private
attr_reader :current_user
diff --git a/app/services/idv/vendor_result.rb b/app/services/idv/vendor_result.rb
new file mode 100644
index 00000000000..7f01ee62b90
--- /dev/null
+++ b/app/services/idv/vendor_result.rb
@@ -0,0 +1,27 @@
+module Idv
+ class VendorResult
+ attr_reader :success, :errors, :reasons, :session_id, :normalized_applicant
+
+ def self.new_from_json(json)
+ parsed = JSON.parse(json, symbolize_names: true)
+
+ applicant = parsed[:normalized_applicant]
+ parsed[:normalized_applicant] = Proofer::Applicant.new(applicant) if applicant
+
+ new(**parsed)
+ end
+
+ def initialize(success: nil, errors: nil, reasons: nil, session_id: nil,
+ normalized_applicant: nil)
+ @success = success
+ @errors = errors
+ @reasons = reasons
+ @session_id = session_id
+ @normalized_applicant = normalized_applicant
+ end
+
+ def success?
+ success
+ end
+ end
+end
diff --git a/app/services/idv/vendor_validator.rb b/app/services/idv/vendor_validator.rb
index bb0ca462b9e..8b0c6222101 100644
--- a/app/services/idv/vendor_validator.rb
+++ b/app/services/idv/vendor_validator.rb
@@ -1,35 +1,28 @@
# abstract base class for proofing vendor validation
module Idv
class VendorValidator
- delegate :success?, :errors, to: :result
- attr_reader :idv_session, :vendor_params
+ attr_reader :applicant, :vendor, :vendor_params, :vendor_session_id
- def initialize(idv_session:, vendor_params:)
- @idv_session = idv_session
+ def initialize(applicant:, vendor:, vendor_params:, vendor_session_id:)
+ @applicant = applicant
+ @vendor = vendor
@vendor_params = vendor_params
+ @vendor_session_id = vendor_session_id
end
- def reasons
- result.vendor_resp.reasons
+ def result
+ @_result ||= try_submit
end
private
- def idv_vendor
- @_idv_vendor ||= Idv::Vendor.new
- end
-
def idv_agent
@_agent ||= Idv::Agent.new(
- applicant: idv_session.applicant,
- vendor: (idv_session.vendor || idv_vendor.pick)
+ applicant: applicant,
+ vendor: vendor
)
end
- def result
- @_result ||= try_submit
- end
-
def try_agent_action
yield
rescue => err
diff --git a/app/services/locale_chooser.rb b/app/services/locale_chooser.rb
new file mode 100644
index 00000000000..8b8676e6a29
--- /dev/null
+++ b/app/services/locale_chooser.rb
@@ -0,0 +1,21 @@
+class LocaleChooser
+ include HttpAcceptLanguage::EasyAccess
+
+ def initialize(locale_param, request)
+ @locale_param = locale_param
+ @request = request
+ end
+
+ def locale
+ return locale_param if locale_valid?
+ http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
+ end
+
+ private
+
+ attr_reader :locale_param, :request
+
+ def locale_valid?
+ LocaleValidator.new(locale_param).success?
+ end
+end
diff --git a/app/services/locale_validator.rb b/app/services/locale_validator.rb
new file mode 100644
index 00000000000..b1e6f1bb59b
--- /dev/null
+++ b/app/services/locale_validator.rb
@@ -0,0 +1,13 @@
+class LocaleValidator
+ def initialize(locale)
+ @locale = locale
+ end
+
+ def success?
+ locale.present? && I18n.available_locales.include?(locale.to_sym)
+ end
+
+ private
+
+ attr_reader :locale
+end
diff --git a/app/services/marketing_site.rb b/app/services/marketing_site.rb
index ccbfa81b344..776fdb339cd 100644
--- a/app/services/marketing_site.rb
+++ b/app/services/marketing_site.rb
@@ -1,23 +1,28 @@
class MarketingSite
BASE_URL = URI('https://www.login.gov').freeze
+ def self.locale_segment
+ active_locale = I18n.locale
+ active_locale == I18n.default_locale ? '/' : "/#{active_locale}/"
+ end
+
def self.base_url
- BASE_URL.to_s
+ URI.join(BASE_URL, locale_segment).to_s
end
def self.privacy_url
- URI.join(BASE_URL, '/policy').to_s
+ URI.join(BASE_URL, locale_segment, 'policy').to_s
end
def self.contact_url
- URI.join(BASE_URL, '/contact').to_s
+ URI.join(BASE_URL, locale_segment, 'contact').to_s
end
def self.help_url
- URI.join(BASE_URL, '/help').to_s
+ URI.join(BASE_URL, locale_segment, 'help').to_s
end
def self.help_authenticator_app_url
- URI.join(BASE_URL, '/help/signing-in/what-is-an-authenticator-app/').to_s
+ URI.join(BASE_URL, locale_segment, 'help/signing-in/what-is-an-authenticator-app/').to_s
end
end
diff --git a/app/services/otp_rate_limiter.rb b/app/services/otp_rate_limiter.rb
index 1fb7bc29dcf..b0287467a20 100644
--- a/app/services/otp_rate_limiter.rb
+++ b/app/services/otp_rate_limiter.rb
@@ -32,10 +32,8 @@ def lock_out_user
end
def increment
- now = Time.zone.now
-
entry_for_current_phone.otp_send_count += 1
- entry_for_current_phone.otp_last_sent_at = now
+ entry_for_current_phone.otp_last_sent_at = Time.zone.now
entry_for_current_phone.save!
end
diff --git a/app/services/submit_idv_job.rb b/app/services/submit_idv_job.rb
new file mode 100644
index 00000000000..a74dfba5df0
--- /dev/null
+++ b/app/services/submit_idv_job.rb
@@ -0,0 +1,32 @@
+class SubmitIdvJob
+ def initialize(vendor_validator_class:, idv_session:, vendor_params:)
+ @vendor_validator_class = vendor_validator_class
+ @idv_session = idv_session
+ @vendor_params = vendor_params
+ end
+
+ def call
+ idv_session.async_result_id = result_id
+
+ VendorValidatorJob.perform_now(
+ result_id: result_id,
+ vendor_validator_class: vendor_validator_class.to_s,
+ vendor: vendor,
+ vendor_params: vendor_params,
+ vendor_session_id: idv_session.vendor_session_id,
+ applicant_json: idv_session.applicant.to_json
+ )
+ end
+
+ private
+
+ attr_reader :vendor_validator_class, :idv_session, :vendor_params
+
+ def result_id
+ @_result_id ||= SecureRandom.uuid
+ end
+
+ def vendor
+ idv_session.vendor || Idv::Vendor.new.pick
+ end
+end
diff --git a/app/services/usps_confirmation_maker.rb b/app/services/usps_confirmation_maker.rb
index 1fb164a1597..e9a3ddf8af5 100644
--- a/app/services/usps_confirmation_maker.rb
+++ b/app/services/usps_confirmation_maker.rb
@@ -17,14 +17,14 @@ def perform
# This method is single statement spread across many lines for readability
def attributes
{
- address1: pii[:address1],
- address2: pii[:address2],
- city: pii[:city],
- otp: pii[:otp],
- first_name: pii[:first_name],
- last_name: pii[:last_name],
- state: pii[:state],
- zipcode: pii[:zipcode],
+ address1: pii[:address1].norm,
+ address2: pii[:address2].norm,
+ city: pii[:city].norm,
+ otp: pii[:otp].norm,
+ first_name: pii[:first_name].norm,
+ last_name: pii[:last_name].norm,
+ state: pii[:state].norm,
+ zipcode: pii[:zipcode].norm,
issuer: issuer,
}
end
diff --git a/app/services/vendor_validator_result_storage.rb b/app/services/vendor_validator_result_storage.rb
new file mode 100644
index 00000000000..098ba2422ff
--- /dev/null
+++ b/app/services/vendor_validator_result_storage.rb
@@ -0,0 +1,24 @@
+class VendorValidatorResultStorage
+ TTL = Figaro.env.session_timeout_in_minutes.to_i.minutes.seconds.to_i
+
+ def store(result_id:, result:)
+ Sidekiq.redis do |redis|
+ redis.setex(redis_key(result_id), TTL, result.to_json)
+ end
+ end
+
+ def load(result_id)
+ result_json = Sidekiq.redis do |redis|
+ redis.get(redis_key(result_id))
+ end
+
+ return unless result_json
+
+ Idv::VendorResult.new_from_json(result_json)
+ end
+
+ # @api private
+ def redis_key(result_id)
+ "vendor-validator-result-#{result_id}"
+ end
+end
diff --git a/app/validators/form_password_validator.rb b/app/validators/form_password_validator.rb
index 23c6b9a9a4f..6997842b5a5 100644
--- a/app/validators/form_password_validator.rb
+++ b/app/validators/form_password_validator.rb
@@ -40,11 +40,15 @@ def zxcvbn_feedback
feedback = @pass_score.feedback.values.flatten.reject(&:empty?)
feedback.map do |error|
- I18n.t("zxcvbn.feedback.#{error.tr('.', '_')}")
+ I18n.t("zxcvbn.feedback.#{i18n_key(error)}")
end.join('. ').gsub(/\.\s*\./, '.')
end
def password_strength_enabled?
@enabled ||= FeatureManagement.password_strength_enabled?
end
+
+ def i18n_key(key)
+ key.tr(' -', '_').gsub(/\W/, '').downcase
+ end
end
diff --git a/app/views/devise/mailer/confirmation_instructions.html.slim b/app/views/devise/mailer/confirmation_instructions.html.slim
index f35fa928af9..e95edad6caf 100644
--- a/app/views/devise/mailer/confirmation_instructions.html.slim
+++ b/app/views/devise/mailer/confirmation_instructions.html.slim
@@ -12,15 +12,15 @@ table.button.expanded.large.radius
center
= link_to t('mailer.confirmation_instructions.link_text'), \
sign_up_create_email_confirmation_url(_request_id: \
- @request_id, confirmation_token: @token), \
+ @request_id, confirmation_token: @token, locale: @locale), \
target: '_blank', \
class: 'float-center', align: 'center'
td.expander
p = link_to sign_up_create_email_confirmation_url(_request_id: @request_id, \
- confirmation_token: @token), \
+ confirmation_token: @token, locale: @locale), \
sign_up_create_email_confirmation_url(_request_id: @request_id, \
- confirmation_token: @token), target: '_blank'
+ confirmation_token: @token, locale: @locale), target: '_blank'
table.spacer
tbody
diff --git a/app/views/devise/mailer/reset_password_instructions.html.slim b/app/views/devise/mailer/reset_password_instructions.html.slim
index a20f9ed0ede..71674e9e400 100644
--- a/app/views/devise/mailer/reset_password_instructions.html.slim
+++ b/app/views/devise/mailer/reset_password_instructions.html.slim
@@ -11,13 +11,13 @@ table.button.expanded.large.radius
td
center
= link_to t('mailer.reset_password.link_text'),
- edit_password_url(@resource, reset_password_token: @token),
+ edit_password_url(@resource, reset_password_token: @token, locale: @locale),
target: '_blank', class: 'float-center', align: 'center'
td.expander
p
- = link_to edit_password_url(@resource, reset_password_token: @token), \
- edit_password_url(@resource, reset_password_token: @token), \
+ = link_to edit_password_url(@resource, reset_password_token: @token, locale: @locale), \
+ edit_password_url(@resource, reset_password_token: @token, locale: @locale), \
target: '_blank'
table.spacer
diff --git a/app/views/devise/passwords/new.html.slim b/app/views/devise/passwords/new.html.slim
index 345c3eab860..52b74d9aaee 100644
--- a/app/views/devise/passwords/new.html.slim
+++ b/app/views/devise/passwords/new.html.slim
@@ -10,4 +10,6 @@ p.mt-tiny.mb0#email-description
= f.input :email, required: true, input_html: { 'aria-describedby': 'email-description' }
= f.button :submit, t('forms.buttons.continue'), class: 'mt2'
-= render 'shared/cancel', link: new_user_session_path
+.mt2.pt1.border-top
+ = link_to t('links.cancel'), decorated_session.cancel_link_path, class: 'h5'
+
diff --git a/app/views/shared/_accordion.html.slim b/app/views/shared/_accordion.html.slim
index b36a57c2bab..e42dfed41b3 100644
--- a/app/views/shared/_accordion.html.slim
+++ b/app/views/shared/_accordion.html.slim
@@ -18,6 +18,7 @@
.accordion-content.clearfix.pt1(
id="#{target_id}"
role="region"
+ aria-hidden="true"
)
.px2
= yield
diff --git a/app/views/two_factor_authentication/personal_key_verification/show.html.slim b/app/views/two_factor_authentication/personal_key_verification/show.html.slim
index c3f6d778ce5..00dacd73161 100644
--- a/app/views/two_factor_authentication/personal_key_verification/show.html.slim
+++ b/app/views/two_factor_authentication/personal_key_verification/show.html.slim
@@ -8,4 +8,4 @@ p.mt-tiny.mb0 = t('devise.two_factor_authentication.personal_key_prompt')
= render 'partials/personal_key/entry_fields', f: f, attribute_name: :personal_key
= f.button :submit, t('forms.buttons.submit.default'), class: 'btn btn-primary'
-= render 'shared/cancel', link: destroy_user_session_path
+= render 'shared/cancel', link: sign_out_path
diff --git a/app/views/verify/usps/index.html.slim b/app/views/verify/usps/index.html.slim
index 75c25ae8732..302048a8ad2 100644
--- a/app/views/verify/usps/index.html.slim
+++ b/app/views/verify/usps/index.html.slim
@@ -2,7 +2,7 @@
= image_tag(asset_url('check-email.svg'), size: '48x48', alt: 'check email',\
class: 'absolute top-n24 left-0 right-0 mx-auto')
h1.h2
- = @title
+ = @decorated_usps.title
p
= t('idv.messages.usps.byline')
@@ -10,7 +10,7 @@
strong
= t('idv.messages.usps.success')
-= button_to @button, verify_usps_path, method: 'put',
+= button_to @decorated_usps.button, verify_usps_path, method: 'put',
class: 'btn btn-primary btn-wide', form_class: 'inline-block mr2'
= link_to t('idv.messages.usps.bad_address'), verify_phone_path
diff --git a/certs/sp/cbp_goes_prod.crt b/certs/sp/cbp_goes_prod.crt
new file mode 100644
index 00000000000..5bf2246006b
--- /dev/null
+++ b/certs/sp/cbp_goes_prod.crt
@@ -0,0 +1,45 @@
+-----BEGIN CERTIFICATE-----
+MIIH8DCCBtigAwIBAgIEWRoIeDANBgkqhkiG9w0BAQsFADCBhzELMAkGA1UEBhMC
+VVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEoMCYGA1UECxMfRGVwYXJ0bWVu
+dCBvZiBIb21lbGFuZCBTZWN1cml0eTEiMCAGA1UECxMZQ2VydGlmaWNhdGlvbiBB
+dXRob3JpdGllczEQMA4GA1UECxMHREhTIENBNDAeFw0xNzA2MjQxNDQ2MTlaFw0y
+MDA2MjQxNTE2MTlaMIGPMQswCQYDVQQGEwJVUzEYMBYGA1UEChMPVS5TLiBHb3Zl
+cm5tZW50MSgwJgYDVQQLEx9EZXBhcnRtZW50IG9mIEhvbWVsYW5kIFNlY3VyaXR5
+MQwwCgYDVQQLEwNDQlAxEDAOBgNVBAsTB0RldmljZXMxHDAaBgNVBAMTE3R0cC1h
+cHAuY2JwLmRocy5nb3YwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDP
+ubSOea85FVVr9Adn+mez7LjuPBs3aeMCEK9gM9xnV2tGFJra1Mh9ef/jnnO2ANur
+jtRovws/ea/k+J54ngIv7ZXCZGUvZZoOtyFqX7Mjnj8gFvIrFCVAD4a/FySSiGNo
+Z7+X/ypX1rFb8CNFSi/SxU+zH61ZS0i+OAZ2xAk3Gv+OkZ4DHRHXsGJn8rCJ2O1N
+c/OyKpBOpkS5EjTAPw3OqD/U8CU9Hl6QbK52ovxFLgtkHGWv37dLc0Qojwa8Lqaa
+FAxjWPyND2oo4aDfGtu7YtbGk0zRf97QNvfoqjkGYAaUK0ozCTMKZEVuXknhDtvy
+a6C26kDVYA8RUn9RtFjNAgMBAAGjggRYMIIEVDAOBgNVHQ8BAf8EBAMCBaAwFwYD
+VR0gBBAwDjAMBgpghkgBZQMCAQMIMIIBGwYIKwYBBQUHAQEEggENMIIBCTA0Bggr
+BgEFBQcwAoYoaHR0cDovL3BraS5kaW1jLmRocy5nb3YvZGhzY2FfZWVfYWlhLnA3
+YzCBqgYIKwYBBQUHMAKGgZ1sZGFwOi8vbGRhcDAxLmRpbWMuZGhzLmdvdi9vdT1E
+SFMlMjBDQTQsb3U9Q2VydGlmaWNhdGlvbiUyMEF1dGhvcml0aWVzLG91PURlcGFy
+dG1lbnQlMjBvZiUyMEhvbWVsYW5kJTIwU2VjdXJpdHksbz1VLlMuJTIwR292ZXJu
+bWVudCxjPVVTP2NBQ2VydGlmaWNhdGU7YmluYXJ5MCQGCCsGAQUFBzABhhhodHRw
+Oi8vb2NzcC5kaW1jLmRocy5nb3YwEwYDVR0lBAwwCgYIKwYBBQUHAwEwggEBBgNV
+HREEgfkwgfaCE3R0cC1hcHAuY2JwLmRocy5nb3aCGHR0cC1pbnRlcm5hbC5jYnAu
+ZGhzLmdvdoIcdHRwLWludGVybmFsLWFwcC5jYnAuZGhzLmdvdoIZdHRwLXNjaGVk
+dWxlci5jYnAuZGhzLmdvdoIddHRwLXNjaGVkdWxlci1hcHAuY2JwLmRocy5nb3aC
+F3R0cC1jcmVkc3ZjLmNicC5kaHMuZ292ght0dHAtY3JlZHN2Yy1hcHAuY2JwLmRo
+cy5nb3aCF3R0cC1hZHMtYXBwLmNicC5kaHMuZ292gh50dHAtc2FwYWRhcHRlci1h
+cHAuY2JwLmRocy5nb3YwggGTBgNVHR8EggGKMIIBhjCCAVegggFToIIBT6SBnDCB
+mTELMAkGA1UEBhMCVVMxGDAWBgNVBAoTD1UuUy4gR292ZXJubWVudDEoMCYGA1UE
+CxMfRGVwYXJ0bWVudCBvZiBIb21lbGFuZCBTZWN1cml0eTEiMCAGA1UECxMZQ2Vy
+dGlmaWNhdGlvbiBBdXRob3JpdGllczEQMA4GA1UECxMHREhTIENBNDEQMA4GA1UE
+AxMHQ1JMNTM5MYaBrWxkYXA6Ly9sZGFwMDEuZGltYy5kaHMuZ292L2NuPUNSTDUz
+OTEsb3U9REhTJTIwQ0E0LG91PUNlcnRpZmljYXRpb24lMjBBdXRob3JpdGllcyxv
+dT1EZXBhcnRtZW50JTIwb2YlMjBIb21lbGFuZCUyMFNlY3VyaXR5LG89VS5TLiUy
+MEdvdmVybm1lbnQsYz1VUz9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0MCmgJ6Al
+hiNodHRwOi8vcGtpLmRpbWMuZGhzLmdvdi9ESFNfQ0EyLmNybDAfBgNVHSMEGDAW
+gBR8w0pcuh82q4NRffTg5Q6QfxwTQTAdBgNVHQ4EFgQUIWVxsBQWw5oIGgPYwrvG
+o7c35EowGQYJKoZIhvZ9B0EABAwwChsEVjguMQMCA6gwDQYJKoZIhvcNAQELBQAD
+ggEBAG3uUeSo89FY4HcDGGhO9mBjXuk7HRHGLnY8fAdqJoT1gA/oTHoV/fgw8QZu
+D3X9FLgpEjQz43iHFrv/ps2tYwgSKiiovoeYyb68s5X9NuG5kNnn0dGp1TjnNlGX
+9lQSBr4CReaJpCtSAPbOtuZ+8b7MA7ODnMg/1u1qNKovIqMV6eIq5vrD/+MeSG61
++c+MI6Gita5j6J6lzZqqwtAltfMFetc1Qkl2tjqdpce9u60Ch7yuVR3Xh1SEhaAa
+jRJsbOuqNAfC038A6ASv3nxGao8AYUHt2aR9chXApKfBzfx6VYW0462UPqzeRi5l
+RCa8sud1bb1pFRJv8LWiqOC/9yw=
+-----END CERTIFICATE-----
diff --git a/config/application.rb b/config/application.rb
index b12f84bf8a2..91fbd99d570 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -16,8 +16,9 @@ class Application < Rails::Application
config.browserify_rails.force = true
config.browserify_rails.commandline_options = '-t [ babelify --presets [ es2015 ] ]'
- config.i18n.available_locales = Figaro.env.available_locales.split(' ')
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{yml}')]
+ config.i18n.available_locales = Figaro.env.available_locales.split(' ')
+ config.i18n.default_locale = :en
routes.default_url_options[:host] = Figaro.env.domain_name
diff --git a/config/application.yml.example b/config/application.yml.example
index 3d9d481d90c..6a8e465e094 100644
--- a/config/application.yml.example
+++ b/config/application.yml.example
@@ -53,7 +53,7 @@ development:
attribute_cost: '4000$8$4$' # SCrypt::Engine.calibrate(max_time: 0.5)
attribute_encryption_key: '2086dfbd15f5b0c584f3664422a1d3409a0d2aa6084f65b6ba57d64d4257431c124158670c7655e45cabe64194f7f7b6c7970153c285bdb8287ec0c4f7553e25'
attribute_encryption_key_queue: '["old-key-one", "old-key-two"]'
- available_locales: 'en es'
+ available_locales: 'en es fr'
aws_kms_key_id: 'alias/login-dot-gov-development-keymaker'
aws_region: 'us-east-1'
dashboard_api_token: 'test_token'
@@ -164,7 +164,7 @@ test:
attribute_cost: '800$8$1$' # SCrypt::Engine.calibrate(max_time: 0.01)
attribute_encryption_key: '2086dfbd15f5b0c584f3664422a1d3409a0d2aa6084f65b6ba57d64d4257431c124158670c7655e45cabe64194f7f7b6c7970153c285bdb8287ec0c4f7553e25'
attribute_encryption_key_queue: '["old-key-one", "old-key-two"]'
- available_locales: 'en es'
+ available_locales: 'en es fr'
aws_kms_key_id: 'alias/login-dot-gov-test-keymaker'
aws_region: 'us-east-1'
domain_name: 'www.example.com'
diff --git a/config/locales/account/fr.yml b/config/locales/account/fr.yml
new file mode 100644
index 00000000000..643b1ae1eba
--- /dev/null
+++ b/config/locales/account/fr.yml
@@ -0,0 +1,32 @@
+---
+fr:
+ account:
+ index:
+ address: Adresse actuelle
+ auth_app_disabled: non activée
+ auth_app_enabled: activée
+ authentication_app: Application d'authentification
+ dob: Date de naissance
+ email: Adresse courriel
+ full_name: Nom complet
+ login: Information de connexion
+ password: Mot de passe
+ phone: Numéro de téléphone
+ previous_address: Adresse précédente
+ reactivation:
+ instructions: Votre profil a été récemment désactivé en raison d'une réinitialisation de mot passe. Vous pouvez utiliser votre clé personnelle pour réactiver votre profil.
+ link: NOT TRANSLATED YET
+ ssn: Numéro d'assurance sociale
+ verification:
+ instructions: Votre compte requiert la vérification d'un code secret.
+ reactivate_button: Entrez le code que vous avez reçu par US mail
+ success: Votre compte a été vérifié.
+ with_phone_button: Verifiez avec votre téléphone
+ items:
+ personal_key: Clé personnelle
+ links:
+ regenerate_personal_key: Obtenez une nouvelle clé
+ security:
+ link: En apprendre davantage dans le Centre d'aide
+ text: L'information de votre profil est verrouillée pour votre sécurité.
+ welcome: Bienvenue
diff --git a/config/locales/activerecord/fr.yml b/config/locales/activerecord/fr.yml
new file mode 100644
index 00000000000..86bdb29d16f
--- /dev/null
+++ b/config/locales/activerecord/fr.yml
@@ -0,0 +1,11 @@
+---
+fr:
+ activerecord:
+ errors:
+ models:
+ app_setting:
+ attributes:
+ value:
+ cannot_disable_2fa_in_prod: L'authentification à deux facteurs ne peut
+ être désactivée en production
+ invalid: La valeur doit être de '1' ou de '0'
diff --git a/config/locales/devise/fr.yml b/config/locales/devise/fr.yml
new file mode 100644
index 00000000000..d0c4eda7908
--- /dev/null
+++ b/config/locales/devise/fr.yml
@@ -0,0 +1,153 @@
+---
+fr:
+ devise:
+ confirmations:
+ already_confirmed: Votre adresse courriel a déjà été confirmée. %{action}
+ confirmed: Vous avez confirmé votre adresse courriel
+ confirmed_but_must_set_password: Vous avez confirmé votre adresse courriel
+ send_instructions: 'Vous recevrez dans quelques instants un courriel avec des instructions pour confirmer votre adresse courriel.
+
+'
+ send_paranoid_instructions: 'Vous recevrez dans quelques instants un courriel avec des instructions pour la confirmation de votre adresse courriel.
+
+'
+ failure:
+ already_authenticated: ''
+ inactive: Votre compte n'est pas encore activé.
+ invalid: Adresse courriel ou mot de passe non valide.
+ last_attempt: Il vous reste un essai avant que votre compte ne soit verrouillé.
+ locked: Votre compte est maintenant verrouillé.
+ not_found_in_database: Adresse courriel ou mot de passe non valide.
+ session_limited: 'Vos authentifiants ont été utilisés dans un autre navigateur. Veuillez vous connecter de nouveau pour continuer avec ce navigateur.
+
+'
+ timeout: Votre session est expirée. Veuillez vous connecter de nouveau pour continuer.
+ unauthenticated: ''
+ unconfirmed: Vous devez confirmer votre adresse courriel avant de continuer.
+ mailer:
+ account_locked:
+ subject: Votre compte login.gov a été verrouillé
+ confirmation_instructions:
+ subject: Confirmez votre adresse courriel
+ password_updated:
+ subject: Votre mot de passe a été modifié
+ reset_password_instructions:
+ subject: Réinitialisez votre mot de passe
+ passwords:
+ choose_new_password: Choisissez un nouveau mot de passe.
+ invalid_token: Le jeton de réinitialisation de mot de passe n'est pas valide. Veuillez essayer de nouveau.
+ no_token: 'Vous ne pouvez accéder à cette page que depuis un courriel de réinitialisation de mot de passe. Si vous avez été redirigé à partir d''un courriel de réinitialisation de mot de passe, veuillez vous assurer que vous avez utilisé le lien fourni complet.
+
+'
+ send_instructions: 'Vous recevrez dans quelques instants un courriel avec des instructions pour réinitialiser votre mot de passe.
+
+'
+ send_paranoid_instructions: 'Vous recevrez dans quelques instants un courriel avec des instructions pour réinitialiser votre mot de passe.
+
+'
+ token_expired: Vous avez pris trop de temps pour réinitialiser votre mot de passe. Veuillez essayer de nouveau.
+ updated: Votre mot de passe a été modifié. Vous êtes maintenant connectée(e).
+ updated_not_active: 'Votre mot de passe a été modifié. Veuillez vous connecter avec votre nouveau mot de passe.
+
+'
+ registrations:
+ close_window: Vous pouvez fermer cette fenêtre si vous avez terminé.
+ destroy_confirm: 'La suppression de votre compte est irréversible. Toutes les données associées à votre compte seront effacées. Souhaitez-vous vraiment supprimer votre compte?
+
+'
+ destroyed: Votre compte a bien été supprimé.
+ email_and_phone_need_confirmation: 'Avant de terminer la mise-à-jour de votre compte, nous devons confirmer votre nouveau numéro et votre nouvelle adresse courriel. Veuillez suivre les instructions ci-dessous pour confirmer votre nouveau numéro, puis surveillez la réception d''un courriel envoyé par login.gov. Suivez le lien inclus dans le courriel pour confirmer votre nouvelle adresse courriel.
+
+'
+ email_update_needs_confirmation: 'Vous avez mis à jour votre compte, mais nous devons confirmer votre nouvelle adresse courriel. Surveillez la réception d''un courriel envoyé par nous et suivez le lien inscrit pour confirmer votre nouvelle adresse courriel.
+
+'
+ enabled_twofactor: Vous avez activé l'authentication à deux facteurs.
+ phone_update_needs_confirmation: 'Votre demande de mise à jour de votre numéro de téléphone a été traitée, mais nous devons d''abord confirmer votre nouveau numéro. Veuillez suivre les instructions ci-dessous. Si vous ne confirmez pas votre nouveau numéro, nous continuerons d''utiliser votre ancien numéro de téléphone.
+
+'
+ signed_up: Bienvenue! Vous avez créé un compte.
+ signed_up_but_inactive: 'Vous avez créé un compte. Cependant, nous n''avons pu vous connecter, car votre compte n''est pas encore activé.
+
+'
+ signed_up_but_locked: 'Vous avez créé un compte. Cependant, nous n''avons pu vous connecter, car votre compte est verrouillé.
+
+'
+ start:
+ accordion: Vous devrez
+ bullet_1_html: fournir votre adresse courriel et créer un mot de passe fort.
+ bullet_2_html: 'Activez l''authentication à deux facteurs. Cela permet d''ajouter un degré de sécurité à votre compte, car vous devez entrer un nouveau code envoyé à votre téléphone chaque fois que vous vous connectez.
+
+'
+ bullet_3_html: 'Fournissez de l''information de base, comme votre nom, adresse et numéro de sécurité sociale.
+
+'
+ bullet_4_html: 'Fournissez un numéro de compte bancaire, comme votre numéro de carte de crédit.
+
+'
+ bullet_5_html: 'Lorsque vous aurez terminé ce processus, nous vous donnerons une clé personnelle. Notez-la et placez-la en lieu sûr : c''est important. On vous demandera la clé personnelle chaque fois que vous apporterez des changements à votre compte.
+
+'
+ learn_more: En savoir plus sur la vérification de votre identité
+ updated: Votre compte a été mis à jour!
+ sessions:
+ already_signed_out: Vous êtes maintenant connecté(e).
+ signed_in: ''
+ signed_out: Vous êtes maintenant déconnecté(e).
+ two_factor_authentication:
+ buttons:
+ confirm_with_sms: Confirmer avec des messages texte
+ confirm_with_voice: Confirmer avec un appel vocal
+ choose_delivery_confirmation: 'Comment souhaitez-vous recevoir votre code de sécurité de confirmation pour %{phone}?
+
+'
+ choose_otp_delivery_html: 'Nous l''envoyons à %{phone} immédiatement. Les tarifs liés aux messages texte et aux données peuvent s''appliquer.
+
+'
+ contact_administrator: Veuillez communiquer avec votre administrateur système.
+ header_text: Entrez votre code de sécurité
+ invalid_otp: 'Ce code de sécurité est non valide. Vous pouvez essayer de l''entrer de nouveau ou demander un nouveau code de sécurité à utilisation unique.
+
+'
+ invalid_personal_key: Cette clé personnelle est non valide.
+ max_generic_login_attempts_reached: 'Votre compte est temporairement verrouillé.
+
+'
+ max_otp_login_attempts_reached: 'Votre compte est temporairement verrouillé, car vous avez entré le code de sécurité à utilisation unique de façon erronée à de trop nombreuses reprises.
+
+'
+ max_otp_requests_reached: NOT TRANSLATED YET
+ max_personal_key_login_attempts_reached: 'Votre compte est temporairement verrouillé, car vous avez entré le code de sécurité à utilisation unique de façon erronée à de trop nombreuses reprises.
+
+'
+ otp_delivery_preference:
+ instruction: Vous pourrez changer votre choix la prochaine fois que vous vous connecterez
+ sms: Message texte (SMS)
+ title: Comment souhaitez-vous recevoir votre code de sécurité?
+ voice: Appel téléphonique
+ otp_phone_label: Numéro de téléphone
+ otp_phone_label_info: Cellulaire ou ligne fixe est acceptable
+ otp_setup_html: "Chaque fois que vous vous connecterez, nous vous enverrons un code de sécurité à utilisation unique par message texte ou par appel téléphonique. Cela aide à protéger votre compte.\n"
+ otp_sms_disclaimer: Les tarifs liés aux messages texte et aux données peuvent s'appliquer.
+ personal_key_fallback:
+ link: Utlisez plutôt une clé personnelle
+ text_html: Vous n'avez pas accès à votre téléphone? %{link}.
+ personal_key_header_text: Entrez votre clé personnelle
+ personal_key_prompt: 'Vous pouvez utiliser cette clé personnelle une fois seulement. Si vous avez toujours besoin d''un code après votre connexion, allez à la page des réglages de votre compte pour en obtenir un nouveau.
+
+'
+ please_confirm: 'Votre numéro de téléphone a été entré. Confirmez-le en entrant le code de sécurité ci-dessous.
+
+'
+ please_try_again_html: Veuillez essayer de nouveau dans %{time_remaining}.
+ totp_fallback:
+ sms_link_text: Obtenir un code via message texte
+ text_html: 'Si vous ne pouvez utiliser votre application d''authentification maintenant, vous pouvez %{sms_link} ou %{voice_link}.
+
+'
+ voice_link_text: obtenir un code par appel téléphonique
+ totp_header_text: Entrez votre code d'application d'authentification
+ totp_info: Utilisez n'importe quelle application d'authentification pour balayer le code QR ci-dessous.
+ two_factor_setup: Ajoutez un numéro de téléphone
+ user:
+ new_otp_sent: Nous vous avons envoyé un code de sécurité à utilisation unique.
diff --git a/config/locales/errors/fr.yml b/config/locales/errors/fr.yml
new file mode 100644
index 00000000000..b96aad84756
--- /dev/null
+++ b/config/locales/errors/fr.yml
@@ -0,0 +1,35 @@
+---
+fr:
+ errors:
+ confirm_password_incorrect: Mot de passe incorrect.
+ invalid_authenticity_token: Oups, une erreur s'est produite. Veuillez vous connecter
+ de nouveau.
+ invalid_totp: Code non valide. Veuillez essayer de nouveau.
+ max_password_attempts_reached: Vous avez inscrit des mots de passe incorrects
+ un trop grand nombre de fois. Vous pouvez réinitialiser votre mot de passe en
+ utilisant le lien « Vous avez oublié votre mot de passe? ».
+ messages:
+ already_confirmed: a déjà été confirmé, veuillez essayer de vous connecter
+ blank: Veuillez remplir ce champ.
+ confirmation_code_incorrect: Code non valide. L'avez-vous inscrit correctement?
+ confirmation_invalid_token: Lien de confirmation non valide. Le lien est expiré ou vous avez déjà confirmé votre compte.
+ confirmation_period_expired: Lien de confirmation expiré. Vous pouvez cliquer sur « Envoyer les instructions de confirmation de nouveau » pour en obtenir un autre.
+ expired: est expiré, veuillez en demander un nouveau
+ format_mismatch: Veuillez vous assurer de respecter le format requis.
+ improbable_phone: Numéro de téléphone non valide. Veuillez vous assurer d'entrer un numéro de téléphone à 10 chiffres.
+ missing_field: Veuillez remplir ce champ.
+ no_password_reset_profile: Aucun profil récemment désactivé en raison d'une réinitialisation de mot de passe
+ no_pending_profile: Aucun profil en attente de vérification
+ not_found: introuvable
+ not_locked: n'a pas été verrouillé
+ not_saved:
+ one: "1 erreur a interdit la sauvegarde de cette %{resource} :"
+ other: "%{count} des erreurs ont empêché la sauvegarde de cette %{resource} :"
+ otp_incorrect: Code non valide. L'avez-vous inscrit correctement?
+ password_incorrect: Mot de passe incorrect
+ personal_key_incorrect: Clé personnelle incorrecte
+ requires_phone: vous demande d'entrer votre numéro de téléphone.
+ unauthorized_authn_context: Contexte d'authentification non autorisé
+ unauthorized_service_provider: Fournisseur de service non autorisé
+ weak_password: Votre mot de passe n'est pas assez fort. %{feedback}
+ not_authorized: Vous n'êtes pas autorisé(e) à effectuer cette action.
diff --git a/config/locales/event_types/fr.yml b/config/locales/event_types/fr.yml
new file mode 100644
index 00000000000..62131dacbc6
--- /dev/null
+++ b/config/locales/event_types/fr.yml
@@ -0,0 +1,13 @@
+---
+fr:
+ event_types:
+ account_created: Compte créé
+ account_verified: Compte vérifié
+ authenticated_at: Connecté à %{service_provider}
+ authenticator_disabled: Application d'authentification désactivée
+ authenticator_enabled: Application d'authentification activée
+ eastern_timestamp: "%{timestamp} (Eastern)"
+ email_changed: Adresse courriel modifiée
+ phone_changed: Numéro de téléphone modifié
+ phone_confirmed: Numéro de téléphone confirmé
+ usps_mail_sent: Lettre envoyée
diff --git a/config/locales/forms/fr.yml b/config/locales/forms/fr.yml
new file mode 100644
index 00000000000..22885c22e37
--- /dev/null
+++ b/config/locales/forms/fr.yml
@@ -0,0 +1,48 @@
+---
+fr:
+ forms:
+ buttons:
+ back: Retour
+ continue: Continuer
+ disable: Désactiver
+ edit: Modifier
+ enable: Activer
+ resend_confirmation: 'Envoyer les instructions de confirmation de nouveau '
+ send_security_code: Envoyer le code de sécurité
+ submit:
+ confirm_change: Confirmer le changement
+ default: Soumettre
+ update: Mettre à jour
+ confirmation:
+ show_hdr: Créez un mot de passe fort
+ passwords:
+ edit:
+ buttons:
+ submit: Changer le mot de passe
+ labels:
+ password: Nouveau mot de passe
+ show: Afficher le mot de passe
+ personal_key:
+ alternative: NOT TRANSLATED YET
+ confirmation_label: Clé personnelle
+ instructions: Veuillez confirmer que vous avez une copie de votre clé personnelle en l'entrant ci-dessous.
+ title: Entrez votre clé personnelle
+ registration:
+ labels:
+ email: Adresse courriel
+ totp_setup:
+ totp_intro_html: 'Lorsque vous vous connectez, vous pouvez obtenir votre code de sécurité d''une application d''authentification. %{link}
+
+'
+ totp_step_1: Démarrez votre application d'authentification
+ totp_step_2: Entrez cette clé dans l'application
+ totp_step_3: Entrez le code à partir de l'application
+ two_factor:
+ code: Code de sécurité
+ personal_key: Clé personnelle
+ try_again: Utilisez un autre numéro de téléphone
+ verify_profile:
+ instructions: Entrez le code à dix caractères qui se trouve dans la lettre que nous vous avons envoyée.
+ name: Code de confirmation
+ submit: Confirmer le compte
+ title: Confirmez votre compte
diff --git a/config/locales/headings/fr.yml b/config/locales/headings/fr.yml
new file mode 100644
index 00000000000..4f72ab3c2c9
--- /dev/null
+++ b/config/locales/headings/fr.yml
@@ -0,0 +1,32 @@
+---
+fr:
+ headings:
+ account:
+ account_history: Historique du compte
+ login_info: Votre compte
+ profile_info: Information du profil
+ reactivate: NOT TRANSLATED YET
+ two_factor: Authentication à deux facteurs
+ verified_account: Compte vérifié
+ confirmations:
+ new: Envoyer un autre courriel de confirmation
+ create_account_with_sp:
+ sp_text: utilise login.gov pour rendre la connexion plus facile et plus sécurisée.
+ create_account_without_sp: Créer un compte login.gov
+ edit_info:
+ email: Changez votre courriel
+ password: Changez votre mot de passe
+ phone: Entrez votre nouveau numéro de téléphone
+ passwords:
+ change: Changez votre mot de passe
+ confirm: Confirmez votre mot de passe actuel pour continuer
+ forgot: Vous avez oublié votre mot de passe?
+ personal_key: Voici votre clé personnelle
+ registrations:
+ enter_email: Commencer à créer le compte
+ session_timeout_warning: Vous avez besoin de plus de temps?
+ sign_in_with_sp: Connectez-vous pour continuer à %{sp}
+ sign_in_without_sp: Connexion
+ totp_setup:
+ new: Activer une application d'authentification
+ verify_email: Consultez vos courriels
diff --git a/config/locales/help_text/fr.yml b/config/locales/help_text/fr.yml
new file mode 100644
index 00000000000..846349f8f36
--- /dev/null
+++ b/config/locales/help_text/fr.yml
@@ -0,0 +1,14 @@
+---
+fr:
+ help_text:
+ change_factor: 'Avant de pouvoir modifier votre %{factor}, vous devrez confirmer votre mot de passe et recevoir un code de sécurité sur votre téléphone.
+
+'
+ requested_attributes:
+ address: Adresse postale
+ birthdate: Date de naissance
+ email: Adresse courriel
+ full_name: Nom complet
+ intro_html: Il s'agit de la seule information que %{app_name} partagera avec %{sp}
+ phone: Numéro de téléphone
+ social_security_number: Numéro de sécurité sociale
diff --git a/config/locales/idv/en.yml b/config/locales/idv/en.yml
index fd3104718a8..50dac2ba93e 100644
--- a/config/locales/idv/en.yml
+++ b/config/locales/idv/en.yml
@@ -30,7 +30,7 @@ en:
mail_limit_reached: You have have requested too much mail in the last month.
pattern_mismatch:
dob: Your date of birth must be entered in as mm/dd/yyyy
- "personal-key": >
+ personal_key: >
Please enter your personal key for this account. Example: ABC1-DEF2-G3HI-J456
ssn: 'Your Social Security Number must be entered in as ###-##-####'
zipcode: 'Your zipcode must be entered in as #####-####'
diff --git a/config/locales/idv/es.yml b/config/locales/idv/es.yml
index efb28f903ff..b54e1fbce07 100644
--- a/config/locales/idv/es.yml
+++ b/config/locales/idv/es.yml
@@ -26,7 +26,7 @@ es:
mail_limit_reached: NOT TRANSLATED YET
pattern_mismatch:
dob: NOT TRANSLATED YET
- "personal-key": NOT TRANSLATED YET
+ personal_key: NOT TRANSLATED YET
ssn: NOT TRANSLATED YET
zipcode: NOT TRANSLATED YET
form:
diff --git a/config/locales/idv/fr.yml b/config/locales/idv/fr.yml
new file mode 100644
index 00000000000..30e470e53b8
--- /dev/null
+++ b/config/locales/idv/fr.yml
@@ -0,0 +1,191 @@
+---
+fr:
+ idv:
+ buttons:
+ activate_by_mail: Activer par la poste
+ activate_by_phone: Activer par téléphone
+ cancel: Annuler et retourner à votre profil
+ continue: Continuer la vérification d'identité
+ help: Continuer jusqu'au Centre d'aide
+ mail:
+ resend: Envoyer une autre lettre
+ send: Envoyer une lettre
+ cancel:
+ modal_header: Souhaitez-vous vraiment annuler?
+ warning_header: Si vous annulez maintenant
+ warning_points:
+ - Nous ne serons pas en mesure de vérifier votre identité
+ - Nous ne conserverons pas de dossier contenant votre nom, adresse, date de naissance ou numéro de sécurité sociale
+ - Vous ne pourrez pas accéder à votre information de manière sécuritaire en utilisant login.gov
+ - Vous conserverez un compte login.gov pour votre adresse courriel
+ - Vous pouvez gérer ce compte sur la page de votre profil
+ errors:
+ bad_dob: Votre date de naissance doit être une date valide.
+ duplicate_ssn: Un compte existe déjà avec l'information que vous avez fournie.
+ finance_number_length: Le nombre doit comprendre entre %{minimum} et %{maximum} chiffres.
+ hardfail: Nous sommes incapables de vérifier votre identité pour le moment.
+ incorrect_password: Le mot de passe que vous avez inscrit est incorrect.
+ invalid_ccn: Vous devez inscrire seulement les 8 derniers chiffres du numéro de carte de crédit.
+ mail_limit_reached: Vous avez demandé trop de courriels au cours du dernier mois.
+ missing_finance: Vous devez fournir un numéro de compte bancaire.
+ pattern_mismatch:
+ dob: 'Votre date de naissance doit être inscrite de cette façon: mm/jj/aaaa'
+ personal_key: 'Veuillez inscrire votre clé personnelle pour ce compte, par exemple : ABC1-DEF2-G3HI-J456
+
+'
+ ssn: 'Votre numéro de sécurité sociale doit être inscrit de cette façon : ###-##-####'
+ zipcode: 'Votre code zip doit être inscrit de cette façon : #####-####'
+ form:
+ activate_by_mail: Activer mon compte par la poste.
+ address1: Adresse 1
+ address2: Adresse 2 (optional)
+ auto_loan: Numéro du prêt automobile
+ ccn: Les 8 derniers chiffres d'une carte de crédit
+ city: Ville
+ dob: Date de naissance
+ dob_hint: 'exemple : 01/17/1964'
+ first_name: Prénom
+ home_equity_line: Numéro du compte de crédit hypothécaire
+ last_name: Nom de famille
+ mortgage: Numéro du compte de prêt hypothécaire
+ no_alternate_phone_html: Je n'ai pas de numéro de téléphone. %{link}
+ password: Mot de passe
+ personal_details: Information personnelle
+ phone: Numéro de téléphone
+ phone_label_aside: Ligne mobile ou fixe
+ previous_address_add: Inscrire votre adresse précédente
+ previous_address_html: A déménagé au cours des 3 derniers mois?
+ select_financial_account: Sélectionnez un compte bancaire…
+ ssn_label_html: Numéro de sécurité sociale %{tooltip}
+ state: État
+ use_ccn: Fournissez un numéro de carte de crédit >
+ use_financial_account: Vous n'avez pas de carte de crédit?
+ zipcode: Code ZIP
+ index:
+ continue_link: Oui, continuer
+ paragraph_1: "Nous avons un partenariat avec une entreprise de confiance afin de vérifier cette information ainsi que votre identité. \n"
+ prompt: Est-ce que vous avez accès à toute cette information en ce moment?
+ section_1:
+ bullet_1: Nom complet
+ bullet_2: Adresse postale
+ bullet_3: Date de naissance
+ bullet_4: Numéro de sécurité sociale
+ header: Information personnelle
+ subheader: 'Nous allons commencer avec de l''information personnelle, comme:'
+ section_2:
+ bullet_1: Les 8 derniers chiffres d'une carte de crédit
+ bullet_2: Numéro du compte de prêt automobile
+ bullet_3: Numéro du compte de crédit hypothécaire
+ bullet_4: Numéro du compte de prêt hypothécaire
+ footnote: "Vous n'aurez jamais à payer quoi que ce soit avec nous, ni à partager le solde de vos comptes. Nous vérifions uniquement le numéro du compte pour vérifier votre identité. \n"
+ header: Information bancaire
+ subheader: 'Nous avons besoin de l''un des numéros de compte suivants:'
+ section_3:
+ bullet_1: Votre nom ou celui d'un membre de votre famille
+ bullet_2_html: "Not un téléphone virtuel, comme Google Voice ou Skype\n"
+ bullet_3_html: "Not un téléphone prépayé\n"
+ header: Une ligne téléphonique à votre nom
+ subheader: "Nous utilisons les informations des compagnies de téléphone pour vérifier votre identité. Le numéro de téléphone que vous nous donnez devrait: \n"
+ subheader: Pour vérifier votre identité, nous vous demandons de répondre à quelques questions
+ messages:
+ activated_html: 'Votre identité a été vérifiée. Si vous souhaitez modifier votre information vérifiée, veuillez %{link}.
+
+'
+ activated_link: communiquer avec nous
+ cancel: "Pour continuer, %{app} doit vérifier votre identité. Nous devons recueillir quelques informations personnelles de base ainsi que certaines informations financières pour compléter ce processus. Si vous n'avez pas cette information sous la main, vous pouvez continuer plus tard. \n"
+ confirm: Vous avez crypté vos données vérifiées
+ dupe_ssn1: "Si vous recevez ce message d'erreur, il est possible que vous ayez déjà créé et vérifié un compte, mais avec une adresse courriel différente. \n"
+ dupe_ssn2_html: Veuillez %{link} avec l'adresse courriel que vous avez utilisée originalement.
+ dupe_ssn2_link: déconnectez-vous puis connectez-vous à nouveau
+ finance:
+ disclaimer: En continuant, vous acceptez que %{app_name} compare l'information du compte que vous avez fournie avec des sources tiers afin de vérifier votre identité.
+ hint: Vous pouvez utiliser Mastercard, Visa, Diner's Club, ou JCB.
+ intro_account: 'Aidez-nous à vérifier votre identité en fournissant le numéro de votre prêt automobile, de votre prêt hypothécaire, ou de votre crédit hypothécaire.
+
+'
+ intro_ccn_help: comment votre information sera utilisée
+ intro_ccn_html: 'Aidez-nous à vérifier votre identité en fournissant les 8 derniers chiffres de votre carte de crédit. Pour en apprendre davantage sur %{intro_help_link}.
+
+'
+ no_account: Vous préférez utiliser une carte de crédit?
+ no_account_info: Aidez-nous à vérifier votre identité en fournissant les 8 derniers chiffres de votre carte de crédit.
+ hardfail: Nous ne pouvons pas vérifier votre identité pour le moment.
+ hardfail4: Vous pouvez également aller sur %{sp} où vous trouverez davantage d'aide pour accéder à nos services.
+ help_center: Visitez notre Centre d'aide pour en apprendre davantage sur la façon dont nous vérifions votre compte.
+ mail_sent: Votre lettre est en route
+ personal_details_verified: Information personnelle vérifiée!
+ personal_key: 'Il s''agit de votre nouvelle clé personnelle. Notez-la et conservez-la dans un endroit sécuritaire. Vous en aurez besoin si vous perdez votre mot de passe.
+
+'
+ phone:
+ alert: Cette ligne téléphonique doit être
+ in_your_name: en votre nom ou celui d'un membre de votre famille
+ intro: Nous pouvez envoyez un code de confirmation à un numéro de téléphone lié à votre identité légale seulement.
+ phone_of_record: appels téléphoniques
+ prepaid: avec un contrat, et non prépayé
+ same_as_2fa: 'Ce numéro de téléphone peut être le même que celui que vous utilisez pour configurer votre mot de passe à usage unique, tant et aussi longtemps qu''il respecte les critères mentionnés plus haut.
+
+'
+ review:
+ financial_info: Où se trouve l'information sur mon compte bancaire?
+ info_verified_html: Nous avons trouvé des enregistrements liés à votre téléphone%{phone_message}
+ intro: Vos informations vérifiées
+ select_verification_with_sp: Afin de vous protéger des fraudes d'identité, vous ne pouvez pas utiliser votre compte au %{sp_name} tant que vous ne l'aurez pas activé en entrant votre code de confirmation.
+ select_verification_without_sp: Afin de protéger votre compte des fraudes liées à l'identité, votre profil ne sera pas activé tant que vous n'aurez pas entré votre code de confirmation.
+ sessions:
+ no_pii: N'utilisez pas de véritables données personnelles (il s'agit d'une démonstration seulement)
+ pii: Information personnelle
+ success: Nous avons trouvé des données qui correspondent à %{pii_message}
+ usps:
+ bad_address: Je ne peux pas recevoir de courrier à cette adresse
+ byline: Nous posterons une lettre à l'adresse vérifiée dans nos dossiers. Celle-ci contient un code de confirmation.
+ resend: Envoyez-moi une autre lettre.
+ success: Elle devrait arriver dans 5 à 10 jours ouvrables.
+ modal:
+ attempts:
+ one: Il ne vous reste qu' une tentative.
+ other: Il ne vous reste que %{count} tentatives.
+ button:
+ fail: OK
+ warning: Essayez à nouveau
+ financials:
+ fail: 'Votre compte est verrouillé pour 24 heures durant lesquelles vous ne pourrez pas y accéder. Consultez notre Centre d''aide pour plus d''information sur la vérification de votre identité.
+
+'
+ heading: Nous ne trouvons pas de données qui correspondent à vos données financières.
+ warning: Veuillez vérifier l'information que vous avez fournie.
+ phone:
+ fail: 'Votre compte est verrouillé pour 24 heures au cours desquelles vous ne pourrez pas y accéder. Consultez notre Centre d''aide pour plus d''information sur la vérification de votre identité.
+
+'
+ heading: Nous ne trouvons pas de données qui correspondent à vos informations téléphoniques.
+ warning: Veuillez vérifier l'information que vous avez fournie.
+ sessions:
+ fail: "Votre compte est verrouillé pour 24 heures au cours desquelles vous ne pourrez pas y accéder. \n"
+ heading: Nous ne pouvons pas trouver de données concordant avec vos informations personnelles.
+ warning: "Veuillez vérifier l'information que vous avez fournie. Un numéro de sécurité sociale, un code ZIP ou une date de naissance mal écrits sont des erreurs communes. \n"
+ review:
+ dob: Date de naissance
+ full_name: Nom complet
+ mailing_address: Adresse postale
+ ssn: Numéro de sécurité sociale (SSN)
+ titles:
+ activated: Votre identité a déjà été vérifiée
+ cancel: Nous ne pouvons pas vérifier votre identité
+ complete: Vérification de votre identité complétée
+ dupe: 'Erreur: un compte existe déjà'
+ expectations: Ensuite, aidez-vous à vous identifier
+ fail: Nous sommes dans l'incapacité de vérifier votre identité
+ financials: Veuillez fournir un numéro de compte bancaire
+ hardfail: Nous sommes dans l'incapacité de vérifier votre identité
+ intro: Aidez-nous à vous identifier
+ mail:
+ resend: Vous voulez une autre lettre?
+ verify: Vous voulez une lettre?
+ phone: Numéro de téléphone enregistré
+ review: Réviser et soumettre
+ select_verification: Activer votre compte
+ session:
+ phone: Obtenez un code par téléphone
+ review: Encryptez vos données personnelles en entrant votre mot de passe
+ sessions: D'abord, dites-nous qui vous êtes
diff --git a/config/locales/instructions/fr.yml b/config/locales/instructions/fr.yml
new file mode 100644
index 00000000000..92df49f1ec8
--- /dev/null
+++ b/config/locales/instructions/fr.yml
@@ -0,0 +1,51 @@
+---
+fr:
+ instructions:
+ 2fa:
+ authenticator:
+ accordion_header: Balayez le code avec votre appareil mobile
+ confirm_code_html: Entrez le code à partir de votre application d'authentification. Si vous avez plusieurs comptes configurés dans votre application, entrez le code correspondant à %{email} à %{app}.%{tooltip}
+ or: or
+ sms:
+ confirm_code_html: Nous l'avons envoyé par message texte à %{number}. Vous avez besoin d'un autre code? %{resend_code_link}. Les frais liés aux messages texte peuvent s'appliquer.
+ fallback_html: Si vous ne pouvez recevoir de messages texte pour le moment, vous pouvez %{link}
+ voice:
+ confirm_code_html: Nous venons de vous appeler au %{number}. Vous voulez que nous vous appelions de nouveau? %{resend_code_link}
+ fallback_html: Si vous ne pouvez recevoir d'appels pour le moment, vous pouvez %{link}
+ wrong_number_html: Vous avez entré un mauvais numéro de téléphone? %{link}
+ account:
+ reactivate:
+ begin: NOT TRANSLATED YET
+ explanation: NOT TRANSLATED YET
+ intro: NOT TRANSLATED YET
+ modal:
+ copy: NOT TRANSLATED YET
+ heading: NOT TRANSLATED YET
+ with_key: NOT TRANSLATED YET
+ forgot_password:
+ close_window: Vous pourrez fermer cette fenêtre de navigateur lorsque vous aurez réinitialisé votre mot de passe.
+ password:
+ forgot: 'Vous ne connaissez pas votre mot de passe? Réinitialisez-le après avoir confirmé votre adresse courriel.
+
+'
+ help_text: 'Plus long et inhabituel est le mot de passe, plus difficile il sera à trouver. Évitez donc d''utiliser des phrases communes. Évitez aussi de répéter des mots de passe d''autres comptes en ligne comme les comptes bancaires, les comptes courriel et les comptes de médias sociaux.
+
+'
+ help_text_header: Conseils sur la sécurité du mot de passe
+ info:
+ lead: 'Il doit avoir une longueur minimale de %{min_length} caractères et ne pas être un mot de passe couramment utilisé. C''est tout!
+
+'
+ strength:
+ i: Très faible
+ ii: Faible
+ iii: Correct
+ intro: 'Force du mot de passe : '
+ iv: Bonne
+ v: Excellente!
+ personal_key_accent: Notez-la ou imprimez-la.
+ personal_key_html: 'Il s''agit de la seule façon de récupérer l''accès à votre compte si vous perdez votre mot de passe ou votre téléphone. %{accent}
+
+'
+ registration:
+ email: Choisissez une adresse que vous souhaitez utiliser pour les communications avec le gouvernement.
diff --git a/config/locales/jobs/en.yml b/config/locales/jobs/en.yml
index 5405f8f767d..b4ca13ad3bc 100644
--- a/config/locales/jobs/en.yml
+++ b/config/locales/jobs/en.yml
@@ -8,3 +8,5 @@ en:
message_repeat: >
Hello! Your login.gov one time security code is, %{code}, again, your
security code is, %{code}. Press 1 to repeat your code.
+ sms_otp_sender_job:
+ message: "%{code} is your %{app} one-time security code."
diff --git a/config/locales/jobs/es.yml b/config/locales/jobs/es.yml
index 6904a69ef59..c3fe2ebce3d 100644
--- a/config/locales/jobs/es.yml
+++ b/config/locales/jobs/es.yml
@@ -4,3 +4,5 @@ es:
voice_otp_sender_job:
message_final: NOT TRANSLATED YET
message_repeat: NOT TRANSLATED YET
+ sms_otp_sender_job:
+ message: NOT TRANSLATED YET
diff --git a/config/locales/jobs/fr.yml b/config/locales/jobs/fr.yml
new file mode 100644
index 00000000000..f9d11487577
--- /dev/null
+++ b/config/locales/jobs/fr.yml
@@ -0,0 +1,13 @@
+---
+fr:
+ jobs:
+ voice_otp_sender_job:
+ message_final: >
+ Bonjour! Votre code de sécurité à utilisation unique de login.gov
+ est, %{code}, de nouveau, votre code de sécurité est, %{code}, au revoir!
+ message_repeat: >
+ Bonjour! Votre code de sécurité à utilisation unique de login.gov
+ est, %{code}, de nouveau, votre code de sécurité est, %{code}. Appuyez sur 1
+ pour répéter votre code.
+ sms_otp_sender_job:
+ message: NOT TRANSLATED YET
diff --git a/config/locales/links/fr.yml b/config/locales/links/fr.yml
new file mode 100644
index 00000000000..60487a68a76
--- /dev/null
+++ b/config/locales/links/fr.yml
@@ -0,0 +1,36 @@
+---
+fr:
+ links:
+ account:
+ reactivate:
+ with_key: NOT TRANSLATED YET
+ without_key: NOT TRANSLATED YET
+ back_to_sp: Retour à %{sp}
+ cancel: Annuler
+ cancel_account_creation: "‹ Annuler la création du compte"
+ cancel_idv: "‹ Annuler la vérification du compte"
+ contact: Contact
+ copy: Copie
+ create_account: Créer un compte
+ help: Aide
+ next: Suivant
+ passwords:
+ forgot: Vous avez oublié votre mot de passe?
+ phone_confirmation:
+ auth_app_fallback_html: " ou %{link}."
+ fallback_to_sms_html: Envoyez-moi plutôt un message texte contenant le code
+ fallback_to_voice_html: Si vous ne pouvez recevoir de message texte pour le moment, vous pouvez obtenir un code de sécurité par %{link}
+ privacy_policy: Confidentialité et sécurité
+ remove: Retirer
+ resend: Envoyer le courriel de nouveau
+ reverify: NOT TRANSLATED YET
+ sign_in: Connexion
+ sign_out: Déconnexion
+ two_factor_authentication:
+ app: obtenir un code de sécurité par l'application d'authentification
+ resend_code:
+ sms: Recevoir un autre message texte
+ voice: Recevoir un autre appel téléphonique
+ sms: obtenir un code de sécurité par message texte
+ voice: obtenir un code par un appel téléphonique
+ what_is_totp: Qu'est-ce qu'une application d'authentification?
diff --git a/config/locales/mailer/fr.yml b/config/locales/mailer/fr.yml
new file mode 100644
index 00000000000..57717bd8818
--- /dev/null
+++ b/config/locales/mailer/fr.yml
@@ -0,0 +1,32 @@
+---
+fr:
+ mailer:
+ about: À propos %{app}
+ confirmation_instructions:
+ first_sentence:
+ confirmed: Vous tentez de changer votre adresse courriel?
+ reset_requested: 'Votre compte %{app} a été réinitialisé par un représentant
+ du soutien technique. Pour continuer, vous devez confirmer votre adresse
+ courriel.
+
+'
+ unconfirmed: Merci d'avoir créé un compte.
+ footer: Ce lien expirera dans %{confirmation_period}.
+ header: "%{intro} Veuillez cliquer sur le lien ci-dessous ou copier et coller
+ le lien complet dans votre navigateur.\n"
+ link_text: Confirmez votre adresse courriel
+ email_change_notice:
+ subject: Changez votre adresse courriel
+ email_reuse_notice:
+ subject: Cette adresse courriel est déjà associée à un compte.
+ help: Pour plus d'aide, visitez %{link}.
+ no_reply: Veuillez ne pas répondre à ce message.
+ privacy_policy: Politique de confidentialité
+ reset_password:
+ footer: Ce lien expire dans %{expires} heures.
+ header: 'Pour terminer la réinitialisation de votre mot de passe, veuillez cliquer
+ sur le lien ci-dessous ou copier et coller le lien complet dans votre navigateur.
+
+'
+ link_text: Réinitialisez votre mot de passe
+ sent_from: Envoyé à partir de %{app}
diff --git a/config/locales/notices/en.yml b/config/locales/notices/en.yml
index 98188b14bbd..89edc2ba0a8 100644
--- a/config/locales/notices/en.yml
+++ b/config/locales/notices/en.yml
@@ -1,7 +1,7 @@
---
en:
notices:
- account_recovery: Great! You have your personal key.
+ account_reactivation: Great! You have your personal key.
dap_html: >
diff --git a/config/locales/notices/es.yml b/config/locales/notices/es.yml
index 46c9667b8f6..d9e1d0713e6 100644
--- a/config/locales/notices/es.yml
+++ b/config/locales/notices/es.yml
@@ -1,7 +1,7 @@
---
es:
notices:
- account_recovery: NOT TRANSLATED YET
+ account_reactivation: NOT TRANSLATED YET
dap_html: NOT TRANSLATED YET
forgot_password:
use_diff_email:
diff --git a/config/locales/notices/fr.yml b/config/locales/notices/fr.yml
new file mode 100644
index 00000000000..09a60022aa3
--- /dev/null
+++ b/config/locales/notices/fr.yml
@@ -0,0 +1,52 @@
+---
+fr:
+ notices:
+ account_reactivation: NOT TRANSLATED YET
+ dap_html: " \n"
+ forgot_password:
+ first_paragraph_end: 'avec un lien pour réinitialiser votre mot de passe. Suivez le lien pour continuer à réinitialiser votre mot de passe.
+
+'
+ first_paragraph_start: Nous avons envoyé un courriel à
+ no_email_sent_explanation_start: Vous n'avez pas reçu de courriel?
+ resend_email_success: Nous avons envoyé un autre courriel de réinitialisation de mot de passe.
+ use_diff_email:
+ link: Créer un nouveau compte
+ text_html: Ou, %{link} en utilisant une adresse courriel différente.
+ password_changed: Vous avez changé votre mot de passe.
+ resend_confirmation_email:
+ success: Nous avons envoyé un autre courriel de confirmation.
+ send_code:
+ personal_key: Vous avez une nouvelle clé personnelle.
+ session_cleared: 'Pour votre sécurité, nous effacerons l''information que vous avez entrée si vous ne vous déplacez pas vers une nouvelle page dans les %{minutes} prochaines minutes.
+
+'
+ signed_up_but_unconfirmed:
+ first_paragraph_end: 'avec un lien pour confirmer votre adresse courriel. Suivez le lien pour continuer à créer votre compte.
+
+'
+ first_paragraph_start: Nous avons envoyé un courriel à
+ no_email_sent_explanation_start: Vous n'avez pas reçu de courriel?
+ terms_of_service:
+ link: Pratiques en matière de sécurité et énoncé concernant la Loi sur la protection des renseignements personnels
+ timeout_warning:
+ partially_signed_in:
+ continue: Continuer la connexion
+ message_html: 'Pour votre sécurité, nous annulerons votre connexion dans %{time_left_in_session}.
+
+'
+ sign_out: Annuler la connexion
+ signed_in:
+ continue: Gardez ma connexion active
+ message_html: 'Pour votre sécurité, nous vous déconnecterons dans %{time_left_in_session}, sauf en cas d''avis contraire de votre part.
+
+'
+ sign_out: Déconnectez-moi
+ totp_configured: Vous avez activé l'application d'authentification.
+ totp_disabled: Vous avez désactivé l'application d'authentification.
+ use_diff_email:
+ link: utilisez une adresse courriel différente
+ text_html: Or, %{link}
+ session_timedout: 'Nous vous avons déconnecté. Pour votre sécurité, %{app} désactive votre session lorsque vous demeurez sur une page sans vous déplacer pendant %{minutes} minutes.
+
+'
diff --git a/config/locales/openid_connect/fr.yml b/config/locales/openid_connect/fr.yml
new file mode 100644
index 00000000000..c131dccca62
--- /dev/null
+++ b/config/locales/openid_connect/fr.yml
@@ -0,0 +1,24 @@
+---
+fr:
+ openid_connect:
+ authorization:
+ errors:
+ bad_client_id: Mauvaise client_id
+ no_valid_acr_values: Valeurs acr_values inacceptables trouvées
+ no_valid_scope: Aucune étendue de données valide trouvée
+ redirect_uri_invalid: redirect_uri est non valide
+ redirect_uri_no_match: redirect_uri ne correspond pas au redirect_uri enregistré
+ logout:
+ errors:
+ id_token_hint: id_token_hint n'a pas été reconnu
+ token:
+ errors:
+ invalid_aud: Affirmation liée à l'auditoire non valide, attendu %{url}
+ invalid_authentication: Le client doit s'authentifier par PKCE ou private_key_jwt, code_challenge ou client_assertion manquant
+ invalid_code: code non valide
+ invalid_code_verifier: code_verifier ne correspondait pas à code_challenge
+ user_info:
+ errors:
+ malformed_authorization: Forme de l'en-tête d'autorisation non valide
+ no_authorization: Aucune en-tête d'autorisation fournie
+ not_found: L'autorisation pour le contenu du access_token fourni introuvable ou il peut être expiré
diff --git a/config/locales/pages/fr.yml b/config/locales/pages/fr.yml
new file mode 100644
index 00000000000..15529918a62
--- /dev/null
+++ b/config/locales/pages/fr.yml
@@ -0,0 +1,6 @@
+---
+fr:
+ pages:
+ page_not_found:
+ body: Vous pouvez revérifier votre lien et essayer de nouveau. (404)
+ header: La page que vous recherchez n'existe pas.
diff --git a/config/locales/saml_idp/fr.yml b/config/locales/saml_idp/fr.yml
new file mode 100644
index 00000000000..a0fa4f5b4cd
--- /dev/null
+++ b/config/locales/saml_idp/fr.yml
@@ -0,0 +1,12 @@
+---
+fr:
+ saml_idp:
+ shared:
+ saml_post_binding:
+ heading: Soumettre pour continuer
+ no_js: 'JavaScript semble être désactivé dans votre navigateur. Habituellement,
+ cette étape se déroule automatiquement, mais parce que vous avez désactivé
+ le JavaScript, veuillez cliquer sur le lien « soumettre » pour continuer
+ ou pour vous déconnecter.
+
+'
diff --git a/config/locales/shared/fr.yml b/config/locales/shared/fr.yml
new file mode 100644
index 00000000000..d54177fd046
--- /dev/null
+++ b/config/locales/shared/fr.yml
@@ -0,0 +1,7 @@
+---
+fr:
+ shared:
+ footer_lite:
+ gsa: Administration des services généraux des États-Unis
+ usa_banner:
+ official_site: Un site web officiel du gouvernement des États-Unis
diff --git a/config/locales/sign_up/fr.yml b/config/locales/sign_up/fr.yml
new file mode 100644
index 00000000000..44e45170925
--- /dev/null
+++ b/config/locales/sign_up/fr.yml
@@ -0,0 +1,16 @@
+---
+fr:
+ sign_up:
+ buttons:
+ cancel: Annuler et supprimer votre information
+ continue: Continuer la création du compte
+ cancel:
+ modal_header: Souhaitez-vous vraiment annuler?
+ success: Le dossier contenant votre information a été effacé
+ warning_header: Si vous annulez maintenant
+ warning_points:
+ - Vous n'aurez pas de compte login.gov
+ - Nous ne conserverons pas de dossier contenant votre adresse courriel, votre mot de passe et votre numéro de téléphone
+ - Vous ne serez pas en mesure d'accéder à votre information de façon sécuritaire en utilisant login.gov
+ registrations:
+ create_account: Créer un compte
diff --git a/config/locales/simple_form/fr.yml b/config/locales/simple_form/fr.yml
new file mode 100644
index 00000000000..9ba1c383810
--- /dev/null
+++ b/config/locales/simple_form/fr.yml
@@ -0,0 +1,11 @@
+---
+fr:
+ simple_form:
+ error_notification:
+ default_message: 'Veuillez examiner les problèmes ci-dessous :'
+ 'no': Non
+ required:
+ html: '* '
+ mark: "*"
+ text: Ce champ est requis
+ 'yes': Oui
diff --git a/config/locales/time/fr.yml b/config/locales/time/fr.yml
new file mode 100644
index 00000000000..32b0b06bebf
--- /dev/null
+++ b/config/locales/time/fr.yml
@@ -0,0 +1,5 @@
+---
+fr:
+ time:
+ formats:
+ event_timestamp: "%B %e, %Y à %-l:%M %p"
diff --git a/config/locales/titles/fr.yml b/config/locales/titles/fr.yml
new file mode 100644
index 00000000000..1b16899bd5c
--- /dev/null
+++ b/config/locales/titles/fr.yml
@@ -0,0 +1,33 @@
+---
+fr:
+ titles:
+ account: Compte
+ account_locked: Compte verrouillé
+ confirmations:
+ new: Envoyer les instructions de confirmation pour votre compte
+ show: Choisissez un mot de passe
+ edit_info:
+ email: Modifier votre adresse courriel
+ password: Modifier votre mot de passe
+ phone: Modifier votre numéro de téléphone
+ enter_2fa_code: Entrez le code de sécurité à utilisation unique
+ passwords:
+ change: Changez le mot de passe de votre compte
+ confirm: Confirmez le mot de passe de votre compte
+ forgot: Réinitialisez le mot de passe de votre compte
+ personal_key: Juste au cas
+ reactivate_account: Réactiver le profil
+ registrations:
+ new: S'inscrire et créer un compte
+ start: Démarrer
+ sign_up:
+ completion_html: Vous avez %{accent} avec %{app}
+ loa1: créé votre compte
+ loa3: verifié votre identité
+ totp_setup:
+ new: Configurer l'authentification à deux facteurs
+ two_factor_setup: Configuration de l'authentification à deux facteurs
+ verify_email: Consultez vos courriels
+ verify_profile: Activez votre compte
+ visitors:
+ index: Bienvenue
diff --git a/config/locales/tooltips/fr.yml b/config/locales/tooltips/fr.yml
new file mode 100644
index 00000000000..6f8d4f5b6d1
--- /dev/null
+++ b/config/locales/tooltips/fr.yml
@@ -0,0 +1,7 @@
+---
+fr:
+ tooltips:
+ authentication_app: Une application d'authentification est une application de sécurité mobile qui génère des codes de sécurité même si vous n'avez pas de connexion internet ou de service cellulaire.
+ ssn: 'Nous vous demandons votre numéro de sécurité sociale pour nous aider à prouver votre identité. Certaines des agences avec lesquelles nous collaborons pourraient devoir accéder à votre dossier.
+
+'
diff --git a/config/locales/user_mailer/fr.yml b/config/locales/user_mailer/fr.yml
new file mode 100644
index 00000000000..a343bcaa0b3
--- /dev/null
+++ b/config/locales/user_mailer/fr.yml
@@ -0,0 +1,30 @@
+---
+fr:
+ user_mailer:
+ contact_link_text: communiquez avec nous
+ email_changed:
+ help: 'Si vous préférez ne pas changher votre adresse courriel, veuillez visiter le %{help_link} de %{app} ou %{contact_link}.
+
+'
+ intro: L'adresse courriel de votre compte %{app} a été changée.
+ help_link_text: Centre d'aide
+ password_changed:
+ help: 'Si vous n''avez pas changé votre mot de passe, veuillez visiter le %{help_link} de %{app} ou %{contact_link}.
+
+'
+ intro: Le mot de passe de votre compte %{app} a été changé.
+ phone_changed:
+ help: 'Si vous ne souhaitiez pas changer votre numéro de téléphone, veuillez visiter le %{help_link} de %{app} ou %{contact_link}.
+
+'
+ intro: Le numéro de téléphone associé à votre compte %{app} a été changé.
+ subject: Nouveau numéro de téléphone
+ signup_with_your_email:
+ help: 'Si vous n''avez pas demandé un nouveau compte ou que vous soupçonnez qu''une erreur s''est produite, veuillez visiter le %{help_link} de %{app} ou %{contact_link}.
+
+'
+ intro: 'Cette adresse courriel est déjà associée à un compte %{app}, nous ne pouvons donc pas l''utiliser pour créer un nouveau compte. Pour vous connecter à votre compte existant, suivez le lien ci-dessous. Si vous ne tentez pas de vous connecter avec cette adresse courriel, vous pouvez ignorer ce message.
+
+'
+ link_text: Allez à %{app}
+ reset_password: Si vous ne vous souvenez plus de votre mot de passe, allez à %{app} pour le réinitialiser.
diff --git a/config/locales/users/fr.yml b/config/locales/users/fr.yml
new file mode 100644
index 00000000000..ba3cfef2c73
--- /dev/null
+++ b/config/locales/users/fr.yml
@@ -0,0 +1,22 @@
+---
+fr:
+ users:
+ personal_key:
+ close: Fermer
+ confirmation_error: Vous avez entré un clé personnelle erronée.
+ generated_on_html: Générée le %{date}
+ get_another: Obtenir une autre clé
+ header: Votre clé personnelle
+ help_text: |
+ Pour protéger votre compte, vous devez avoir un mot de passe et l'accès à votre téléphone ou application d'authentification au moment de la connexion. Si vous ne pouvez utiliser votre téléphone ou application, vous pouvez vous connecter avec votre clé personnelle.
+
+ Pour votre confidentialité et votre sécurité, login.gov ne conserve pas votre mot de passe ni votre clé personnelle. Seul(e) vous les connaissez. Seul(e) vous pouvez accéder à votre information personnelle et la partager .
+
+ Nous vous demandons de conserver votre clé personnelle à l'extérieur de votre ordinateur ou appareil mobile afin qu'elle soit en sûreté même si vos appareils sont volés ou si vos comptes en ligne sont piratés.
+
+ Si vous n'avez pas votre clé personnelle et que vous oubliez votre mot de passe, la seule façon de garder votre compte en sécurité est de vérifier que vous en êtes le(la) propriétaire légal(e).
+ help_text_header: Pourquoi dois-je conserver ma nouvelle clé sur papier?
+ print: Imprimer cette page
+ totp_setup:
+ new:
+ qr_img_alt: Code QR pour l'application d'authentification
diff --git a/config/locales/valid_email/fr.yml b/config/locales/valid_email/fr.yml
new file mode 100644
index 00000000000..2036952ded5
--- /dev/null
+++ b/config/locales/valid_email/fr.yml
@@ -0,0 +1,7 @@
+---
+fr:
+ valid_email:
+ validations:
+ email:
+ invalid: Format d'adresse courriel ou domaine entré non valide. Corrigez l'adresse
+ et entrez-la de nouveau.
diff --git a/config/locales/zxcvbn/en.yml b/config/locales/zxcvbn/en.yml
index 05e228179f1..7c530bb8064 100644
--- a/config/locales/zxcvbn/en.yml
+++ b/config/locales/zxcvbn/en.yml
@@ -2,39 +2,46 @@
en:
zxcvbn:
feedback:
- "A word by itself is easy to guess": A word by itself is easy to guess
- "Add another word or two_ Uncommon words are better_": >-
- Add another word or two. Uncommon words are better
- "All-uppercase is almost as easy to guess as all-lowercase": >-
- All-uppercase is almost as easy to guess as all-lowercase
- "Avoid dates and years that are associated with you": >-
- Avoid dates and years that are associated with you
- "Avoid recent years": Avoid recent years
- "Avoid repeated words and characters": Avoid repeated words and characters
- "Avoid sequences": Avoid sequences
- "Avoid years that are associated with you": Avoid years that are associated with you
- "Capitalization doesn't help very much": Capitalization doesn’t help very much
- "Common names and surnames are easy to guess": Common names and surnames are easy to guess
- "Dates are often easy to guess": Dates are often easy to guess
- "Names and surnames by themselves are easy to guess": >-
- Names and surnames by themselves are easy to guess
- "No need for symbols, digits, or uppercase letters": >-
- There is no need for symbols, digits, or uppercase letters
- "Predictable substitutions like '@' instead of 'a' don't help very much":
- Predictable substitutions like '@' instead of 'a' don’t help very much
- "Recent years are easy to guess": Recent years are easy to guess
- "Repeats like \"aaa\" are easy to guess": Repeats like "aaa" are easy to guess
- "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": >-
- Repeats like "abcabcabc" are only slightly harder to guess than "abc"
- "Reversed words aren't much harder to guess": Reversed words aren’t much harder to guess
- "Sequences like abc or 6543 are easy to guess": Sequences like abc or 6543 are easy to guess
- "Short keyboard patterns are easy to guess": Short keyboard patterns are easy to guess
- "Straight rows of keys are easy to guess": Straight rows of keys are easy to guess
- "This is a top-10 common password": This is a top-10 common password
- "This is a top-100 common password": This is a top-100 common password
- "This is a very common password": This is a very common password
- "This is similar to a commonly used password": This is similar to a commonly used password
- "Use a few words, avoid common phrases":
- For a stronger password, use a few words separated by spaces, but avoid common phrases
- "Use a longer keyboard pattern with more turns": >-
- Use a longer keyboard pattern with more turns
+ a_word_by_itself_is_easy_to_guess: A word by itself is easy to guess
+ add_another_word_or_two_uncommon_words_are_better: Add another word or two.
+ Uncommon words are better
+ all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase: All-uppercase is
+ almost as easy to guess as all-lowercase
+ avoid_dates_and_years_that_are_associated_with_you: Avoid dates and years that
+ are associated with you
+ avoid_recent_years: Avoid recent years
+ avoid_repeated_words_and_characters: Avoid repeated words and characters
+ avoid_sequences: Avoid sequences
+ avoid_years_that_are_associated_with_you: Avoid years that are associated with
+ you
+ capitalization_doesnt_help_very_much: Capitalization doesn’t help very much
+ common_names_and_surnames_are_easy_to_guess: Common names and surnames are easy
+ to guess
+ dates_are_often_easy_to_guess: Dates are often easy to guess
+ names_and_surnames_by_themselves_are_easy_to_guess: Names and surnames by themselves
+ are easy to guess
+ there_is_no_need_for_symbols_digits_or_uppercase_letters: There is no need for
+ symbols, digits, or uppercase letters
+ predictable_substitutions_like__instead_of_a_dont_help_very_much: Predictable
+ substitutions like '@' instead of 'a' don’t help very much
+ recent_years_are_easy_to_guess: Recent years are easy to guess
+ repeats_like_aaa_are_easy_to_guess: Repeats like "aaa" are easy to guess
+ repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc: Repeats like
+ "abcabcabc" are only slightly harder to guess than "abc"
+ reversed_words_arent_much_harder_to_guess: Reversed words aren’t much harder
+ to guess
+ sequences_like_abc_or_6543_are_easy_to_guess: Sequences like abc or 6543 are
+ easy to guess
+ short_keyboard_patterns_are_easy_to_guess: Short keyboard patterns are easy
+ to guess
+ straight_rows_of_keys_are_easy_to_guess: Straight rows of keys are easy to guess
+ this_is_a_top_10_common_password: This is a top-10 common password
+ this_is_a_top_100_common_password: This is a top-100 common password
+ this_is_a_very_common_password: This is a very common password
+ this_is_similar_to_a_commonly_used_password: This is similar to a commonly used
+ password
+ for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases: For
+ a stronger password, use a few words separated by spaces, but avoid common
+ phrases
+ use_a_longer_keyboard_pattern_with_more_turns: Use a longer keyboard pattern
+ with more turns
diff --git a/config/locales/zxcvbn/es.yml b/config/locales/zxcvbn/es.yml
index 086ffa24261..38be5d1fb42 100644
--- a/config/locales/zxcvbn/es.yml
+++ b/config/locales/zxcvbn/es.yml
@@ -2,30 +2,30 @@
es:
zxcvbn:
feedback:
- "A word by itself is easy to guess": NOT TRANSLATED YET
- "Add another word or two_ Uncommon words are better_": NOT TRANSLATED YET
- "All-uppercase is almost as easy to guess as all-lowercase": NOT TRANSLATED YET
- "Avoid dates and years that are associated with you": NOT TRANSLATED YET
- "Avoid recent years": NOT TRANSLATED YET
- "Avoid repeated words and characters": NOT TRANSLATED YET
- "Avoid sequences": NOT TRANSLATED YET
- "Avoid years that are associated with you": NOT TRANSLATED YET
- "Capitalization doesn't help very much": NOT TRANSLATED YET
- "Common names and surnames are easy to guess": NOT TRANSLATED YET
- "Dates are often easy to guess": NOT TRANSLATED YET
- "Names and surnames by themselves are easy to guess": NOT TRANSLATED YET
- "No need for symbols, digits, or uppercase letters": NOT TRANSLATED YET
- "Predictable substitutions like '@' instead of 'a' don't help very much": NOT TRANSLATED YET
- "Recent years are easy to guess": NOT TRANSLATED YET
- "Repeats like \"aaa\" are easy to guess": NOT TRANSLATED YET
- "Repeats like \"abcabcabc\" are only slightly harder to guess than \"abc\"": NOT TRANSLATED YET
- "Reversed words aren't much harder to guess": NOT TRANSLATED YET
- "Sequences like abc or 6543 are easy to guess": NOT TRANSLATED YET
- "Short keyboard patterns are easy to guess": NOT TRANSLATED YET
- "Straight rows of keys are easy to guess": NOT TRANSLATED YET
- "This is a top-10 common password": NOT TRANSLATED YET
- "This is a top-100 common password": NOT TRANSLATED YET
- "This is a very common password": NOT TRANSLATED YET
- "This is similar to a commonly used password": NOT TRANSLATED YET
- "Use a few words, avoid common phrases": NOT TRANSLATED YET
- "Use a longer keyboard pattern with more turns": NOT TRANSLATED YET
+ a_word_by_itself_is_easy_to_guess: NOT TRANSLATED YET
+ add_another_word_or_two_uncommon_words_are_better: NOT TRANSLATED YET
+ all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase: NOT TRANSLATED YET
+ avoid_dates_and_years_that_are_associated_with_you: NOT TRANSLATED YET
+ avoid_recent_years: NOT TRANSLATED YET
+ avoid_repeated_words_and_characters: NOT TRANSLATED YET
+ avoid_sequences: NOT TRANSLATED YET
+ avoid_years_that_are_associated_with_you: NOT TRANSLATED YET
+ capitalization_doesnt_help_very_much: NOT TRANSLATED YET
+ common_names_and_surnames_are_easy_to_guess: NOT TRANSLATED YET
+ dates_are_often_easy_to_guess: NOT TRANSLATED YET
+ names_and_surnames_by_themselves_are_easy_to_guess: NOT TRANSLATED YET
+ there_is_no_need_for_symbols_digits_or_uppercase_letters: NOT TRANSLATED YET
+ predictable_substitutions_like__instead_of_a_dont_help_very_much: NOT TRANSLATED YET
+ recent_years_are_easy_to_guess: NOT TRANSLATED YET
+ repeats_like_aaa_are_easy_to_guess: NOT TRANSLATED YET
+ repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc: NOT TRANSLATED YET
+ reversed_words_arent_much_harder_to_guess: NOT TRANSLATED YET
+ sequences_like_abc_or_6543_are_easy_to_guess: NOT TRANSLATED YET
+ short_keyboard_patterns_are_easy_to_guess: NOT TRANSLATED YET
+ straight_rows_of_keys_are_easy_to_guess: NOT TRANSLATED YET
+ this_is_a_top_10_common_password: NOT TRANSLATED YET
+ this_is_a_top_100_common_password: NOT TRANSLATED YET
+ this_is_a_very_common_password: NOT TRANSLATED YET
+ this_is_similar_to_a_commonly_used_password: NOT TRANSLATED YET
+ for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases: NOT TRANSLATED YET
+ use_a_longer_keyboard_pattern_with_more_turns: NOT TRANSLATED YET
diff --git a/config/locales/zxcvbn/fr.yml b/config/locales/zxcvbn/fr.yml
new file mode 100644
index 00000000000..da4adf5885c
--- /dev/null
+++ b/config/locales/zxcvbn/fr.yml
@@ -0,0 +1,51 @@
+---
+fr:
+ zxcvbn:
+ feedback:
+ a_word_by_itself_is_easy_to_guess: Un mot seul est facile à deviner
+ add_another_word_or_two_uncommon_words_are_better: Ajoutez un ou deux autres
+ mots. Les mots non communs sont plus efficaces
+ all_uppercase_is_almost_as_easy_to_guess_as_all_lowercase: Tout en majuscules
+ est presque aussi facile à deviner que tout en minuscules
+ avoid_dates_and_years_that_are_associated_with_you: Évitez les dates et années
+ qui vous sont associées
+ avoid_recent_years: Évitez les années récentes
+ avoid_repeated_words_and_characters: Évitez les mots et caractères répétés
+ avoid_sequences: Évitez les séquences
+ avoid_years_that_are_associated_with_you: Évitez les années qui vous sont associées
+ capitalization_doesnt_help_very_much: La capitalisation n'aide pas beaucoup
+ common_names_and_surnames_are_easy_to_guess: Les prénoms et noms de famille
+ communs sont faciles à deviner
+ dates_are_often_easy_to_guess: Les dates sont souvent faciles à deviner
+ names_and_surnames_by_themselves_are_easy_to_guess: Les prénoms et noms de famille
+ seuls sont faciles à deviner
+ there_is_no_need_for_symbols_digits_or_uppercase_letters: Les symboles, les
+ chiffres ou les lettres majuscules ne sont pas nécessaires
+ predictable_substitutions_like__instead_of_a_dont_help_very_much: Les remplacements
+ prévisibles comme es « @ » au lieu de « à » n'aident pas beaucoup
+ recent_years_are_easy_to_guess: Les années récentes sont faciles à deviner
+ repeats_like_aaa_are_easy_to_guess: Les répétitions comme « aaa » sont faciles
+ à deviner
+ repeats_like_abcabcabc_are_only_slightly_harder_to_guess_than_abc: |-
+ Les répétitions comme « abcabcabc » sont à peine
+ plus difficiles à deviner que « abc »
+ reversed_words_arent_much_harder_to_guess: Les mots inversés ne sont pas très
+ difficiles à deviner
+ sequences_like_abc_or_6543_are_easy_to_guess: Les séquences comme abc ou 6543
+ sont faciles à deviner
+ short_keyboard_patterns_are_easy_to_guess: Les motifs de clavier courts sont
+ faciles à deviner
+ straight_rows_of_keys_are_easy_to_guess: Les rangées de lettres consécutives
+ sont faciles à deviner
+ this_is_a_top_10_common_password: Il s'agit d'un des 10 mots de passe les plus
+ communs
+ this_is_a_top_100_common_password: Il s'agit d'un des 100 mots de passe les
+ plus communs
+ this_is_a_very_common_password: Il s'agit d'un mot de passe très commun
+ this_is_similar_to_a_commonly_used_password: Ceci est similaire à un mot de
+ passe souvent utilisé
+ for_a_stronger_password_use_a_few_words_separated_by_spaces_but_avoid_common_phrases: Pour
+ créer un mot de passe plus fort, utilisez quelques mots séparés par des espaces,
+ mais évitez les phrases communes
+ use_a_longer_keyboard_pattern_with_more_turns: Utilisez un motif de clavier
+ plus long avec plus de tours
diff --git a/config/routes.rb b/config/routes.rb
index 9e81f39e9fa..fdfa9f1835e 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -2,60 +2,7 @@
require 'sidekiq/web'
mount Sidekiq::Web => '/sidekiq', constraints: AdminConstraint.new
- # Devise handles login itself. It's first in the chain to avoid a redirect loop during
- # authentication failure.
- devise_for(
- :users,
- skip: %i[confirmations sessions registrations two_factor_authentication],
- controllers: { passwords: 'users/reset_passwords' }
- )
-
- # Additional device controller routes.
- devise_scope :user do
- get '/' => 'users/sessions#new', as: :new_user_session
- post '/' => 'users/sessions#create', as: :user_session
- get '/active' => 'users/sessions#active'
-
- get '/login/two_factor/authenticator' => 'two_factor_authentication/totp_verification#show'
- post '/login/two_factor/authenticator' => 'two_factor_authentication/totp_verification#create'
- get '/login/two_factor/personal_key' => 'two_factor_authentication/personal_key_verification#show'
- post '/login/two_factor/personal_key' => 'two_factor_authentication/personal_key_verification#create'
- get '/login/two_factor/:otp_delivery_preference' => 'two_factor_authentication/otp_verification#show',
- as: :login_two_factor
- post '/login/two_factor/:otp_delivery_preference' => 'two_factor_authentication/otp_verification#create',
- as: :login_otp
-
- get '/reauthn' => 'mfa_confirmation#new', as: :user_password_confirm
- post '/reauthn' => 'mfa_confirmation#create', as: :reauthn_user_password
- get '/timeout' => 'users/sessions#timeout'
- end
-
- if Figaro.env.enable_test_routes == 'true'
- namespace :test do
- # Assertion granting test start + return.
- get '/saml' => 'saml_test#start'
- get '/saml/decode_assertion' => 'saml_test#start'
- post '/saml/decode_assertion' => 'saml_test#decode_response'
- post '/saml/decode_slo_request' => 'saml_test#decode_slo_request'
- end
- end
-
- # Non-devise-controller routes. Alphabetically sorted.
- get '/.well-known/openid-configuration' => 'openid_connect/configuration#index',
- as: :openid_connect_configuration
-
- get '/account' => 'accounts#show'
- get '/account/reactivate/start' => 'reactivate_account#index', as: :reactivate_account
- put '/account/reactivate/start' => 'reactivate_account#update'
- get '/account/reactivate/verify_password' => 'users/verify_password#new', as: :verify_password
- put '/account/reactivate/verify_password' => 'users/verify_password#update', as: :update_verify_password
- get '/account/reactivate/verify_personal_key' => 'users/verify_personal_key#new',
- as: :verify_personal_key
- post '/account/reactivate/verify_personal_key' => 'users/verify_personal_key#create',
- as: :create_verify_personal_key
- get '/account/verify_phone' => 'users/verify_profile_phone#index', as: :verify_profile_phone
- post '/account/verify_phone' => 'users/verify_profile_phone#create'
-
+ # Non i18n routes. Alphabetically sorted.
get '/api/health/workers' => 'health/workers#index'
get '/api/openid_connect/certs' => 'openid_connect/certs#index'
post '/api/openid_connect/token' => 'openid_connect/token#create'
@@ -69,84 +16,143 @@
post '/api/service_provider' => 'service_provider#update'
- delete '/authenticator_setup' => 'users/totp_setup#disable', as: :disable_totp
- get '/authenticator_setup' => 'users/totp_setup#new'
- patch '/authenticator_setup' => 'users/totp_setup#confirm'
- get '/authenticator_start' => 'users/totp_setup#start'
+ get '/openid_connect/authorize' => 'openid_connect/authorization#index'
+ get '/openid_connect/logout' => 'openid_connect/logout#index'
- get '/forgot_password' => 'forgot_password#show'
+ # i18n routes. Alphabetically sorted.
+ scope '(:locale)', locale: /#{I18n.available_locales.join('|')}/ do
+ # Devise handles login itself. It's first in the chain to avoid a redirect loop during
+ # authentication failure.
+ devise_for(
+ :users,
+ skip: %i[confirmations sessions registrations two_factor_authentication],
+ controllers: { passwords: 'users/reset_passwords' }
+ )
+
+ # Additional device controller routes.
+ devise_scope :user do
+ get '/' => 'users/sessions#new', as: :new_user_session
+ post '/' => 'users/sessions#create', as: :user_session
+ get '/active' => 'users/sessions#active'
+
+ get '/login/two_factor/authenticator' => 'two_factor_authentication/totp_verification#show'
+ post '/login/two_factor/authenticator' => 'two_factor_authentication/totp_verification#create'
+ get '/login/two_factor/personal_key' => 'two_factor_authentication/personal_key_verification#show'
+ post '/login/two_factor/personal_key' => 'two_factor_authentication/personal_key_verification#create'
+ get '/login/two_factor/:otp_delivery_preference' => 'two_factor_authentication/otp_verification#show',
+ as: :login_two_factor
+ post '/login/two_factor/:otp_delivery_preference' => 'two_factor_authentication/otp_verification#create',
+ as: :login_otp
+
+ get '/reauthn' => 'mfa_confirmation#new', as: :user_password_confirm
+ post '/reauthn' => 'mfa_confirmation#create', as: :reauthn_user_password
+ get '/timeout' => 'users/sessions#timeout'
+ end
- get '/manage/email' => 'users/emails#edit'
- match '/manage/email' => 'users/emails#update', via: %i[patch put]
- get '/manage/password' => 'users/passwords#edit'
- patch '/manage/password' => 'users/passwords#update'
- get '/manage/phone' => 'users/phones#edit'
- match '/manage/phone' => 'users/phones#update', via: %i[patch put]
- get '/manage/personal_key' => 'users/personal_keys#show', as: :manage_personal_key
- post '/manage/personal_key' => 'users/personal_keys#update'
+ if Figaro.env.enable_test_routes == 'true'
+ namespace :test do
+ # Assertion granting test start + return.
+ get '/saml' => 'saml_test#start'
+ get '/saml/decode_assertion' => 'saml_test#start'
+ post '/saml/decode_assertion' => 'saml_test#decode_response'
+ post '/saml/decode_slo_request' => 'saml_test#decode_slo_request'
+ end
+ end
- get '/openid_connect/authorize' => 'openid_connect/authorization#index'
- get '/openid_connect/logout' => 'openid_connect/logout#index'
+ # Non-devise-controller routes. Alphabetically sorted.
+ get '/.well-known/openid-configuration' => 'openid_connect/configuration#index',
+ as: :openid_connect_configuration
+
+ get '/account' => 'accounts#show'
+ get '/account/reactivate/start' => 'reactivate_account#index', as: :reactivate_account
+ put '/account/reactivate/start' => 'reactivate_account#update'
+ get '/account/reactivate/verify_password' => 'users/verify_password#new', as: :verify_password
+ put '/account/reactivate/verify_password' => 'users/verify_password#update', as: :update_verify_password
+ get '/account/reactivate/verify_personal_key' => 'users/verify_personal_key#new',
+ as: :verify_personal_key
+ post '/account/reactivate/verify_personal_key' => 'users/verify_personal_key#create',
+ as: :create_verify_personal_key
+ get '/account/verify_phone' => 'users/verify_profile_phone#index', as: :verify_profile_phone
+ post '/account/verify_phone' => 'users/verify_profile_phone#create'
+
+ delete '/authenticator_setup' => 'users/totp_setup#disable', as: :disable_totp
+ get '/authenticator_setup' => 'users/totp_setup#new'
+ patch '/authenticator_setup' => 'users/totp_setup#confirm'
+ get '/authenticator_start' => 'users/totp_setup#start'
+
+ get '/forgot_password' => 'forgot_password#show'
+
+ get '/manage/email' => 'users/emails#edit'
+ match '/manage/email' => 'users/emails#update', via: %i[patch put]
+ get '/manage/password' => 'users/passwords#edit'
+ patch '/manage/password' => 'users/passwords#update'
+ get '/manage/phone' => 'users/phones#edit'
+ match '/manage/phone' => 'users/phones#update', via: %i[patch put]
+ get '/manage/personal_key' => 'users/personal_keys#show', as: :manage_personal_key
+ post '/manage/personal_key' => 'users/personal_keys#update'
+
+ get '/otp/send' => 'users/two_factor_authentication#send_code'
+ get '/phone_setup' => 'users/two_factor_authentication_setup#index'
+ patch '/phone_setup' => 'users/two_factor_authentication_setup#set'
+ get '/users/two_factor_authentication' => 'users/two_factor_authentication#show',
+ as: :user_two_factor_authentication # route name is used by two_factor_authentication gem
+
+ get '/profile', to: redirect('/account')
+ get '/profile/reactivate', to: redirect('/account/reactivate')
+ get '/profile/verify', to: redirect('/account/verify')
+
+ post '/sign_up/create_password' => 'sign_up/passwords#create', as: :sign_up_create_password
+ get '/sign_up/email/confirm' => 'sign_up/email_confirmations#create',
+ as: :sign_up_create_email_confirmation
+ get '/sign_up/enter_email' => 'sign_up/registrations#new', as: :sign_up_email
+ post '/sign_up/enter_email' => 'sign_up/registrations#create', as: :sign_up_register
+ get '/sign_up/enter_email/resend' => 'sign_up/email_resend#new', as: :sign_up_email_resend
+ post '/sign_up/enter_email/resend' => 'sign_up/email_resend#create',
+ as: :sign_up_create_email_resend
+ get '/sign_up/enter_password' => 'sign_up/passwords#new'
+ get '/sign_up/personal_key' => 'sign_up/personal_keys#show'
+ post '/sign_up/personal_key' => 'sign_up/personal_keys#update'
+ get '/sign_up/start' => 'sign_up/registrations#show', as: :sign_up_start
+ get '/sign_up/verify_email' => 'sign_up/emails#show', as: :sign_up_verify_email
+ get '/sign_up/completed' => 'sign_up/completions#show', as: :sign_up_completed
+ post '/sign_up/completed' => 'sign_up/completions#update'
+
+ match '/sign_out' => 'sign_out#destroy', via: %i[get post delete]
+
+ delete '/users' => 'users#destroy', as: :destroy_user
+
+ if FeatureManagement.enable_identity_verification?
+ get '/verify' => 'verify#index'
+ get '/verify/activated' => 'verify#activated'
+ get '/verify/address' => 'verify/address#index'
+ get '/verify/cancel' => 'verify#cancel'
+ get '/verify/confirmations' => 'verify/confirmations#show'
+ post '/verify/confirmations' => 'verify/confirmations#update'
+ get '/verify/fail' => 'verify#fail'
+ get '/verify/finance' => 'verify/finance#new'
+ put '/verify/finance' => 'verify/finance#create'
+ get '/verify/finance/other' => 'verify/finance_other#new'
+ get '/verify/phone' => 'verify/phone#new'
+ put '/verify/phone' => 'verify/phone#create'
+ get '/verify/review' => 'verify/review#new'
+ put '/verify/review' => 'verify/review#create'
+ get '/verify/session' => 'verify/sessions#new'
+ put '/verify/session' => 'verify/sessions#create'
+ delete '/verify/session' => 'verify/sessions#destroy'
+ get '/verify/session/dupe' => 'verify/sessions#dupe'
- get '/otp/send' => 'users/two_factor_authentication#send_code'
- get '/phone_setup' => 'users/two_factor_authentication_setup#index'
- patch '/phone_setup' => 'users/two_factor_authentication_setup#set'
- get '/users/two_factor_authentication' => 'users/two_factor_authentication#show',
- as: :user_two_factor_authentication # route name is used by two_factor_authentication gem
-
- get '/profile', to: redirect('/account')
- get '/profile/reactivate', to: redirect('/account/reactivate')
- get '/profile/verify', to: redirect('/account/verify')
-
- post '/sign_up/create_password' => 'sign_up/passwords#create', as: :sign_up_create_password
- get '/sign_up/email/confirm' => 'sign_up/email_confirmations#create',
- as: :sign_up_create_email_confirmation
- get '/sign_up/enter_email' => 'sign_up/registrations#new', as: :sign_up_email
- post '/sign_up/enter_email' => 'sign_up/registrations#create', as: :sign_up_register
- get '/sign_up/enter_email/resend' => 'sign_up/email_resend#new', as: :sign_up_email_resend
- post '/sign_up/enter_email/resend' => 'sign_up/email_resend#create',
- as: :sign_up_create_email_resend
- get '/sign_up/enter_password' => 'sign_up/passwords#new'
- get '/sign_up/personal_key' => 'sign_up/personal_keys#show'
- post '/sign_up/personal_key' => 'sign_up/personal_keys#update'
- get '/sign_up/start' => 'sign_up/registrations#show', as: :sign_up_start
- get '/sign_up/verify_email' => 'sign_up/emails#show', as: :sign_up_verify_email
- get '/sign_up/completed' => 'sign_up/completions#show', as: :sign_up_completed
- post '/sign_up/completed' => 'sign_up/completions#update'
-
- delete '/users' => 'users#destroy', as: :destroy_user
-
- if FeatureManagement.enable_identity_verification?
- get '/verify' => 'verify#index'
- get '/verify/activated' => 'verify#activated'
- get '/verify/address' => 'verify/address#index'
- get '/verify/cancel' => 'verify#cancel'
- get '/verify/confirmations' => 'verify/confirmations#show'
- post '/verify/confirmations' => 'verify/confirmations#update'
- get '/verify/fail' => 'verify#fail'
- get '/verify/finance' => 'verify/finance#new'
- put '/verify/finance' => 'verify/finance#create'
- get '/verify/finance/other' => 'verify/finance_other#new'
- get '/verify/phone' => 'verify/phone#new'
- put '/verify/phone' => 'verify/phone#create'
- get '/verify/review' => 'verify/review#new'
- put '/verify/review' => 'verify/review#create'
- get '/verify/session' => 'verify/sessions#new'
- put '/verify/session' => 'verify/sessions#create'
- delete '/verify/session' => 'verify/sessions#destroy'
- get '/verify/session/dupe' => 'verify/sessions#dupe'
+ end
- end
+ if FeatureManagement.enable_usps_verification?
+ get '/account/verify' => 'users/verify_account#index', as: :verify_account
+ post '/account/verify' => 'users/verify_account#create'
+ get '/verify/usps' => 'verify/usps#index'
+ put '/verify/usps' => 'verify/usps#create'
+ end
- if FeatureManagement.enable_usps_verification?
- get '/account/verify' => 'users/verify_account#index', as: :verify_account
- post '/account/verify' => 'users/verify_account#create'
- get '/verify/usps' => 'verify/usps#index'
- put '/verify/usps' => 'verify/usps#create'
+ root to: 'users/sessions#new'
end
- root to: 'users/sessions#new'
-
# Make sure any new routes are added above this line!
# The line below will route all requests that aren't
# defined route to the 404 page. Therefore, anything you put after this rule
diff --git a/config/service_providers.yml b/config/service_providers.yml
index e61ac94bdde..199a8dc5e22 100644
--- a/config/service_providers.yml
+++ b/config/service_providers.yml
@@ -383,7 +383,17 @@ production:
'urn:gov:dhs.cbp.jobs:openidconnect:jenkins-pspd-credential-service':
friendly_name: 'CBP PSPD Trusted Traveler Programs'
agency: 'DHS'
- logo: 'cbp.png'
+ logo: 'cbp-ttp.png'
cert: 'cbp_goes_pre_prod'
redirect_uris:
- 'http://10.156.152.27/login'
+
+ 'urn:gov:dhs.cbp.jobs:openidconnect:aws-cbp-ttp':
+ agency: 'DHS'
+ allow_on_prod_chef_env: 'true'
+ block_encryption: 'aes256-cbc'
+ cert: 'cbp_goes_prod'
+ friendly_name: 'CBP Trusted Traveler Programs'
+ logo: 'cbp-ttp.png'
+ redirect_uris:
+ - 'https://ttp.cbp.dhs.gov'
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
index 8a0466b9b10..f72f4153bcd 100644
--- a/config/sidekiq.yml
+++ b/config/sidekiq.yml
@@ -3,4 +3,5 @@
- voice
- mailers
- analytics
+ - idv
:logfile: 'log/sidekiq.log'
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000000..41ba3b3cfa3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,2114 @@
+{
+ "name": "upaya",
+ "version": "0.0.1",
+ "lockfileVersion": 1,
+ "dependencies": {
+ "acorn": {
+ "version": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c=",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz",
+ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=",
+ "dev": true,
+ "dependencies": {
+ "acorn": {
+ "version": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz",
+ "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=",
+ "dev": true
+ }
+ }
+ },
+ "ajv": {
+ "version": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz",
+ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=",
+ "dev": true,
+ "dependencies": {
+ "json-stable-stringify": {
+ "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "dev": true
+ }
+ }
+ },
+ "ajv-keywords": {
+ "version": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz",
+ "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=",
+ "dev": true
+ },
+ "amdefine": {
+ "version": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
+ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
+ "dev": true,
+ "optional": true
+ },
+ "ansi-escapes": {
+ "version": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz",
+ "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "argparse": {
+ "version": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
+ "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
+ "dev": true
+ },
+ "array-filter": {
+ "version": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
+ "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
+ "dev": true
+ },
+ "array-map": {
+ "version": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
+ "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
+ "dev": true
+ },
+ "array-reduce": {
+ "version": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
+ "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
+ "dev": true
+ },
+ "array-union": {
+ "version": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true
+ },
+ "array-uniq": {
+ "version": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "arrify": {
+ "version": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asn1.js": {
+ "version": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.9.1.tgz",
+ "integrity": "sha1-SLokC0WpKA6UdImQull9IWYX/UA=",
+ "dev": true
+ },
+ "assert": {
+ "version": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz",
+ "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
+ "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=",
+ "dev": true
+ },
+ "astw": {
+ "version": "https://registry.npmjs.org/astw/-/astw-2.2.0.tgz",
+ "integrity": "sha1-e9QXhNMkk5h66yOba04cV6hzuRc=",
+ "dev": true
+ },
+ "async": {
+ "version": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
+ "dev": true
+ },
+ "babel-code-frame": {
+ "version": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.22.0.tgz",
+ "integrity": "sha1-AnYgvuVnqIwyVhV05/0IAdMxGOQ=",
+ "dev": true
+ },
+ "babel-core": {
+ "version": "https://registry.npmjs.org/babel-core/-/babel-core-6.24.1.tgz",
+ "integrity": "sha1-jEKFZNzh4fQfszfsNPTDsCK1rYM=",
+ "dev": true
+ },
+ "babel-eslint": {
+ "version": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-7.2.3.tgz",
+ "integrity": "sha1-sv4tgBJkcPXBlELcdXJTqJdxCCc=",
+ "dev": true
+ },
+ "babel-generator": {
+ "version": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.24.1.tgz",
+ "integrity": "sha1-5xX0hsWN7SVknYiJRNUqoHxdlJc=",
+ "dev": true,
+ "dependencies": {
+ "jsesc": {
+ "version": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
+ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
+ "dev": true
+ }
+ }
+ },
+ "babel-helper-call-delegate": {
+ "version": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz",
+ "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=",
+ "dev": true
+ },
+ "babel-helper-define-map": {
+ "version": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.24.1.tgz",
+ "integrity": "sha1-epdH8ljYlH0y1RX2qhx70CIEoIA=",
+ "dev": true
+ },
+ "babel-helper-function-name": {
+ "version": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz",
+ "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=",
+ "dev": true
+ },
+ "babel-helper-get-function-arity": {
+ "version": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz",
+ "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=",
+ "dev": true
+ },
+ "babel-helper-hoist-variables": {
+ "version": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz",
+ "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=",
+ "dev": true
+ },
+ "babel-helper-optimise-call-expression": {
+ "version": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz",
+ "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=",
+ "dev": true
+ },
+ "babel-helper-regex": {
+ "version": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.24.1.tgz",
+ "integrity": "sha1-024i+rEAjXnYhkjjIRaGgShFbOg=",
+ "dev": true
+ },
+ "babel-helper-replace-supers": {
+ "version": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz",
+ "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=",
+ "dev": true
+ },
+ "babel-helpers": {
+ "version": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
+ "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
+ "dev": true
+ },
+ "babel-messages": {
+ "version": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "dev": true
+ },
+ "babel-plugin-check-es2015-constants": {
+ "version": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
+ "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-arrow-functions": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
+ "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-block-scoped-functions": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz",
+ "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-block-scoping": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.24.1.tgz",
+ "integrity": "sha1-dsKV3DpHQbFmWt/TFnIV3P8ypXY=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-classes": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz",
+ "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-computed-properties": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz",
+ "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-destructuring": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz",
+ "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-duplicate-keys": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz",
+ "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-for-of": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz",
+ "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-function-name": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz",
+ "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-literals": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz",
+ "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-modules-amd": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz",
+ "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-modules-commonjs": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.24.1.tgz",
+ "integrity": "sha1-0+MQtA72ZKNmIiAAl8bUQCmPK/4=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-modules-systemjs": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz",
+ "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-modules-umd": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz",
+ "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-object-super": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz",
+ "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-parameters": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz",
+ "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-shorthand-properties": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz",
+ "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-spread": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz",
+ "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-sticky-regex": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz",
+ "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-template-literals": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz",
+ "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-typeof-symbol": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz",
+ "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=",
+ "dev": true
+ },
+ "babel-plugin-transform-es2015-unicode-regex": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz",
+ "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=",
+ "dev": true
+ },
+ "babel-plugin-transform-regenerator": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.24.1.tgz",
+ "integrity": "sha1-uNowWtQ8PJm0hI5P5AN7dw0jxBg=",
+ "dev": true
+ },
+ "babel-plugin-transform-strict-mode": {
+ "version": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz",
+ "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=",
+ "dev": true
+ },
+ "babel-preset-es2015": {
+ "version": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz",
+ "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=",
+ "dev": true
+ },
+ "babel-register": {
+ "version": "https://registry.npmjs.org/babel-register/-/babel-register-6.24.1.tgz",
+ "integrity": "sha1-fhDhOi9xBlvfrVoXh7pFvKbe118=",
+ "dev": true
+ },
+ "babel-runtime": {
+ "version": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.23.0.tgz",
+ "integrity": "sha1-CpSJ8UTecO+zzkMArM2zKeL8VDs=",
+ "dev": true
+ },
+ "babel-template": {
+ "version": "https://registry.npmjs.org/babel-template/-/babel-template-6.24.1.tgz",
+ "integrity": "sha1-BK5RTx+Ts6JTfyoPYKWkX7gwgzM=",
+ "dev": true
+ },
+ "babel-traverse": {
+ "version": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.24.1.tgz",
+ "integrity": "sha1-qzZnP9NW+aCUhlnnszjV/q2zFpU=",
+ "dev": true
+ },
+ "babel-types": {
+ "version": "https://registry.npmjs.org/babel-types/-/babel-types-6.24.1.tgz",
+ "integrity": "sha1-oTaHncFbNga9oNkMH8dDBML/CXU=",
+ "dev": true
+ },
+ "babelify": {
+ "version": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz",
+ "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=",
+ "dev": true
+ },
+ "babylon": {
+ "version": "https://registry.npmjs.org/babylon/-/babylon-6.17.2.tgz",
+ "integrity": "sha1-IB0l71+JLEG65JSIsI2w3Udun1w=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
+ "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz",
+ "integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=",
+ "dev": true
+ },
+ "basscss": {
+ "version": "https://registry.npmjs.org/basscss/-/basscss-7.1.1.tgz",
+ "integrity": "sha1-e/MSAxl6Kd8O7lUfQlYXqL58d3s="
+ },
+ "basscss-align": {
+ "version": "https://registry.npmjs.org/basscss-align/-/basscss-align-1.0.2.tgz",
+ "integrity": "sha1-KUqmidb5nahuSvTFwokocIVcHDc="
+ },
+ "basscss-background-colors": {
+ "version": "https://registry.npmjs.org/basscss-background-colors/-/basscss-background-colors-1.1.3.tgz",
+ "integrity": "sha1-VKKDZRxAklZTJKTvW8JdcL52IdY="
+ },
+ "basscss-base-forms": {
+ "version": "https://registry.npmjs.org/basscss-base-forms/-/basscss-base-forms-2.0.2.tgz",
+ "integrity": "sha1-Fgi5n/SG3WuJEZLKjSXi7VdYWao="
+ },
+ "basscss-base-reset": {
+ "version": "https://registry.npmjs.org/basscss-base-reset/-/basscss-base-reset-2.0.3.tgz",
+ "integrity": "sha1-WScWF55JfgtOfJrdHxxMNAF8Vy0="
+ },
+ "basscss-base-tables": {
+ "version": "https://registry.npmjs.org/basscss-base-tables/-/basscss-base-tables-1.0.2.tgz",
+ "integrity": "sha1-uFDqHWSwb5GSK/z0AUuqoINquzA="
+ },
+ "basscss-base-typography": {
+ "version": "https://registry.npmjs.org/basscss-base-typography/-/basscss-base-typography-2.0.3.tgz",
+ "integrity": "sha1-H0vzRXEkgoII9oa8OFzTMK5+enI="
+ },
+ "basscss-border": {
+ "version": "https://registry.npmjs.org/basscss-border/-/basscss-border-3.0.4.tgz",
+ "integrity": "sha1-ZZk1aNoIZ+t12Wtwn5fjOyuQJIY="
+ },
+ "basscss-border-colors": {
+ "version": "https://registry.npmjs.org/basscss-border-colors/-/basscss-border-colors-1.1.3.tgz",
+ "integrity": "sha1-nrIya0eeqpe/m9bE7rwf2CTYCf4="
+ },
+ "basscss-borders": {
+ "version": "https://registry.npmjs.org/basscss-borders/-/basscss-borders-2.0.5.tgz",
+ "integrity": "sha1-PYRw+6kOzoknBeGlskwna3Oe644="
+ },
+ "basscss-btn": {
+ "version": "https://registry.npmjs.org/basscss-btn/-/basscss-btn-1.1.1.tgz",
+ "integrity": "sha1-xCFX8gG9lduaJRVoxULnQLKj178="
+ },
+ "basscss-btn-outline": {
+ "version": "https://registry.npmjs.org/basscss-btn-outline/-/basscss-btn-outline-1.1.0.tgz",
+ "integrity": "sha1-uEROqdPVCM0Adgqdb9/0uvXUJ1g="
+ },
+ "basscss-btn-primary": {
+ "version": "https://registry.npmjs.org/basscss-btn-primary/-/basscss-btn-primary-1.1.0.tgz",
+ "integrity": "sha1-DBJJKXHiFuQr3xNEEa/DgCw9i/c="
+ },
+ "basscss-color-base": {
+ "version": "https://registry.npmjs.org/basscss-color-base/-/basscss-color-base-2.0.2.tgz",
+ "integrity": "sha1-7YSL/OORq1NabRSnAqFPpMTzJC0="
+ },
+ "basscss-color-forms": {
+ "version": "https://registry.npmjs.org/basscss-color-forms/-/basscss-color-forms-3.0.2.tgz",
+ "integrity": "sha1-jy0dB9X8tmRVbNNUvvmM67LV4Ms="
+ },
+ "basscss-color-tables": {
+ "version": "https://registry.npmjs.org/basscss-color-tables/-/basscss-color-tables-1.0.4.tgz",
+ "integrity": "sha1-1DXsfF8hD6F959G4gT3rvKalQ+E="
+ },
+ "basscss-colors": {
+ "version": "https://registry.npmjs.org/basscss-colors/-/basscss-colors-2.2.0.tgz",
+ "integrity": "sha1-3Mt3Picu/kXfSkgJYsi9iLams+E=",
+ "dependencies": {
+ "colors.css": {
+ "version": "https://registry.npmjs.org/colors.css/-/colors.css-3.0.0.tgz",
+ "integrity": "sha1-URz0L7inGZqMvvSciKTqTx2Pnvw="
+ }
+ }
+ },
+ "basscss-defaults": {
+ "version": "https://registry.npmjs.org/basscss-defaults/-/basscss-defaults-2.1.3.tgz",
+ "integrity": "sha1-tOpjToFcaSPwx2ZbFIpMyLO0OSc="
+ },
+ "basscss-grid": {
+ "version": "https://registry.npmjs.org/basscss-grid/-/basscss-grid-1.0.6.tgz",
+ "integrity": "sha1-GlEsc7h0MwXkejanQyqtXCbMKGc="
+ },
+ "basscss-layout": {
+ "version": "https://registry.npmjs.org/basscss-layout/-/basscss-layout-3.1.0.tgz",
+ "integrity": "sha1-+fOS5IDaZmV9n+XenKTAfFecOk4="
+ },
+ "basscss-margin": {
+ "version": "https://registry.npmjs.org/basscss-margin/-/basscss-margin-1.0.7.tgz",
+ "integrity": "sha1-WpLYzamO85HHOhXt6Xs0tIiGQXw="
+ },
+ "basscss-padding": {
+ "version": "https://registry.npmjs.org/basscss-padding/-/basscss-padding-1.1.3.tgz",
+ "integrity": "sha1-adt5lBTm3Vi+2Dd2lSzCmeLmh04="
+ },
+ "basscss-position": {
+ "version": "https://registry.npmjs.org/basscss-position/-/basscss-position-2.0.3.tgz",
+ "integrity": "sha1-RnGAofjzhukHLtjQgpTSpuC6QwU="
+ },
+ "basscss-positions": {
+ "version": "https://registry.npmjs.org/basscss-positions/-/basscss-positions-1.0.5.tgz",
+ "integrity": "sha1-5P37bQMc8ljGERf5M3Hzq7uTiBo="
+ },
+ "basscss-responsive-states": {
+ "version": "https://registry.npmjs.org/basscss-responsive-states/-/basscss-responsive-states-1.0.6.tgz",
+ "integrity": "sha1-2JI0PheZiFwD5PHHAs18GrUo8AI="
+ },
+ "basscss-sass": {
+ "version": "https://registry.npmjs.org/basscss-sass/-/basscss-sass-3.0.0.tgz",
+ "integrity": "sha1-nxvoX6jqafmUQVN2ImjEavMLxM0="
+ },
+ "basscss-type-scale": {
+ "version": "https://registry.npmjs.org/basscss-type-scale/-/basscss-type-scale-1.0.5.tgz",
+ "integrity": "sha1-I79eQcnRQsgGHPmCnM8j6bMljsc="
+ },
+ "basscss-typography": {
+ "version": "https://registry.npmjs.org/basscss-typography/-/basscss-typography-3.0.3.tgz",
+ "integrity": "sha1-GCz0PffE6+0CdQ3HSAQcut/2DUM="
+ },
+ "bn.js": {
+ "version": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz",
+ "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
+ "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=",
+ "dev": true
+ },
+ "brorand": {
+ "version": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browser-pack": {
+ "version": "https://registry.npmjs.org/browser-pack/-/browser-pack-6.0.2.tgz",
+ "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=",
+ "dev": true
+ },
+ "browser-resolve": {
+ "version": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz",
+ "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=",
+ "dev": true,
+ "dependencies": {
+ "resolve": {
+ "version": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
+ "dev": true
+ }
+ }
+ },
+ "browserify": {
+ "version": "https://registry.npmjs.org/browserify/-/browserify-13.3.0.tgz",
+ "integrity": "sha1-tanJAgJD8McORnW+yCI7xifkFc4=",
+ "dev": true
+ },
+ "browserify-aes": {
+ "version": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz",
+ "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=",
+ "dev": true
+ },
+ "browserify-cache-api": {
+ "version": "https://registry.npmjs.org/browserify-cache-api/-/browserify-cache-api-3.0.1.tgz",
+ "integrity": "sha1-liR+hT8Gj9bg1FzHPwuyzZd47wI=",
+ "dev": true
+ },
+ "browserify-cipher": {
+ "version": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.0.tgz",
+ "integrity": "sha1-mYgkSHS/XtTijalWZtzWasj8Njo=",
+ "dev": true
+ },
+ "browserify-des": {
+ "version": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.0.tgz",
+ "integrity": "sha1-2qJ3cXRwki7S/hhZQRihdUOXId0=",
+ "dev": true
+ },
+ "browserify-incremental": {
+ "version": "https://registry.npmjs.org/browserify-incremental/-/browserify-incremental-3.1.1.tgz",
+ "integrity": "sha1-BxPLdYckemMqnwjPG9FpuHi2Koo=",
+ "dev": true,
+ "dependencies": {
+ "jsonparse": {
+ "version": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz",
+ "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz",
+ "integrity": "sha1-dDSdDYlSK3HzDwoD/5vSDKbxKsA=",
+ "dev": true
+ }
+ }
+ },
+ "browserify-rsa": {
+ "version": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true
+ },
+ "browserify-sign": {
+ "version": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
+ "dev": true
+ },
+ "browserify-zlib": {
+ "version": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz",
+ "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=",
+ "dev": true
+ },
+ "buffer": {
+ "version": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
+ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "cached-path-relative": {
+ "version": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
+ "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc=",
+ "dev": true
+ },
+ "caller-path": {
+ "version": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz",
+ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=",
+ "dev": true
+ },
+ "callsites": {
+ "version": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz",
+ "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=",
+ "dev": true
+ },
+ "chai": {
+ "version": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true
+ },
+ "cipher-base": {
+ "version": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.3.tgz",
+ "integrity": "sha1-7qvxlEGc6QDaMBjCB9IS8qbfCgc=",
+ "dev": true
+ },
+ "circular-json": {
+ "version": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz",
+ "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=",
+ "dev": true
+ },
+ "classlist.js": {
+ "version": "https://registry.npmjs.org/classlist.js/-/classlist.js-1.1.20150312.tgz",
+ "integrity": "sha1-HXCEL3Ai8I2awIbOaeWyUPLFd4k="
+ },
+ "cli-cursor": {
+ "version": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz",
+ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=",
+ "dev": true
+ },
+ "cli-width": {
+ "version": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz",
+ "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=",
+ "dev": true
+ },
+ "clipboard": {
+ "version": "https://registry.npmjs.org/clipboard/-/clipboard-1.7.1.tgz",
+ "integrity": "sha1-Ng1taUbpmnof7zleQrqStem1oWs="
+ },
+ "co": {
+ "version": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
+ "dev": true
+ },
+ "code-point-at": {
+ "version": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
+ "dev": true
+ },
+ "colors.css": {
+ "version": "https://registry.npmjs.org/colors.css/-/colors.css-2.3.0.tgz",
+ "integrity": "sha1-6JU4N1Q+GdmOKRf/C5mPbbKGITs="
+ },
+ "combine-source-map": {
+ "version": "https://registry.npmjs.org/combine-source-map/-/combine-source-map-0.7.2.tgz",
+ "integrity": "sha1-CHAxKFazB6h8xKxIbzqaYq7MwJ4=",
+ "dev": true,
+ "dependencies": {
+ "convert-source-map": {
+ "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.1.3.tgz",
+ "integrity": "sha1-SCnId+n+SbMWHzvzZziI4gRpmGA=",
+ "dev": true
+ }
+ }
+ },
+ "concat-map": {
+ "version": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "concat-stream": {
+ "version": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz",
+ "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=",
+ "dev": true,
+ "dependencies": {
+ "readable-stream": {
+ "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+ "dev": true
+ }
+ }
+ },
+ "console-browserify": {
+ "version": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
+ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
+ "dev": true
+ },
+ "constants-browserify": {
+ "version": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "contains-path": {
+ "version": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
+ "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz",
+ "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=",
+ "dev": true
+ },
+ "core-js": {
+ "version": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
+ "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=",
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "create-ecdh": {
+ "version": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.0.tgz",
+ "integrity": "sha1-iIxyNZbN92EvZJgjPuvXo1MBc30=",
+ "dev": true
+ },
+ "create-hash": {
+ "version": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
+ "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
+ "dev": true
+ },
+ "create-hmac": {
+ "version": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
+ "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
+ "dev": true
+ },
+ "crypto-browserify": {
+ "version": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.11.0.tgz",
+ "integrity": "sha1-NlKgkGq5sqfgw85mpAjpV6JIVSI=",
+ "dev": true
+ },
+ "d": {
+ "version": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
+ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
+ "dev": true
+ },
+ "date-now": {
+ "version": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+ "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+ "dev": true
+ },
+ "debug": {
+ "version": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
+ "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "dev": true,
+ "dependencies": {
+ "type-detect": {
+ "version": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true
+ }
+ }
+ },
+ "deep-is": {
+ "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "defined": {
+ "version": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
+ "dev": true
+ },
+ "del": {
+ "version": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true
+ },
+ "delegate": {
+ "version": "https://registry.npmjs.org/delegate/-/delegate-3.1.3.tgz",
+ "integrity": "sha1-moJRp3fXAl+qVXN7w7BxdCEnqf0="
+ },
+ "deps-sort": {
+ "version": "https://registry.npmjs.org/deps-sort/-/deps-sort-2.0.0.tgz",
+ "integrity": "sha1-CRckkC6EZYJg65EHSMzNGvbiH7U=",
+ "dev": true
+ },
+ "des.js": {
+ "version": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
+ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+ "dev": true
+ },
+ "detect-indent": {
+ "version": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
+ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
+ "dev": true
+ },
+ "detective": {
+ "version": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz",
+ "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=",
+ "dev": true
+ },
+ "diffie-hellman": {
+ "version": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.2.tgz",
+ "integrity": "sha1-tYNXOScM/ias9jIJn97SoH8gnl4=",
+ "dev": true
+ },
+ "dirty-chai": {
+ "version": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-1.2.2.tgz",
+ "integrity": "sha1-eEleYZY19/5EIZqkyDeEm/GDFC4=",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz",
+ "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=",
+ "dev": true
+ },
+ "domain-browser": {
+ "version": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz",
+ "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=",
+ "dev": true
+ },
+ "duplexer2": {
+ "version": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "dev": true
+ },
+ "elliptic": {
+ "version": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz",
+ "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=",
+ "dev": true
+ },
+ "error-ex": {
+ "version": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "dev": true
+ },
+ "es5-ext": {
+ "version": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.22.tgz",
+ "integrity": "sha1-GHbFH5kHacESx4HqPr6J+E/TkHE=",
+ "dev": true
+ },
+ "es6-iterator": {
+ "version": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz",
+ "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=",
+ "dev": true
+ },
+ "es6-map": {
+ "version": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz",
+ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=",
+ "dev": true
+ },
+ "es6-set": {
+ "version": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz",
+ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=",
+ "dev": true
+ },
+ "es6-symbol": {
+ "version": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz",
+ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=",
+ "dev": true
+ },
+ "es6-weak-map": {
+ "version": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz",
+ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
+ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
+ "dev": true,
+ "dependencies": {
+ "esprima": {
+ "version": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
+ "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+ "dev": true
+ },
+ "estraverse": {
+ "version": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
+ "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
+ "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "escope": {
+ "version": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz",
+ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz",
+ "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=",
+ "dev": true,
+ "dependencies": {
+ "json-stable-stringify": {
+ "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz",
+ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-config-airbnb-base": {
+ "version": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-11.2.0.tgz",
+ "integrity": "sha1-GancRIGib3CQRUXsBAEWh2AY+FM=",
+ "dev": true
+ },
+ "eslint-import-resolver-node": {
+ "version": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.2.3.tgz",
+ "integrity": "sha1-Wt2BBujJKNssuiMrzZ76hG49oWw=",
+ "dev": true
+ },
+ "eslint-module-utils": {
+ "version": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.0.0.tgz",
+ "integrity": "sha1-pvjCHZATWHWc3DXbrBmCrh7li84=",
+ "dev": true,
+ "dependencies": {
+ "debug": {
+ "version": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+ "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
+ "dev": true
+ },
+ "ms": {
+ "version": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
+ "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
+ "dev": true
+ }
+ }
+ },
+ "eslint-plugin-import": {
+ "version": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.3.0.tgz",
+ "integrity": "sha1-N8gB4K2g4pbL3yDD85OstbUq82s=",
+ "dev": true,
+ "dependencies": {
+ "doctrine": {
+ "version": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
+ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
+ "dev": true
+ }
+ }
+ },
+ "espree": {
+ "version": "https://registry.npmjs.org/espree/-/espree-3.4.3.tgz",
+ "integrity": "sha1-KRC1zNSc6JPC//+qtP2LOjG4I3Q=",
+ "dev": true,
+ "dependencies": {
+ "acorn": {
+ "version": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
+ "integrity": "sha1-xGDfCEkUY/AozLguqzcwvwEIez0=",
+ "dev": true
+ }
+ }
+ },
+ "esprima": {
+ "version": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
+ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=",
+ "dev": true
+ },
+ "esquery": {
+ "version": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz",
+ "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=",
+ "dev": true
+ },
+ "esrecurse": {
+ "version": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.1.0.tgz",
+ "integrity": "sha1-RxO2U2rffyrE8yfVWed1a/9kgiA=",
+ "dev": true,
+ "dependencies": {
+ "estraverse": {
+ "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.1.1.tgz",
+ "integrity": "sha1-9srKcokzqFDvkGYdDheYK6RxEaI=",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "event-emitter": {
+ "version": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
+ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=",
+ "dev": true
+ },
+ "events": {
+ "version": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
+ "dev": true
+ },
+ "evp_bytestokey": {
+ "version": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz",
+ "integrity": "sha1-SXtmrZ/vZc18CKYYCCS6FHa2blM=",
+ "dev": true
+ },
+ "exit-hook": {
+ "version": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
+ "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "field-kit": {
+ "version": "https://registry.npmjs.org/field-kit/-/field-kit-2.1.0.tgz",
+ "integrity": "sha1-5o7eX04wUbLcQlgQWklcVBo7LX8="
+ },
+ "figures": {
+ "version": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz",
+ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=",
+ "dev": true
+ },
+ "file-entry-cache": {
+ "version": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true
+ },
+ "fill-keys": {
+ "version": "https://registry.npmjs.org/fill-keys/-/fill-keys-1.0.2.tgz",
+ "integrity": "sha1-mo+jb06K1jTjv2tPPIiCVRRS6yA=",
+ "dev": true
+ },
+ "find-up": {
+ "version": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "dev": true
+ },
+ "flat-cache": {
+ "version": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz",
+ "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=",
+ "dev": true
+ },
+ "flex-object": {
+ "version": "https://registry.npmjs.org/flex-object/-/flex-object-2.0.5.tgz",
+ "integrity": "sha1-Ebm7wPT4ZOncYH6YzixnBEK9yFE="
+ },
+ "focus-trap": {
+ "version": "https://registry.npmjs.org/focus-trap/-/focus-trap-2.3.0.tgz",
+ "integrity": "sha1-B8kZZIZ9NGMV9PX434i/lkVTFuI="
+ },
+ "formatio": {
+ "version": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz",
+ "integrity": "sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek="
+ },
+ "fs.realpath": {
+ "version": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "function-bind": {
+ "version": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz",
+ "integrity": "sha1-FhdnFMgBeY5Ojyz391KUZ7tKV3E=",
+ "dev": true
+ },
+ "generate-function": {
+ "version": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz",
+ "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=",
+ "dev": true
+ },
+ "generate-object-property": {
+ "version": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz",
+ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=",
+ "dev": true
+ },
+ "glob": {
+ "version": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha1-wZyd+aAocC1nhhI4SmVSQExjbRU=",
+ "dev": true
+ },
+ "globals": {
+ "version": "https://registry.npmjs.org/globals/-/globals-9.17.0.tgz",
+ "integrity": "sha1-DAymltm5u2lNLlRwvTd3fKrVAoY=",
+ "dev": true
+ },
+ "globby": {
+ "version": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true
+ },
+ "good-listener": {
+ "version": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA="
+ },
+ "graceful-fs": {
+ "version": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
+ "dev": true
+ },
+ "has": {
+ "version": "https://registry.npmjs.org/has/-/has-1.0.1.tgz",
+ "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=",
+ "dev": true
+ },
+ "has-ansi": {
+ "version": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true
+ },
+ "has-require": {
+ "version": "https://registry.npmjs.org/has-require/-/has-require-1.2.2.tgz",
+ "integrity": "sha1-khZ1qxMNvZdo/I2o8ajiQt+kF3Q=",
+ "dev": true
+ },
+ "hash-base": {
+ "version": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz",
+ "integrity": "sha1-ZuodhW206KVHDK32/OI65SRO8uE=",
+ "dev": true
+ },
+ "hash.js": {
+ "version": "https://registry.npmjs.org/hash.js/-/hash.js-1.0.3.tgz",
+ "integrity": "sha1-EzL/ABVsCg/92CNgE9B7d6BFFXM=",
+ "dev": true
+ },
+ "hint.css": {
+ "version": "https://registry.npmjs.org/hint.css/-/hint.css-2.5.0.tgz",
+ "integrity": "sha1-OMrjZn5C2R392+UDEAqzSTL2/WU="
+ },
+ "hmac-drbg": {
+ "version": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true
+ },
+ "home-or-tmp": {
+ "version": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
+ "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
+ "dev": true
+ },
+ "hosted-git-info": {
+ "version": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.4.2.tgz",
+ "integrity": "sha1-AHa59GonBQbduq6lZJaJdGBhKmc=",
+ "dev": true
+ },
+ "htmlescape": {
+ "version": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
+ "integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
+ "dev": true
+ },
+ "https-browserify": {
+ "version": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz",
+ "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=",
+ "dev": true
+ },
+ "ieee754": {
+ "version": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz",
+ "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz",
+ "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=",
+ "dev": true
+ },
+ "imurmurhash": {
+ "version": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "indexof": {
+ "version": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true
+ },
+ "inherits": {
+ "version": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE="
+ },
+ "inline-source-map": {
+ "version": "https://registry.npmjs.org/inline-source-map/-/inline-source-map-0.6.2.tgz",
+ "integrity": "sha1-+Tk0ccGKedFyT4Y/o4tYY3Ct4qU=",
+ "dev": true
+ },
+ "input-sim": {
+ "version": "https://registry.npmjs.org/input-sim/-/input-sim-3.1.0.tgz",
+ "integrity": "sha1-g/nCFPTW2MjpCU00V5eVkeqIzCw="
+ },
+ "inquirer": {
+ "version": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz",
+ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=",
+ "dev": true
+ },
+ "insert-module-globals": {
+ "version": "https://registry.npmjs.org/insert-module-globals/-/insert-module-globals-7.0.1.tgz",
+ "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=",
+ "dev": true
+ },
+ "interpret": {
+ "version": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz",
+ "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=",
+ "dev": true
+ },
+ "invariant": {
+ "version": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
+ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-buffer": {
+ "version": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
+ "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=",
+ "dev": true
+ },
+ "is-builtin-module": {
+ "version": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "dev": true
+ },
+ "is-finite": {
+ "version": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "dev": true
+ },
+ "is-my-json-valid": {
+ "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz",
+ "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=",
+ "dev": true
+ },
+ "is-object": {
+ "version": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz",
+ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz",
+ "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=",
+ "dev": true
+ },
+ "is-path-inside": {
+ "version": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz",
+ "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=",
+ "dev": true
+ },
+ "is-property": {
+ "version": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
+ "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=",
+ "dev": true
+ },
+ "is-resolvable": {
+ "version": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz",
+ "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz",
+ "integrity": "sha1-COnxMkhKLEWjCQfp3E1VZ7fxFNc=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.8.4.tgz",
+ "integrity": "sha1-UgtFZPhlc7qWZir4Woyvp7S1pvY=",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ },
+ "json-stable-stringify": {
+ "version": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-0.0.1.tgz",
+ "integrity": "sha1-YRwj6BTbN1Un34URk9tZ3Sryf0U=",
+ "dev": true
+ },
+ "json5": {
+ "version": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+ "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
+ "dev": true
+ },
+ "jsonify": {
+ "version": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
+ "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
+ "dev": true
+ },
+ "jsonparse": {
+ "version": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "jsonpointer": {
+ "version": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz",
+ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
+ "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
+ "dev": true
+ },
+ "labeled-stream-splicer": {
+ "version": "https://registry.npmjs.org/labeled-stream-splicer/-/labeled-stream-splicer-2.0.0.tgz",
+ "integrity": "sha1-pS4dE4AkwAuGscDJH2d5GLiuClk=",
+ "dev": true,
+ "dependencies": {
+ "isarray": {
+ "version": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ }
+ }
+ },
+ "levn": {
+ "version": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true
+ },
+ "lexical-scope": {
+ "version": "https://registry.npmjs.org/lexical-scope/-/lexical-scope-1.2.0.tgz",
+ "integrity": "sha1-/Ope3HBKSzqHls3KQZw6CvryLfQ=",
+ "dev": true
+ },
+ "load-json-file": {
+ "version": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
+ "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "dependencies": {
+ "path-exists": {
+ "version": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ }
+ }
+ },
+ "lodash": {
+ "version": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=",
+ "dev": true
+ },
+ "lodash.cond": {
+ "version": "https://registry.npmjs.org/lodash.cond/-/lodash.cond-4.5.2.tgz",
+ "integrity": "sha1-9HGh2khr5g9quVXRcRVSPdHSVdU=",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz",
+ "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=",
+ "dev": true
+ },
+ "lolex": {
+ "version": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz",
+ "integrity": "sha1-fD2mL/yzDw9agKJWbKJORdigHzE="
+ },
+ "loose-envify": {
+ "version": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "dev": true
+ },
+ "merge-descriptors": {
+ "version": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "miller-rabin": {
+ "version": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.0.tgz",
+ "integrity": "sha1-SmL7HUKTPAVYOYL0xxb2+55sbT0=",
+ "dev": true
+ },
+ "minimalistic-assert": {
+ "version": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz",
+ "integrity": "sha1-cCvi3aazf0g2vLP121ZkG2Sh09M=",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=",
+ "dev": true
+ },
+ "minimist": {
+ "version": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true
+ },
+ "module-deps": {
+ "version": "https://registry.npmjs.org/module-deps/-/module-deps-4.1.1.tgz",
+ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=",
+ "dev": true
+ },
+ "module-not-found-error": {
+ "version": "https://registry.npmjs.org/module-not-found-error/-/module-not-found-error-1.0.1.tgz",
+ "integrity": "sha1-z4tP9PKWQGdNbN0CsOO8UjwrvcA=",
+ "dev": true
+ },
+ "ms": {
+ "version": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz",
+ "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.3.8.tgz",
+ "integrity": "sha1-2Bntoqne29H/pWPqQHHZNngilbs=",
+ "dev": true
+ },
+ "normalize.css": {
+ "version": "https://registry.npmjs.org/normalize.css/-/normalize.css-4.2.0.tgz",
+ "integrity": "sha1-IdZsxVcVTUN5/R4HnsfeWKN5sJk="
+ },
+ "number-is-nan": {
+ "version": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "once": {
+ "version": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true
+ },
+ "onetime": {
+ "version": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz",
+ "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=",
+ "dev": true
+ },
+ "optionator": {
+ "version": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true
+ },
+ "os-browserify": {
+ "version": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.1.2.tgz",
+ "integrity": "sha1-ScoCk+CxlZCl9d4Qx/JlphfY/lQ=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-tmpdir": {
+ "version": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz",
+ "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=",
+ "dev": true
+ },
+ "p-locate": {
+ "version": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true
+ },
+ "pako": {
+ "version": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz",
+ "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=",
+ "dev": true
+ },
+ "parents": {
+ "version": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+ "dev": true
+ },
+ "parse-asn1": {
+ "version": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.0.tgz",
+ "integrity": "sha1-N8T5t+06tlx0gXtfJICTf7+XxxI=",
+ "dev": true
+ },
+ "parse-json": {
+ "version": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
+ "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
+ "dev": true
+ },
+ "path-platform": {
+ "version": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
+ "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
+ "dev": true
+ },
+ "pbkdf2": {
+ "version": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.12.tgz",
+ "integrity": "sha1-vjZ4XFBn6kjYBv+SMojF91C2uKI=",
+ "dev": true
+ },
+ "pff": {
+ "version": "https://registry.npmjs.org/pff/-/pff-1.0.0.tgz",
+ "integrity": "sha1-6l8J7mVxyuKSp4/CgJBaOGVmjng=",
+ "dev": true
+ },
+ "pify": {
+ "version": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz",
+ "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=",
+ "dev": true
+ },
+ "pluralize": {
+ "version": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz",
+ "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=",
+ "dev": true
+ },
+ "prelude-ls": {
+ "version": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "private": {
+ "version": "https://registry.npmjs.org/private/-/private-0.1.7.tgz",
+ "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=",
+ "dev": true
+ },
+ "process": {
+ "version": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+ "dev": true
+ },
+ "progress": {
+ "version": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
+ "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
+ "dev": true
+ },
+ "proxyquireify": {
+ "version": "https://registry.npmjs.org/proxyquireify/-/proxyquireify-3.2.1.tgz",
+ "integrity": "sha1-Fb7hATYKzJHc2G7k2aRF+Klx7qA=",
+ "dev": true,
+ "dependencies": {
+ "acorn": {
+ "version": "https://registry.npmjs.org/acorn/-/acorn-1.2.2.tgz",
+ "integrity": "sha1-yM4n3grMdtiW0rH6099YjZ6C8BQ=",
+ "dev": true
+ },
+ "detective": {
+ "version": "https://registry.npmjs.org/detective/-/detective-4.1.1.tgz",
+ "integrity": "sha1-nEusHp+4uzT38YyuCA6h0Dr/LNo=",
+ "dev": true
+ },
+ "through": {
+ "version": "https://registry.npmjs.org/through/-/through-2.2.7.tgz",
+ "integrity": "sha1-bo4hIAGR1OtqmfbwEN9Gqhxusr0=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "https://registry.npmjs.org/xtend/-/xtend-3.0.0.tgz",
+ "integrity": "sha1-XM50B7r2Qsunvs2laBEcST9ZZlo=",
+ "dev": true
+ }
+ }
+ },
+ "public-encrypt": {
+ "version": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.0.tgz",
+ "integrity": "sha1-OfaZ86RlYN1eusvKaTyvfGXBjMY=",
+ "dev": true
+ },
+ "punycode": {
+ "version": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ },
+ "querystring": {
+ "version": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.4.tgz",
+ "integrity": "sha1-lVHfIIQiyPgOtY4jJt0LhA/yLv0=",
+ "dev": true
+ },
+ "read-only-stream": {
+ "version": "https://registry.npmjs.org/read-only-stream/-/read-only-stream-2.0.0.tgz",
+ "integrity": "sha1-JyT9aoET1zdkrCiNQ4YnDB2/F/A=",
+ "dev": true
+ },
+ "read-pkg": {
+ "version": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
+ "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
+ "dev": true
+ },
+ "read-pkg-up": {
+ "version": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
+ "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
+ "dev": true,
+ "dependencies": {
+ "find-up": {
+ "version": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.10.tgz",
+ "integrity": "sha1-7/5yu3yITA3TNeI3nVJhltnQEe4=",
+ "dev": true,
+ "dependencies": {
+ "string_decoder": {
+ "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz",
+ "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=",
+ "dev": true
+ }
+ }
+ },
+ "readline2": {
+ "version": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz",
+ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=",
+ "dev": true
+ },
+ "rechoir": {
+ "version": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+ "dev": true
+ },
+ "regenerate": {
+ "version": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.2.tgz",
+ "integrity": "sha1-0ZQcZ7rUN+G+dkM63Vs4X5WxkmA=",
+ "dev": true
+ },
+ "regenerator-runtime": {
+ "version": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
+ "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.9.11.tgz",
+ "integrity": "sha1-On0GdSDLe3F2dp61/4aGkb7+EoM=",
+ "dev": true
+ },
+ "regexpu-core": {
+ "version": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz",
+ "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=",
+ "dev": true
+ },
+ "regjsgen": {
+ "version": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+ "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz",
+ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=",
+ "dev": true
+ },
+ "repeating": {
+ "version": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "dev": true
+ },
+ "require-deps": {
+ "version": "https://registry.npmjs.org/require-deps/-/require-deps-1.0.1.tgz",
+ "integrity": "sha1-JBXPScNb02pdMXc5UQjT8jcgUmM=",
+ "dev": true
+ },
+ "require-uncached": {
+ "version": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz",
+ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz",
+ "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
+ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz",
+ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.1.tgz",
+ "integrity": "sha1-wjOOxkPfeht/5cVPqG9XQopV8z0=",
+ "dev": true
+ },
+ "ripemd160": {
+ "version": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.1.tgz",
+ "integrity": "sha1-D0WEKVxTo2KK9+bXmsohzlfRxuc=",
+ "dev": true
+ },
+ "run-async": {
+ "version": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz",
+ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=",
+ "dev": true
+ },
+ "rx-lite": {
+ "version": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz",
+ "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
+ "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=",
+ "dev": true
+ },
+ "samsam": {
+ "version": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz",
+ "integrity": "sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc="
+ },
+ "select": {
+ "version": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+ },
+ "semver": {
+ "version": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
+ "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.8.tgz",
+ "integrity": "sha1-NwaMLEdra69ALRSknGf1l5IfY08=",
+ "dev": true
+ },
+ "shasum": {
+ "version": "https://registry.npmjs.org/shasum/-/shasum-1.0.2.tgz",
+ "integrity": "sha1-5wEjENj0F/TetXEhUOVni4euVl8=",
+ "dev": true
+ },
+ "shell-quote": {
+ "version": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
+ "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
+ "dev": true
+ },
+ "shelljs": {
+ "version": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.7.tgz",
+ "integrity": "sha1-svXHfvlxSPS09uImguELuoZnz/E=",
+ "dev": true
+ },
+ "sinon": {
+ "version": "https://registry.npmjs.org/sinon/-/sinon-1.17.7.tgz",
+ "integrity": "sha1-RUKk9JugxFwF6y6d2dID4rjv4L8="
+ },
+ "slash": {
+ "version": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+ "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
+ "dev": true
+ },
+ "slice-ansi": {
+ "version": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz",
+ "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
+ "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz",
+ "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+ "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw=",
+ "dev": true
+ },
+ "spdx-license-ids": {
+ "version": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc=",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "stream-browserify": {
+ "version": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz",
+ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=",
+ "dev": true
+ },
+ "stream-combiner2": {
+ "version": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+ "dev": true
+ },
+ "stream-http": {
+ "version": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.1.tgz",
+ "integrity": "sha1-VGpRdBrVprB+njGwsQRBqRffUoo=",
+ "dev": true
+ },
+ "stream-splicer": {
+ "version": "https://registry.npmjs.org/stream-splicer/-/stream-splicer-2.0.0.tgz",
+ "integrity": "sha1-G2O+Q4oTPktnHMGTUZdgAXWRDYM=",
+ "dev": true
+ },
+ "string_decoder": {
+ "version": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true
+ },
+ "strip-bom": {
+ "version": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+ "dev": true
+ },
+ "strip-json-comments": {
+ "version": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "stround": {
+ "version": "https://registry.npmjs.org/stround/-/stround-0.3.1.tgz",
+ "integrity": "sha1-vl8uHdf1tqFGhoJUish0YbjWZFQ="
+ },
+ "subarg": {
+ "version": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+ "dev": true,
+ "dependencies": {
+ "minimist": {
+ "version": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+ "dev": true
+ }
+ }
+ },
+ "supports-color": {
+ "version": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "syntax-error": {
+ "version": "https://registry.npmjs.org/syntax-error/-/syntax-error-1.3.0.tgz",
+ "integrity": "sha1-HtkmbE1AvnXcVb+bsct3Biu5bKE=",
+ "dev": true
+ },
+ "tabbable": {
+ "version": "https://registry.npmjs.org/tabbable/-/tabbable-1.0.6.tgz",
+ "integrity": "sha1-fCaofqb0ol7fXtthl0WgrnQHJPw="
+ },
+ "table": {
+ "version": "https://registry.npmjs.org/table/-/table-3.8.3.tgz",
+ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=",
+ "dev": true,
+ "dependencies": {
+ "is-fullwidth-code-point": {
+ "version": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "https://registry.npmjs.org/string-width/-/string-width-2.0.0.tgz",
+ "integrity": "sha1-Y1xUNsxypuDDh87KJ41OLuxSaH4=",
+ "dev": true
+ }
+ }
+ },
+ "text-table": {
+ "version": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+ "dev": true
+ },
+ "timers-browserify": {
+ "version": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-1.4.2.tgz",
+ "integrity": "sha1-ycWLV1voQHN1y14kYtrO50NZ9B0=",
+ "dev": true
+ },
+ "tiny-emitter": {
+ "version": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.0.0.tgz",
+ "integrity": "sha1-utMnrbGAS0KiMa+nQVMr2ITNCa0="
+ },
+ "to-arraybuffer": {
+ "version": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
+ "dev": true
+ },
+ "trim-right": {
+ "version": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
+ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
+ "dev": true
+ },
+ "tryit": {
+ "version": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz",
+ "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=",
+ "dev": true
+ },
+ "tty-browserify": {
+ "version": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+ "dev": true
+ },
+ "type-check": {
+ "version": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true
+ },
+ "type-detect": {
+ "version": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "dev": true
+ },
+ "typedarray": {
+ "version": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "umd": {
+ "version": "https://registry.npmjs.org/umd/-/umd-3.0.1.tgz",
+ "integrity": "sha1-iuVW4RAR9jwllnCKiDclnwGz1g4=",
+ "dev": true
+ },
+ "url": {
+ "version": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "dev": true,
+ "dependencies": {
+ "punycode": {
+ "version": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "user-home": {
+ "version": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz",
+ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=",
+ "dev": true
+ },
+ "util": {
+ "version": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk="
+ },
+ "util-deprecate": {
+ "version": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "validate-npm-package-license": {
+ "version": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+ "dev": true
+ },
+ "vm-browserify": {
+ "version": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
+ "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
+ "dev": true
+ },
+ "wordwrap": {
+ "version": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+ "dev": true
+ },
+ "zxcvbn": {
+ "version": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
+ "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA="
+ }
+ }
+}
diff --git a/spec/controllers/sign_out_controller_spec.rb b/spec/controllers/sign_out_controller_spec.rb
new file mode 100644
index 00000000000..a080e73cf6c
--- /dev/null
+++ b/spec/controllers/sign_out_controller_spec.rb
@@ -0,0 +1,22 @@
+require 'rails_helper'
+
+describe SignOutController do
+ describe '#destroy' do
+ it 'redirects to decorated_session.cancel_link_path with flash message' do
+ stub_sign_in_before_2fa
+ allow(controller.decorated_session).to receive(:cancel_link_path).and_return('foo')
+
+ get :destroy
+
+ expect(response).to redirect_to 'foo'
+ expect(flash[:success]).to eq t('devise.sessions.signed_out')
+ end
+
+ it 'calls #sign_out and #delete_branded_experience' do
+ expect(controller).to receive(:sign_out).and_call_original
+ expect(controller).to receive(:delete_branded_experience)
+
+ get :destroy
+ end
+ end
+end
diff --git a/spec/controllers/users/verify_password_controller_spec.rb b/spec/controllers/users/verify_password_controller_spec.rb
index 540ce2967a7..fba5173e971 100644
--- a/spec/controllers/users/verify_password_controller_spec.rb
+++ b/spec/controllers/users/verify_password_controller_spec.rb
@@ -20,71 +20,72 @@
end
end
- context 'without personal key flag set' do
- let(:profiles) { [create(:profile, deactivation_reason: :password_reset)] }
-
- describe '#new' do
- it 'redirects to the root url' do
- get :new
- expect(response).to redirect_to(root_url)
- end
- end
-
- describe '#update' do
- it 'redirects to the root url' do
- get :new
- expect(response).to redirect_to(root_url)
- end
- end
- end
-
context 'with password reset profile' do
let(:profiles) { [create(:profile, deactivation_reason: :password_reset)] }
let(:response_ok) { FormResponse.new(success: true, errors: {}, extra: { personal_key: key }) }
let(:response_bad) { FormResponse.new(success: false, errors: {}) }
let(:key) { 'key' }
- before do
- allow(subject.reactivate_account_session).to receive(:personal_key?).and_return(personal_key)
- end
-
- describe '#new' do
- it 'renders the `new` template' do
- get :new
+ context 'without personal key flag set' do
+ describe '#new' do
+ it 'redirects to the root url' do
+ get :new
+ expect(response).to redirect_to(root_url)
+ end
+ end
- expect(response).to render_template(:new)
+ describe '#update' do
+ it 'redirects to the root url' do
+ get :new
+ expect(response).to redirect_to(root_url)
+ end
end
end
- describe '#update' do
- let(:form) { instance_double(VerifyPasswordForm) }
-
+ context 'with personal key flag set' do
before do
- expect(controller).to receive(:verify_password_form).and_return(form)
+ allow(subject.reactivate_account_session).to receive(:personal_key?).
+ and_return(personal_key)
end
- context 'with valid password' do
- before do
- allow(form).to receive(:submit).and_return(response_ok)
- put :update, user: { password: user.password }
+ describe '#new' do
+ it 'renders the `new` template' do
+ get :new
+
+ expect(response).to render_template(:new)
end
+ end
- it 'redirects to the account page' do
- expect(response).to redirect_to(account_url)
+ describe '#update' do
+ let(:form) { instance_double(VerifyPasswordForm) }
+
+ before do
+ expect(controller).to receive(:verify_password_form).and_return(form)
end
- it 'sets a new personal key as a flash message' do
- expect(flash[:personal_key]).to eq(key)
+ context 'with valid password' do
+ before do
+ allow(form).to receive(:submit).and_return(response_ok)
+ put :update, user: { password: user.password }
+ end
+
+ it 'redirects to the account page' do
+ expect(response).to redirect_to(account_url)
+ end
+
+ it 'sets a new personal key as a flash message' do
+ expect(flash[:personal_key]).to eq(key)
+ end
end
- end
- context 'without valid password' do
- it 'renders the new template' do
- allow(form).to receive(:submit).and_return(response_bad)
+ context 'without valid password' do
+ it 'renders the new template' do
+ allow(form).to receive(:submit).and_return(response_bad)
- put :update, user: { password: user.password }
+ put :update, user: { password: user.password }
- expect(response).to render_template(:new)
+ expect(response).to render_template(:new)
+ 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 0e16e56d7ca..d8d7698bfa3 100644
--- a/spec/controllers/users/verify_personal_key_controller_spec.rb
+++ b/spec/controllers/users/verify_personal_key_controller_spec.rb
@@ -33,7 +33,7 @@
it 'displays a flash message to the user' do
get :new
- expect(subject.flash[:notice]).to eq(t('notices.account_recovery'))
+ expect(subject.flash[:notice]).to eq(t('notices.account_reactivation'))
end
end
end
diff --git a/spec/controllers/verify/finance_controller_spec.rb b/spec/controllers/verify/finance_controller_spec.rb
index 4ae70d9091c..335e4f2cc93 100644
--- a/spec/controllers/verify/finance_controller_spec.rb
+++ b/spec/controllers/verify/finance_controller_spec.rb
@@ -141,7 +141,10 @@
}
expect(@analytics).to have_received(:track_event).with(
- Analytics::IDV_FINANCE_CONFIRMATION, result
+ Analytics::IDV_FINANCE_CONFIRMATION_FORM, result
+ )
+ expect(@analytics).to have_received(:track_event).with(
+ Analytics::IDV_FINANCE_CONFIRMATION_VENDOR, result
)
end
end
@@ -156,7 +159,9 @@
}
expect(@analytics).to have_received(:track_event).
- with(Analytics::IDV_FINANCE_CONFIRMATION, result)
+ with(Analytics::IDV_FINANCE_CONFIRMATION_FORM, success: true, errors: {})
+ expect(@analytics).to have_received(:track_event).
+ with(Analytics::IDV_FINANCE_CONFIRMATION_VENDOR, result)
end
end
@@ -172,8 +177,8 @@
}
expect(@analytics).to have_received(:track_event).
- with(Analytics::IDV_FINANCE_CONFIRMATION, result)
- expect(subject.idv_session.financials_confirmation).to eq false
+ with(Analytics::IDV_FINANCE_CONFIRMATION_FORM, result)
+ expect(subject.idv_session.financials_confirmation).to be_falsy
expect(Idv::FinancialsValidator).to_not have_received(:new)
end
end
diff --git a/spec/controllers/verify/phone_controller_spec.rb b/spec/controllers/verify/phone_controller_spec.rb
index 5f20d7df012..8631d32ae97 100644
--- a/spec/controllers/verify/phone_controller_spec.rb
+++ b/spec/controllers/verify/phone_controller_spec.rb
@@ -1,7 +1,8 @@
require 'rails_helper'
-include Features::LocalizationHelper
describe Verify::PhoneController do
+ include Features::LocalizationHelper
+
let(:max_attempts) { Idv::Attempter.idv_max_attempts }
let(:good_phone) { '+1 (555) 555-0000' }
let(:bad_phone) { '+1 (555) 555-5555' }
@@ -57,7 +58,7 @@
end
it 'tracks form error and does not make a vendor API call' do
- allow(Idv::PhoneValidator).to receive(:new)
+ expect(Idv::PhoneValidator).to_not receive(:new)
put :create, idv_phone_form: { phone: '703' }
@@ -69,10 +70,9 @@
}
expect(@analytics).to have_received(:track_event).with(
- Analytics::IDV_PHONE_CONFIRMATION, result
+ Analytics::IDV_PHONE_CONFIRMATION_FORM, result
)
- expect(subject.idv_session.phone_confirmation).to eq false
- expect(Idv::PhoneValidator).to_not have_received(:new)
+ expect(subject.idv_session.phone_confirmation).to be_falsy
end
end
@@ -91,7 +91,10 @@
result = { success: true, errors: {} }
expect(@analytics).to have_received(:track_event).with(
- Analytics::IDV_PHONE_CONFIRMATION, result
+ Analytics::IDV_PHONE_CONFIRMATION_FORM, result
+ )
+ expect(@analytics).to have_received(:track_event).with(
+ Analytics::IDV_PHONE_CONFIRMATION_VENDOR, result
)
end
@@ -111,7 +114,10 @@
expect(flash[:warning]).to match t('idv.modal.phone.heading')
expect(flash[:warning]).to match t('idv.modal.attempts', count: max_attempts - 1)
expect(@analytics).to have_received(:track_event).with(
- Analytics::IDV_PHONE_CONFIRMATION, result
+ Analytics::IDV_PHONE_CONFIRMATION_FORM, success: true, errors: {}
+ )
+ expect(@analytics).to have_received(:track_event).with(
+ Analytics::IDV_PHONE_CONFIRMATION_VENDOR, result
)
end
diff --git a/spec/controllers/verify/sessions_controller_spec.rb b/spec/controllers/verify/sessions_controller_spec.rb
index b0e90722b48..67ff7dc8888 100644
--- a/spec/controllers/verify/sessions_controller_spec.rb
+++ b/spec/controllers/verify/sessions_controller_spec.rb
@@ -104,13 +104,11 @@
result = {
success: false,
- idv_attempts_exceeded: false,
errors: { ssn: [t('idv.errors.duplicate_ssn')] },
- vendor: { reasons: nil },
}
expect(@analytics).to receive(:track_event).
- with(Analytics::IDV_BASIC_INFO_SUBMITTED, result)
+ with(Analytics::IDV_BASIC_INFO_SUBMITTED_FORM, result)
post :create, profile: user_attrs.merge(ssn: '666-66-1234')
@@ -129,15 +127,21 @@
end
context 'missing fields' do
- it 'checks for required fields' do
- partial_attrs = user_attrs.dup
- partial_attrs.delete :first_name
+ let(:partial_attrs) do
+ user_attrs.tap { |attrs| attrs.delete :first_name }
+ end
+ it 'checks for required fields' do
post :create, profile: partial_attrs
expect(response).to render_template(:new)
expect(flash[:warning]).to be_nil
end
+
+ it 'does not increment attempts count' do
+ expect { post :create, profile: partial_attrs }.
+ to_not change(user, :idv_attempts)
+ end
end
context 'un-resolvable attributes' do
@@ -164,7 +168,7 @@
}
expect(@analytics).to have_received(:track_event).
- with(Analytics::IDV_BASIC_INFO_SUBMITTED, result)
+ with(Analytics::IDV_BASIC_INFO_SUBMITTED_VENDOR, result)
end
end
@@ -189,7 +193,7 @@
}
expect(@analytics).to have_received(:track_event).
- with(Analytics::IDV_BASIC_INFO_SUBMITTED, result)
+ with(Analytics::IDV_BASIC_INFO_SUBMITTED_VENDOR, result)
expect(response).to render_template(:new)
end
end
@@ -206,7 +210,11 @@
}
expect(@analytics).to have_received(:track_event).
- with(Analytics::IDV_BASIC_INFO_SUBMITTED, result)
+ with(Analytics::IDV_BASIC_INFO_SUBMITTED_VENDOR, result)
+ end
+
+ it 'increments attempts count' do
+ expect { post :create, profile: user_attrs }.to change(user, :idv_attempts).by(1)
end
end
diff --git a/spec/decorators/service_provider_session_decorator_spec.rb b/spec/decorators/service_provider_session_decorator_spec.rb
index 3f1ed39ff0c..56bd0ab121a 100644
--- a/spec/decorators/service_provider_session_decorator_spec.rb
+++ b/spec/decorators/service_provider_session_decorator_spec.rb
@@ -3,7 +3,12 @@
RSpec.describe ServiceProviderSessionDecorator do
let(:view_context) { ActionController::Base.new.view_context }
subject do
- ServiceProviderSessionDecorator.new(sp: sp, view_context: view_context, sp_session: {})
+ ServiceProviderSessionDecorator.new(
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
+ )
end
let(:sp) { build_stubbed(:service_provider) }
let(:sp_name) { subject.sp_name }
@@ -59,7 +64,10 @@
it 'returns the agency name if friendly name is not present' do
sp = build_stubbed(:service_provider, friendly_name: nil)
subject = ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: {}
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
)
expect(subject.sp_name).to eq sp.agency
expect(subject.sp_name).to_not be_nil
@@ -73,7 +81,10 @@
sp = build_stubbed(:service_provider, logo: sp_logo)
subject = ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: {}
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
)
expect(subject.sp_logo).to eq sp_logo
@@ -85,7 +96,10 @@
sp = build_stubbed(:service_provider, logo: nil)
subject = ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: {}
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
)
expect(subject.sp_logo).to eq 'generic.svg'
@@ -96,7 +110,10 @@
describe '#cancel_link_path' do
it 'returns sign_up_start_url with the request_id as a param' do
subject = ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: { request_id: 'foo' }
+ sp: sp,
+ view_context: view_context,
+ sp_session: { request_id: 'foo' },
+ service_provider_request: ServiceProviderRequest.new
)
expect(subject.cancel_link_path).
diff --git a/spec/decorators/usps_decorator_spec.rb b/spec/decorators/usps_decorator_spec.rb
index 890182ba499..d26a29f2aaf 100644
--- a/spec/decorators/usps_decorator_spec.rb
+++ b/spec/decorators/usps_decorator_spec.rb
@@ -1,23 +1,16 @@
require 'rails_helper'
RSpec.describe UspsDecorator do
+ let(:user) { create(:user) }
subject(:decorator) do
- user = create(
- :user,
- :signed_up,
- profiles: [build(:profile, :active, :verified, pii: { first_name: 'Jane' })]
- )
-
- idv_session = Idv::Session.new(user_session: {}, current_user: user, issuer: nil)
- UspsDecorator.new(idv_session)
+ usps_mail_service = Idv::UspsMail.new(user)
+ UspsDecorator.new(usps_mail_service)
end
describe '#title' do
context 'a letter has not been sent' do
- let(:idv_session) { subject.idv_session }
-
it 'provides text to send' do
- subject.idv_session.address_verification_mechanism = nil
+ allow(subject.usps_mail_service).to receive(:any_mail_sent?).and_return(false)
expect(subject.title).to eq(
I18n.t('idv.titles.mail.verify')
)
@@ -26,7 +19,7 @@
context 'a letter has been sent' do
it 'provides text to resend' do
- subject.idv_session.address_verification_mechanism = 'usps'
+ allow(subject.usps_mail_service).to receive(:any_mail_sent?).and_return(true)
expect(subject.title).to eq(
I18n.t('idv.titles.mail.resend')
)
@@ -37,7 +30,7 @@
describe '#button' do
context 'a letter has not been sent' do
it 'provides text to send' do
- subject.idv_session.address_verification_mechanism = nil
+ allow(subject.usps_mail_service).to receive(:any_mail_sent?).and_return(false)
expect(subject.button).to eq(
I18n.t('idv.buttons.mail.send')
)
@@ -46,7 +39,7 @@
context 'a letter has been sent' do
it 'provides text to resend' do
- subject.idv_session.address_verification_mechanism = 'usps'
+ allow(subject.usps_mail_service).to receive(:any_mail_sent?).and_return(true)
expect(subject.button).to eq(
I18n.t('idv.buttons.mail.resend')
)
diff --git a/spec/features/openid_connect/openid_connect_spec.rb b/spec/features/openid_connect/openid_connect_spec.rb
index 01de6a86d96..6dad66fde81 100644
--- a/spec/features/openid_connect/openid_connect_spec.rb
+++ b/spec/features/openid_connect/openid_connect_spec.rb
@@ -487,9 +487,12 @@
visit_idp_from_sp_with_loa1
click_link t('links.sign_in')
fill_in_credentials_and_submit(user.email, user.password)
+ sp_request_id = ServiceProviderRequest.last.uuid
+ sp = ServiceProvider.from_issuer('urn:gov:gsa:openidconnect:sp:server')
click_link t('links.cancel')
- expect(current_url).to eq root_url
+ expect(current_url).to eq sign_up_start_url(request_id: sp_request_id)
+ expect(page).to have_content t('links.back_to_sp', sp: sp.friendly_name)
end
end
diff --git a/spec/features/saml/loa1_sso_spec.rb b/spec/features/saml/loa1_sso_spec.rb
index b74e07152fa..b8e1e9bae7a 100644
--- a/spec/features/saml/loa1_sso_spec.rb
+++ b/spec/features/saml/loa1_sso_spec.rb
@@ -152,6 +152,23 @@
end
end
+ context 'canceling sign in after email and password' do
+ it 'returns to the branded landing page' do
+ user = create(:user, :signed_up)
+ authn_request = auth_request.create(saml_settings)
+
+ visit authn_request
+ click_link t('links.sign_in')
+ fill_in_credentials_and_submit(user.email, user.password)
+ sp_request_id = ServiceProviderRequest.last.uuid
+ sp = ServiceProvider.from_issuer('http://localhost:3000')
+ click_link t('links.cancel')
+
+ expect(current_url).to eq sign_up_start_url(request_id: sp_request_id)
+ expect(page).to have_content t('links.back_to_sp', sp: sp.friendly_name)
+ end
+ end
+
def sign_in_and_require_viewing_personal_key(user)
login_as(user, scope: :user, run_callbacks: false)
Warden.on_next_request do |proxy|
diff --git a/spec/features/saml/loa3_sso_spec.rb b/spec/features/saml/loa3_sso_spec.rb
index 85785816185..a1a58671f14 100644
--- a/spec/features/saml/loa3_sso_spec.rb
+++ b/spec/features/saml/loa3_sso_spec.rb
@@ -4,6 +4,24 @@
include SamlAuthHelper
include IdvHelper
+ def perform_id_verification_with_usps_without_confirming_code_then_sign_out(user)
+ saml_authn_request = auth_request.create(loa3_with_bundle_saml_settings)
+ visit saml_authn_request
+ sign_in_live_with_2fa(user)
+ click_idv_begin
+ fill_out_idv_form_ok
+ click_idv_continue
+ fill_out_financial_form_ok
+ click_idv_continue
+ click_idv_address_choose_usps
+ click_on t('idv.buttons.mail.send')
+ fill_in :user_password, with: user.password
+ click_submit_default
+ click_acknowledge_personal_key
+ first(:link, t('links.sign_out')).click
+ click_submit_default
+ end
+
context 'First time registration' do
let(:email) { 'test@test.com' }
before do
@@ -186,6 +204,7 @@
sign_in_live_with_2fa(user)
expect(current_path).to eq verify_account_path
+ expect(page).to have_content t('idv.messages.usps.resend')
click_button t('forms.verify_profile.submit')
@@ -196,6 +215,21 @@
expect(current_url).to eq saml_authn_request
end
+
+ it 'provides an option to send another letter' do
+ user = create(:user, :signed_up)
+
+ perform_id_verification_with_usps_without_confirming_code_then_sign_out(user)
+
+ sign_in_live_with_2fa(user)
+
+ expect(current_path).to eq verify_account_path
+
+ click_link(t('idv.messages.usps.resend'))
+
+ expect(user.events.account_verified.size).to be(0)
+ expect(current_path).to eq(verify_usps_path)
+ end
end
context 'having previously cancelled phone verification' do
diff --git a/spec/features/visitors/i18n_spec.rb b/spec/features/visitors/i18n_spec.rb
index e86cf30231c..12f3a72f814 100644
--- a/spec/features/visitors/i18n_spec.rb
+++ b/spec/features/visitors/i18n_spec.rb
@@ -1,7 +1,14 @@
require 'rails_helper'
feature 'Internationalization' do
- context 'visit homepage' do
+ context 'visit homepage with no locale set' do
+ it 'displays a header in the default locale' do
+ visit root_path
+ expect(page).to have_content t('headings.sign_in_without_sp', locale: 'en')
+ end
+ end
+
+ context 'visit homepage with locale set in header' do
before do
page.driver.header 'Accept-Language', locale
visit root_path
@@ -22,5 +29,33 @@
expect(page).to have_content t('headings.sign_in_without_sp', locale: 'es')
end
end
+
+ context 'when the user selects an unsupported locale' do
+ let(:locale) { :es }
+
+ it 'it does not raise an exception' do
+ expect { visit root_path + '?locale=foo' }.to_not raise_exception
+ end
+
+ it 'it falls back to the locale set in header' do
+ expect(page).to have_content t('headings.sign_in_without_sp', locale: 'es')
+ end
+ end
+ end
+
+ context 'visit homepage without a locale param set' do
+ it 'displays header in the default locale' do
+ visit '/'
+
+ expect(page).to have_content t('headings.sign_in_without_sp', locale: 'en')
+ end
+ end
+
+ context 'visit homepage with locale param set to :es' do
+ it 'displays a translated header to the user' do
+ visit '/es/'
+
+ expect(page).to have_content t('headings.sign_in_without_sp', locale: 'es')
+ end
end
end
diff --git a/spec/features/visitors/set_password_spec.rb b/spec/features/visitors/set_password_spec.rb
index 8a518521deb..ad3efba140c 100644
--- a/spec/features/visitors/set_password_spec.rb
+++ b/spec/features/visitors/set_password_spec.rb
@@ -55,7 +55,7 @@
expect(page).to have_content '...'
fill_in 'password_form_password', with: 'password'
- expect(page).to have_content 'This is a top-10 common password'
+ expect(page).to have_content t('zxcvbn.feedback.this_is_a_top_10_common_password')
end
end
@@ -92,7 +92,7 @@
click_button t('forms.buttons.continue')
- expect(page).to have_content t('zxcvbn.feedback.This is a top-10 common password')
+ expect(page).to have_content t('zxcvbn.feedback.this_is_a_top_10_common_password')
end
end
end
diff --git a/spec/forms/idv/finance_form_spec.rb b/spec/forms/idv/finance_form_spec.rb
index a2b0411a10a..2c2a027da57 100644
--- a/spec/forms/idv/finance_form_spec.rb
+++ b/spec/forms/idv/finance_form_spec.rb
@@ -25,39 +25,58 @@
end
describe '#submit' do
- it 'adds ccn key to idv_params when valid' do
- expect(subject.submit(ccn: '12345678', finance_type: :ccn)).to eq true
+ context 'when the form is valid' do
+ let(:result) { subject.submit(ccn: '12345678', finance_type: :ccn) }
- expected_params = {
- ccn: '12345678',
- }
+ it 'returns a successful form response' do
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ end
- expect(subject.idv_params).to eq expected_params
+ it 'adds ccn key to idv_params' do
+ expected_params = {
+ ccn: '12345678',
+ }
+ subject.submit(ccn: '12345678', finance_type: :ccn)
+ expect(subject.idv_params).to eq expected_params
+ end
end
- it 'fails when missing all finance fields' do
- expect(subject.submit(foo: 'bar')).to eq false
+ context 'when the form is invalid' do
+ it 'returns an unsuccessful form response' do
+ result = subject.submit(foo: 'bar')
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to be_present
+ end
end
context 'when CCN is invalid' do
- it 'fails when alpha' do
- expect(subject.submit(ccn: '1234567a', finance_type: :ccn)).to eq false
- expect(subject.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
+ it 'returns an unsuccessful form response when alpha' do
+ result = subject.submit(ccn: '1234567a', finance_type: :ccn)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
end
- it 'fails when long' do
- expect(subject.submit(ccn: '123456789', finance_type: :ccn)).to eq false
- expect(subject.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
+ it 'returns an unsuccessful form response when long' do
+ result = subject.submit(ccn: '123456789', finance_type: :ccn)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
end
- it 'fails when short' do
- expect(subject.submit(ccn: '1234567', finance_type: :ccn)).to eq false
- expect(subject.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
+ it 'returns an unsuccessful form response when short' do
+ result = subject.submit(ccn: '1234567', finance_type: :ccn)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors[:ccn]).to eq([t('idv.errors.invalid_ccn')])
end
end
context 'any non-ccn financial value is less than the minimum allowed digits' do
- it 'fails' do
+ it 'returns an unsuccessful form response' do
finance_types = Idv::FinanceForm::FINANCE_TYPES
short_value = '1' * (FormFinanceValidator::VALID_MINIMUM_LENGTH - 1)
@@ -65,8 +84,10 @@
next if type == :ccn
params = { type => short_value, finance_type: type }
- expect(subject.submit(params)).to eq false
- expect(subject.errors[type]).to eq([t(
+ result = subject.submit(params)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors[type]).to eq([t(
'idv.errors.finance_number_length',
minimum: FormFinanceValidator::VALID_MINIMUM_LENGTH,
maximum: FormFinanceValidator::VALID_MAXIMUM_LENGTH
@@ -76,7 +97,7 @@
end
context 'any non-ccn financial value is over the max allowed digits' do
- it 'fails' do
+ it 'returns an unsuccessful form response' do
finance_types = Idv::FinanceForm::FINANCE_TYPES
long_value = '1' * (FormFinanceValidator::VALID_MAXIMUM_LENGTH + 1)
@@ -88,8 +109,10 @@
finance_type: symbolized_type,
}
- expect(subject.submit(params)).to eq false
- expect(subject.errors[symbolized_type]).to eq([t(
+ result = subject.submit(params)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors[symbolized_type]).to eq([t(
'idv.errors.finance_number_length',
minimum: FormFinanceValidator::VALID_MINIMUM_LENGTH,
maximum: FormFinanceValidator::VALID_MAXIMUM_LENGTH
diff --git a/spec/forms/idv/phone_form_spec.rb b/spec/forms/idv/phone_form_spec.rb
index 7eb9673c2a1..9aa04797889 100644
--- a/spec/forms/idv/phone_form_spec.rb
+++ b/spec/forms/idv/phone_form_spec.rb
@@ -7,14 +7,34 @@
it_behaves_like 'a phone form'
describe '#submit' do
- it 'adds phone key to idv_params when valid' do
- subject.submit(phone: '703-555-1212')
+ context 'when the form is valid' do
+ it 'returns a successful form response' do
+ result = subject.submit(phone: '703-555-1212')
- expected_params = {
- phone: '+1 (703) 555-1212',
- }
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ end
- expect(subject.idv_params).to eq expected_params
+ it 'adds phone key to idv_params' do
+ subject.submit(phone: '703-555-1212')
+
+ expected_params = {
+ phone: '+1 (703) 555-1212',
+ }
+
+ expect(subject.idv_params).to eq expected_params
+ end
+ end
+
+ context 'when the form is invalid' do
+ it 'returns an unsuccessful form response' do
+ result = subject.submit(phone: 'Im not a phone number 🙃')
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to include(:phone)
+ end
end
it 'adds phone_confirmed_at key to idv_params when submitted phone equals user phone' do
diff --git a/spec/forms/idv/profile_form_spec.rb b/spec/forms/idv/profile_form_spec.rb
index 60a8b935e7e..3c0987ef2ad 100644
--- a/spec/forms/idv/profile_form_spec.rb
+++ b/spec/forms/idv/profile_form_spec.rb
@@ -19,6 +19,28 @@
}
end
+ describe '#submit' do
+ context 'when the form is valid' do
+ it 'returns a successful form response' do
+ result = subject.submit(profile_attrs)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ end
+ end
+
+ context 'when the form is invalid' do
+ before { profile_attrs[:dob] = nil }
+
+ it 'returns an unsuccessful form response' do
+ result = subject.submit(profile_attrs)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to include(:dob)
+ end
+ end
+ end
+
describe 'presence validations' do
it 'is invalid when required attribute is not present' do
%i[first_name last_name ssn dob address1 city state zipcode].each do |attr|
diff --git a/spec/helpers/locale_helper_spec.rb b/spec/helpers/locale_helper_spec.rb
new file mode 100644
index 00000000000..de3dc7818d1
--- /dev/null
+++ b/spec/helpers/locale_helper_spec.rb
@@ -0,0 +1,31 @@
+require 'rails_helper'
+
+RSpec.describe LocaleHelper do
+ include LocaleHelper
+
+ describe '#locale_url_param' do
+ context 'in the default locale' do
+ before { I18n.locale = :en }
+
+ it 'is nil' do
+ expect(locale_url_param).to be_nil
+ end
+ end
+
+ context 'in French (a non-default locale)' do
+ before { I18n.locale = :fr }
+
+ it 'is that locale' do
+ expect(locale_url_param).to eq(:fr)
+ end
+ end
+
+ context 'in Spanish (a non-default locale)' do
+ before { I18n.locale = :es }
+
+ it 'is that locale' do
+ expect(locale_url_param).to eq(:es)
+ end
+ end
+ end
+end
diff --git a/spec/i18n_spec.rb b/spec/i18n_spec.rb
index 8dd0319a723..5ab0b18e9d7 100644
--- a/spec/i18n_spec.rb
+++ b/spec/i18n_spec.rb
@@ -20,4 +20,34 @@
"#{unused_keys.leaves.count} unused i18n keys, run `i18n-tasks unused' to show them"
)
end
+
+ root_dir = File.expand_path(File.join(File.dirname(__FILE__), '../'))
+
+ Dir[File.join(root_dir, '/config/locales/**/*.yml')].each do |full_path|
+ i18n_file = full_path.sub("#{root_dir}/", '')
+
+ describe i18n_file do
+ it 'has only lower_snake_case keys' do
+ keys = hash_keys(YAML.load_file(full_path))
+
+ bad_keys = keys.reject { |key| key =~ /^[a-z0-9_.]+$/ }
+
+ expect(bad_keys).to be_empty
+ end
+ end
+ end
+
+ def hash_keys(hash, parent_keys: [])
+ keys = []
+
+ hash.each do |key, value|
+ if value.is_a?(Hash)
+ keys += hash_keys(value, parent_keys: parent_keys + [key])
+ else
+ keys << [*parent_keys, key].join('.')
+ end
+ end
+
+ keys
+ end
end
diff --git a/spec/jobs/sms_otp_sender_job_spec.rb b/spec/jobs/sms_otp_sender_job_spec.rb
index 1afc5369d23..414e5b4b7ed 100644
--- a/spec/jobs/sms_otp_sender_job_spec.rb
+++ b/spec/jobs/sms_otp_sender_job_spec.rb
@@ -20,8 +20,7 @@
expect(msg.from).to match(/(\+19999999999|\+12222222222)/)
expect(msg.to).to eq('555-5555')
- expect(msg.body).to include('one-time security code')
- expect(msg.body).to include('1234')
+ expect(msg.body).to eq(I18n.t('jobs.sms_otp_sender_job.message', code: '1234', app: APP_NAME))
end
it 'does not send if the OTP code is expired' do
diff --git a/spec/jobs/vendor_validator_job_spec.rb b/spec/jobs/vendor_validator_job_spec.rb
new file mode 100644
index 00000000000..d10bd5f4533
--- /dev/null
+++ b/spec/jobs/vendor_validator_job_spec.rb
@@ -0,0 +1,44 @@
+require 'rails_helper'
+
+RSpec.describe VendorValidatorJob do
+ let(:result_id) { SecureRandom.uuid }
+ let(:vendor_validator_class) { 'Idv::PhoneValidator' }
+ let(:vendor) { :mock }
+ let(:vendor_params) { '+1 (888) 123-4567' }
+ let(:applicant) { Proofer::Applicant.new(first_name: 'Test') }
+ let(:applicant_json) { applicant.to_json }
+ let(:vendor_session_id) { SecureRandom.uuid }
+
+ subject(:job) { VendorValidatorJob.new }
+
+ describe '#perform' do
+ subject(:perform) do
+ job.perform(
+ result_id: result_id,
+ vendor_validator_class: vendor_validator_class,
+ vendor: vendor,
+ vendor_params: vendor_params,
+ applicant_json: applicant_json,
+ vendor_session_id: vendor_session_id
+ )
+ end
+
+ it 'calls out to a vendor and serializes the result' do
+ expect(Idv::PhoneValidator).to receive(:new).
+ with(
+ applicant: kind_of(Proofer::Applicant),
+ vendor: vendor,
+ vendor_params: vendor_params,
+ vendor_session_id: vendor_session_id
+ ).and_call_original
+
+ before_result = VendorValidatorResultStorage.new.load(result_id)
+ expect(before_result).to be_nil
+
+ perform
+
+ after_result = VendorValidatorResultStorage.new.load(result_id)
+ expect(after_result).to be_a(Idv::VendorResult)
+ end
+ end
+end
diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb
index cfce7ebf52d..c3b16d2cd8a 100644
--- a/spec/mailers/user_mailer_spec.rb
+++ b/spec/mailers/user_mailer_spec.rb
@@ -67,6 +67,14 @@
)
expect_email_body_to_have_help_and_contact_links
end
+
+ context 'in a non-default locale' do
+ before { I18n.locale = :fr }
+
+ it 'links to the correct locale' do
+ expect(mail.html_part.body).to include(root_url(locale: :fr))
+ end
+ end
end
describe 'phone_changed' do
diff --git a/spec/models/otp_requests_tracker_spec.rb b/spec/models/otp_requests_tracker_spec.rb
new file mode 100644
index 00000000000..21c0f8fdde1
--- /dev/null
+++ b/spec/models/otp_requests_tracker_spec.rb
@@ -0,0 +1,51 @@
+require 'rails_helper'
+
+describe OtpRequestsTracker do
+ describe '.find_or_create_with_phone' do
+ let(:phone) { '+1 703 555 1212' }
+ let(:phone_fingerprint) { Pii::Fingerprinter.fingerprint(phone) }
+
+ context 'match found' do
+ it 'returns the existing record and does not change it' do
+ OtpRequestsTracker.create(
+ phone_fingerprint: phone_fingerprint,
+ otp_send_count: 3,
+ otp_last_sent_at: Time.zone.now - 1.hour
+ )
+
+ existing = OtpRequestsTracker.where(phone_fingerprint: phone_fingerprint).first
+
+ expect { OtpRequestsTracker.find_or_create_with_phone(phone) }.
+ to_not change(OtpRequestsTracker, :count)
+ expect { OtpRequestsTracker.find_or_create_with_phone(phone) }.
+ to_not change { existing.otp_send_count }
+ expect { OtpRequestsTracker.find_or_create_with_phone(phone) }.
+ to_not change { existing.otp_last_sent_at }
+ end
+ end
+
+ context 'match not found' do
+ it 'creates new record with otp_send_count = 0 and otp_last_sent_at = current time' do
+ expect { OtpRequestsTracker.find_or_create_with_phone(phone) }.
+ to change(OtpRequestsTracker, :count).by(1)
+
+ existing = OtpRequestsTracker.where(phone_fingerprint: phone_fingerprint).first
+
+ expect(existing.otp_send_count).to eq 0
+ expect(existing.otp_last_sent_at).to be_within(2.seconds).of(Time.zone.now)
+ end
+ end
+
+ context 'race condition' do
+ it 'retries once, then raises ActiveRecord::RecordNotUnique' do
+ tracker = OtpRequestsTracker.new
+ allow(OtpRequestsTracker).to receive(:where).
+ and_raise(ActiveRecord::RecordNotUnique.new(tracker))
+
+ expect(OtpRequestsTracker).to receive(:where).exactly(:once)
+ expect { OtpRequestsTracker.find_or_create_with_phone(phone) }.
+ to raise_error ActiveRecord::RecordNotUnique
+ end
+ end
+ end
+end
diff --git a/spec/services/idv/financials_step_spec.rb b/spec/services/idv/financials_step_spec.rb
index 4e2407e82cb..1445ab394f7 100644
--- a/spec/services/idv/financials_step_spec.rb
+++ b/spec/services/idv/financials_step_spec.rb
@@ -7,58 +7,49 @@
idvs.vendor = :mock
idvs
end
- let(:idv_finance_form) { Idv::FinanceForm.new(idv_session.params) }
+ let(:idv_form_params) { idv_session.params }
- def build_step(params)
+ def build_step(vendor_validator_result)
described_class.new(
- idv_form: idv_finance_form,
+ idv_form_params: idv_form_params,
idv_session: idv_session,
- params: params
+ vendor_validator_result: vendor_validator_result
)
end
describe '#submit' do
- it 'returns FormResponse with success: false for invalid params' do
- step = build_step(finance_type: :ccn, ccn: '1234')
- errors = { ccn: [t('idv.errors.invalid_ccn')] }
-
- response = instance_double(FormResponse)
- allow(FormResponse).to receive(:new).and_return(response)
- submission = step.submit
-
- expect(submission).to eq response
- expect(FormResponse).to have_received(:new).
- with(success: false, errors: errors)
- expect(idv_session.financials_confirmation).to eq false
- end
-
it 'returns FormResponse with success: true for mock-happy CCN' do
- step = build_step(finance_type: :ccn, ccn: '12345678')
+ step = build_step(
+ Idv::VendorResult.new(
+ success: true,
+ errors: {}
+ )
+ )
+
+ result = step.submit
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
- response = instance_double(FormResponse)
- allow(FormResponse).to receive(:new).and_return(response)
-
- submission = step.submit
-
- expect(submission).to eq response
- expect(FormResponse).to have_received(:new).
- with(success: true, errors: {})
expect(idv_session.financials_confirmation).to eq true
- expect(idv_session.params).to eq idv_finance_form.idv_params
+ expect(idv_session.params).to eq idv_form_params
end
it 'returns FormResponse with success: false for mock-sad CCN' do
- step = build_step(finance_type: :ccn, ccn: '00000000')
-
errors = { ccn: ['The ccn could not be verified.'] }
- response = instance_double(FormResponse)
- allow(FormResponse).to receive(:new).and_return(response)
- submission = step.submit
+ step = build_step(
+ Idv::VendorResult.new(
+ success: false,
+ errors: errors
+ )
+ )
+
+ result = step.submit
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
- expect(submission).to eq response
- expect(FormResponse).to have_received(:new).
- with(success: false, errors: errors)
expect(idv_session.financials_confirmation).to eq false
end
end
diff --git a/spec/services/idv/financials_validator_spec.rb b/spec/services/idv/financials_validator_spec.rb
index 0292c6b204d..2010f5bd8bb 100644
--- a/spec/services/idv/financials_validator_spec.rb
+++ b/spec/services/idv/financials_validator_spec.rb
@@ -3,13 +3,9 @@
describe Idv::FinancialsValidator do
let(:user) { build(:user) }
- let(:idv_session) do
- idvs = Idv::Session.new(user_session: {}, current_user: user, issuer: nil)
- idvs.vendor = :mock
- idvs
- end
-
- let(:session_id) { idv_session.vendor_session_id }
+ let(:applicant) { Proofer::Applicant.new({}) }
+ let(:vendor) { :mock }
+ let(:vendor_session_id) { SecureRandom.uuid }
let(:params) do
{ ccn: '123-45-6789' }
@@ -17,38 +13,42 @@
let(:confirmation) { instance_double(Proofer::Confirmation) }
- subject { Idv::FinancialsValidator.new(idv_session: idv_session, vendor_params: params) }
+ subject do
+ Idv::FinancialsValidator.new(
+ applicant: applicant,
+ vendor: vendor,
+ vendor_params: params,
+ vendor_session_id: vendor_session_id
+ )
+ end
def stub_agent_calls
agent = instance_double(Idv::Agent)
allow(Idv::Agent).to receive(:new).
- with(applicant: idv_session.applicant, vendor: :mock).
+ with(applicant: applicant, vendor: vendor).
and_return(agent)
expect(agent).to receive(:submit_financials).
- with(params, idv_session.vendor_session_id).and_return(confirmation)
+ with(params, vendor_session_id).and_return(confirmation)
end
- describe '#success?' do
- it 'returns Proofer::Confirmation#success?' do
+ describe '#result' do
+ it 'has success' do
stub_agent_calls
success_string = 'true'
-
expect(confirmation).to receive(:success?).and_return(success_string)
- expect(subject.success?).to eq success_string
+ expect(subject.result.success?).to eq success_string
end
- end
- describe '#error' do
- it 'returns Proofer::Confirmation#errors' do
+ it 'has errors' do
stub_agent_calls
error_string = 'mucho errors'
expect(confirmation).to receive(:errors).and_return(error_string)
- expect(subject.errors).to eq error_string
+ expect(subject.result.errors).to eq error_string
end
end
end
diff --git a/spec/services/idv/phone_step_spec.rb b/spec/services/idv/phone_step_spec.rb
index 3a609f56829..4de8223d203 100644
--- a/spec/services/idv/phone_step_spec.rb
+++ b/spec/services/idv/phone_step_spec.rb
@@ -12,50 +12,49 @@
end
let(:idv_phone_form) { Idv::PhoneForm.new(idv_session.params, user) }
- def build_step(params)
+ def build_step(phone, vendor_validator_result)
described_class.new(
- idv_form: idv_phone_form,
idv_session: idv_session,
- params: params
+ idv_form_params: { phone: phone },
+ vendor_validator_result: vendor_validator_result
)
end
describe '#submit' do
- it 'returns false for invalid-looking phone' do
- step = build_step(phone: '555')
-
- errors = { phone: [invalid_phone_message] }
-
- result = instance_double(FormResponse)
-
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors).and_return(result)
- expect(step.submit).to eq result
- expect(idv_session.phone_confirmation).to eq false
- end
-
it 'returns true for mock-happy phone' do
- step = build_step(phone: '555-555-0000')
-
- result = instance_double(FormResponse)
-
- expect(FormResponse).to receive(:new).with(success: true, errors: {}).
- and_return(result)
- expect(step.submit).to eq result
+ step = build_step(
+ '555-555-0000',
+ Idv::VendorResult.new(
+ success: true,
+ errors: {}
+ )
+ )
+
+ result = step.submit
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
expect(idv_session.phone_confirmation).to eq true
expect(idv_session.params).to eq idv_phone_form.idv_params
end
it 'returns false for mock-sad phone' do
- step = build_step(phone: '555-555-5555')
-
errors = { phone: ['The phone number could not be verified.'] }
- result = instance_double(FormResponse)
+ step = build_step(
+ '555-555-5555',
+ Idv::VendorResult.new(
+ success: false,
+ errors: errors
+ )
+ )
+
+ result = step.submit
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors).and_return(result)
- expect(step.submit).to eq result
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
expect(idv_session.phone_confirmation).to eq false
end
end
diff --git a/spec/services/idv/phone_validator_spec.rb b/spec/services/idv/phone_validator_spec.rb
index 83b849ca116..faa6755c778 100644
--- a/spec/services/idv/phone_validator_spec.rb
+++ b/spec/services/idv/phone_validator_spec.rb
@@ -3,13 +3,9 @@
describe Idv::PhoneValidator do
let(:user) { build(:user) }
- let(:idv_session) do
- idvs = Idv::Session.new(user_session: {}, current_user: user, issuer: nil)
- idvs.vendor = :mock
- idvs
- end
-
- let(:session_id) { idv_session.vendor_session_id }
+ let(:applicant) { Proofer::Applicant.new({}) }
+ let(:vendor) { :mock }
+ let(:vendor_session_id) { SecureRandom.uuid }
let(:params) do
{ phone: '202-555-1212' }
@@ -17,38 +13,43 @@
let(:confirmation) { instance_double(Proofer::Confirmation) }
- subject { Idv::PhoneValidator.new(idv_session: idv_session, vendor_params: params) }
+ subject do
+ Idv::PhoneValidator.new(
+ applicant: applicant,
+ vendor: vendor,
+ vendor_params: params,
+ vendor_session_id: vendor_session_id
+ )
+ end
def stub_agent_calls
agent = instance_double(Idv::Agent)
allow(Idv::Agent).to receive(:new).
- with(applicant: idv_session.applicant, vendor: :mock).
+ with(applicant: applicant, vendor: vendor).
and_return(agent)
expect(agent).to receive(:submit_phone).
- with(params, idv_session.vendor_session_id).and_return(confirmation)
+ with(params, vendor_session_id).and_return(confirmation)
end
- describe '#success?' do
- it 'returns Proofer::Confirmation#success?' do
+ describe '#result' do
+ it 'has success' do
stub_agent_calls
success_string = 'true'
expect(confirmation).to receive(:success?).and_return(success_string)
- expect(subject.success?).to eq success_string
+ expect(subject.result.success?).to eq success_string
end
- end
- describe '#error' do
- it 'returns Proofer::Confirmation#errors' do
+ it 'has errors' do
stub_agent_calls
error_string = 'mucho errors'
expect(confirmation).to receive(:errors).and_return(error_string)
- expect(subject.errors).to eq error_string
+ expect(subject.result.errors).to eq error_string
end
end
end
diff --git a/spec/services/idv/profile_step_spec.rb b/spec/services/idv/profile_step_spec.rb
index e51f6f48c25..cc7655d0599 100644
--- a/spec/services/idv/profile_step_spec.rb
+++ b/spec/services/idv/profile_step_spec.rb
@@ -18,127 +18,139 @@
}
end
- def build_step(params)
+ def build_step(params, vendor_validator_result)
+ idv_session.params.merge!(params)
+ idv_session.applicant = idv_session.vendor_params
+
described_class.new(
- idv_form: idv_profile_form,
- idv_session: idv_session,
- params: params
+ idv_form_params: params,
+ vendor_validator_result: vendor_validator_result,
+ idv_session: idv_session
)
end
describe '#submit' do
it 'succeeds with good params' do
- step = build_step(user_attrs)
-
- result = instance_double(FormResponse)
+ reasons = ['Everything looks good']
extra = {
idv_attempts_exceeded: false,
- vendor: { reasons: ['Everything looks good'] },
+ vendor: { reasons: reasons },
}
- expect(FormResponse).to receive(:new).
- with(success: true, errors: {}, extra: extra).and_return(result)
- expect(step.submit).to eq result
+ step = build_step(
+ user_attrs,
+ Idv::VendorResult.new(
+ success: true,
+ errors: {},
+ reasons: reasons,
+ normalized_applicant: Proofer::Applicant.new(first_name: 'Some')
+ )
+ )
+
+ result = step.submit
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
+ expect(result.errors).to be_empty
+ expect(result.extra).to eq(extra)
expect(idv_session.profile_confirmation).to eq true
end
it 'fails with invalid SSN' do
- step = build_step(user_attrs.merge(ssn: '666-66-6666'))
-
+ reasons = ['The SSN was suspicious']
errors = { ssn: ['Unverified SSN.'] }
extra = {
idv_attempts_exceeded: false,
- vendor: { reasons: ['The SSN was suspicious'] },
+ vendor: { reasons: reasons },
}
- result = instance_double(FormResponse)
+ step = build_step(
+ user_attrs.merge(ssn: '666-66-6666'),
+ Idv::VendorResult.new(success: false, errors: errors, reasons: reasons)
+ )
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors, extra: extra).and_return(result)
- expect(step.submit).to eq result
- expect(idv_session.profile_confirmation).to be_nil
- end
-
- it 'fails when form validation fails' do
- step = build_step(user_attrs.merge(ssn: '6666'))
+ result = step.submit
- errors = { ssn: [t('idv.errors.pattern_mismatch.ssn')] }
- extra = {
- idv_attempts_exceeded: false,
- vendor: { reasons: nil },
- }
-
- result = instance_double(FormResponse)
-
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors, extra: extra).and_return(result)
- expect(step.submit).to eq result
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
+ expect(result.extra).to eq(extra)
expect(idv_session.profile_confirmation).to be_nil
end
it 'fails with invalid first name' do
- step = build_step(user_attrs.merge(first_name: 'Bad'))
-
errors = { first_name: ['Unverified first name.'] }
-
- result = instance_double(FormResponse)
+ reasons = ['The name was suspicious']
extra = {
idv_attempts_exceeded: false,
- vendor: { reasons: ['The name was suspicious'] },
+ vendor: { reasons: reasons },
}
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors, extra: extra).and_return(result)
- expect(step.submit).to eq result
+ step = build_step(
+ user_attrs.merge(first_name: 'Bad'),
+ Idv::VendorResult.new(success: false, errors: errors, reasons: reasons)
+ )
+
+ result = step.submit
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
+ expect(result.extra).to eq(extra)
expect(idv_session.profile_confirmation).to be_nil
end
it 'fails with invalid ZIP code on current address' do
- step = build_step(user_attrs.merge(zipcode: '00000'))
-
+ reasons = ['The ZIP code was suspicious']
errors = { zipcode: ['Unverified ZIP code.'] }
-
- result = instance_double(FormResponse)
extra = {
idv_attempts_exceeded: false,
- vendor: { reasons: ['The ZIP code was suspicious'] },
+ vendor: { reasons: reasons },
}
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors, extra: extra).and_return(result)
- expect(step.submit).to eq result
+ step = build_step(
+ user_attrs.merge(zipcode: '00000'),
+ Idv::VendorResult.new(success: false, errors: errors, reasons: reasons)
+ )
+
+ result = step.submit
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
+ expect(result.extra).to eq(extra)
expect(idv_session.profile_confirmation).to be_nil
end
it 'fails with invalid ZIP code on previous address' do
- step = build_step(user_attrs.merge(prev_zipcode: '00000'))
-
+ reasons = ['The ZIP code was suspicious']
errors = { zipcode: ['Unverified ZIP code.'] }
-
- result = instance_double(FormResponse)
extra = {
idv_attempts_exceeded: false,
- vendor: { reasons: ['The ZIP code was suspicious'] },
+ vendor: { reasons: reasons },
}
- expect(FormResponse).to receive(:new).
- with(success: false, errors: errors, extra: extra).and_return(result)
- expect(step.submit).to eq result
+ step = build_step(
+ user_attrs.merge(prev_zipcode: '00000'),
+ Idv::VendorResult.new(success: false, errors: errors, reasons: reasons)
+ )
+
+ result = step.submit
+
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(false)
+ expect(result.errors).to eq(errors)
+ expect(result.extra).to eq(extra)
expect(idv_session.profile_confirmation).to be_nil
end
- it 'increments attempts count if the form is valid' do
- step = build_step(user_attrs)
+ it 'increments attempts count' do
+ step = build_step(user_attrs, Idv::VendorResult.new(errors: {}))
expect { step.submit }.to change(user, :idv_attempts).by(1)
end
- it 'does not increment the attempts count if the form is not valid' do
- step = build_step(user_attrs.merge(ssn: '666'))
- expect { step.submit }.to change(user, :idv_attempts).by(0)
- end
-
it 'initializes the idv_session' do
- step = build_step(user_attrs)
+ step = build_step(user_attrs, Idv::VendorResult.new(errors: {}))
step.submit
expect(idv_session.params).to eq user_attrs
@@ -152,7 +164,7 @@ def build_step(params)
allow(Idv::Attempter).to receive(:new).with(user).and_return(attempter)
allow(attempter).to receive(:exceeded?)
- step = build_step(user_attrs)
+ step = build_step(user_attrs, Idv::VendorResult.new(errors: {}))
expect(step.attempts_exceeded?).to eq attempter.exceeded?
end
end
diff --git a/spec/services/idv/vendor_result_spec.rb b/spec/services/idv/vendor_result_spec.rb
new file mode 100644
index 00000000000..9dce4b8f52d
--- /dev/null
+++ b/spec/services/idv/vendor_result_spec.rb
@@ -0,0 +1,62 @@
+require 'rails_helper'
+
+RSpec.describe Idv::VendorResult do
+ let(:success) { true }
+ let(:errors) { { foo: ['is not valid'] } }
+ let(:reasons) { %w[foo bar baz] }
+ let(:session_id) { SecureRandom.uuid }
+ let(:normalized_applicant) do
+ Proofer::Applicant.new(
+ last_name: 'Ever',
+ first_name: 'Greatest'
+ )
+ end
+
+ subject(:vendor_result) do
+ Idv::VendorResult.new(
+ success: success,
+ errors: errors,
+ reasons: reasons,
+ session_id: session_id,
+ normalized_applicant: normalized_applicant
+ )
+ end
+
+ describe '#success?' do
+ it 'is the success value' do
+ expect(vendor_result.success?).to eq(success)
+ end
+ end
+
+ describe '#to_json' do
+ it 'serializes normalized_applicant correctly' do
+ json = vendor_result.to_json
+
+ parsed = JSON.parse(json, symbolize_names: true)
+ expect(parsed[:normalized_applicant][:last_name]).to eq(normalized_applicant.last_name)
+ end
+ end
+
+ describe '.new_from_json' do
+ subject(:new_from_json) { Idv::VendorResult.new_from_json(vendor_result.to_json) }
+
+ it 'has simple attributes' do
+ expect(new_from_json.success?).to eq(vendor_result.success?)
+ expect(new_from_json.errors).to eq(vendor_result.errors)
+ expect(new_from_json.reasons).to eq(vendor_result.reasons)
+ expect(new_from_json.session_id).to eq(vendor_result.session_id)
+ end
+
+ it 'turns applicant into a full object' do
+ expect(new_from_json.normalized_applicant.last_name).to eq(normalized_applicant.last_name)
+ end
+
+ context 'without an applicant' do
+ let(:normalized_applicant) { nil }
+
+ it 'does not have an applicant' do
+ expect(new_from_json.normalized_applicant).to eq(nil)
+ end
+ end
+ end
+end
diff --git a/spec/services/marketing_site_spec.rb b/spec/services/marketing_site_spec.rb
index bd72aca8384..80127eac095 100644
--- a/spec/services/marketing_site_spec.rb
+++ b/spec/services/marketing_site_spec.rb
@@ -3,7 +3,15 @@
RSpec.describe MarketingSite do
describe '.base_url' do
it 'points to the base URL' do
- expect(MarketingSite.base_url).to eq('https://www.login.gov')
+ expect(MarketingSite.base_url).to eq('https://www.login.gov/')
+ end
+
+ context 'when the user has set their locale to :es' do
+ before { I18n.locale = :es }
+
+ it 'points to the base URL with the locale appended' do
+ expect(MarketingSite.base_url).to eq('https://www.login.gov/es/')
+ end
end
end
@@ -11,18 +19,42 @@
it 'points to the privacy page' do
expect(MarketingSite.privacy_url).to eq('https://www.login.gov/policy')
end
+
+ context 'when the user has set their locale to :es' do
+ before { I18n.locale = :es }
+
+ it 'points to the privacy page with the locale appended' do
+ expect(MarketingSite.privacy_url).to eq('https://www.login.gov/es/policy')
+ end
+ end
end
describe '.contact_url' do
it 'points to the contact page' do
expect(MarketingSite.contact_url).to eq('https://www.login.gov/contact')
end
+
+ context 'when the user has set their locale to :es' do
+ before { I18n.locale = :es }
+
+ it 'points to the contact page with the locale appended' do
+ expect(MarketingSite.contact_url).to eq('https://www.login.gov/es/contact')
+ end
+ end
end
describe '.help_url' do
it 'points to the help page' do
expect(MarketingSite.help_url).to eq('https://www.login.gov/help')
end
+
+ context 'when the user has set their locale to :es' do
+ before { I18n.locale = :es }
+
+ it 'points to the help page with the locale appended' do
+ expect(MarketingSite.help_url).to eq('https://www.login.gov/es/help')
+ end
+ end
end
describe '.help_authenticator_app_url' do
@@ -31,5 +63,15 @@
'https://www.login.gov/help/signing-in/what-is-an-authenticator-app/'
)
end
+
+ context 'when the user has set their locale to :es' do
+ before { I18n.locale = :es }
+
+ it 'points to the authenticator app section of the help page with the locale appended' do
+ expect(MarketingSite.help_authenticator_app_url).to eq(
+ 'https://www.login.gov/es/help/signing-in/what-is-an-authenticator-app/'
+ )
+ end
+ end
end
end
diff --git a/spec/services/otp_rate_limiter_spec.rb b/spec/services/otp_rate_limiter_spec.rb
index bdf838dcac8..f2e6b2eb349 100644
--- a/spec/services/otp_rate_limiter_spec.rb
+++ b/spec/services/otp_rate_limiter_spec.rb
@@ -23,11 +23,13 @@
end
describe '#increment' do
- it 'sets the otp_last_sent_at' do
- now = Time.zone.now
+ it 'updates otp_last_sent_at' do
+ tracker = OtpRequestsTracker.find_or_create_with_phone(current_user.phone)
+ old_otp_last_sent_at = tracker.reload.otp_last_sent_at
otp_rate_limiter.increment
+ new_otp_last_sent_at = tracker.reload.otp_last_sent_at
- expect(rate_limited_phone.otp_last_sent_at.to_i).to eq(now.to_i)
+ expect(new_otp_last_sent_at).to be > old_otp_last_sent_at
end
it 'increments the otp_send_count' do
diff --git a/spec/services/submit_idv_job_spec.rb b/spec/services/submit_idv_job_spec.rb
new file mode 100644
index 00000000000..ba802f21269
--- /dev/null
+++ b/spec/services/submit_idv_job_spec.rb
@@ -0,0 +1,53 @@
+require 'rails_helper'
+
+RSpec.describe SubmitIdvJob do
+ subject(:service) do
+ SubmitIdvJob.new(
+ vendor_validator_class: vendor_validator_class,
+ idv_session: idv_session,
+ vendor_params: vendor_params
+ )
+ end
+
+ let(:idv_session) do
+ Idv::Session.new(
+ current_user: user,
+ issuer: nil,
+ user_session: {
+ idv: {
+ applicant: applicant,
+ vendor_session_id: vendor_session_id,
+ vendor: :mock,
+ },
+ }
+ )
+ end
+
+ let(:user) { build(:user) }
+ let(:applicant) { Proofer::Applicant.new(first_name: 'Greatest') }
+ let(:vendor_session_id) { '12345' }
+ let(:result_id) { 'abcdef' }
+ let(:vendor_params) { '+1 (888) 123-4567' }
+ let(:vendor_validator_class) { 'Idv::PhoneValidator' }
+
+ describe '#call' do
+ subject(:call) { service.call }
+
+ it 'generates a UUID and enqueues a job, and saves the UUID in the session' do
+ expect(SecureRandom).to receive(:uuid).and_return(result_id).once
+
+ expect(VendorValidatorJob).to receive(:perform_now).
+ with(
+ result_id: result_id,
+ vendor_validator_class: vendor_validator_class,
+ vendor: :mock,
+ vendor_params: vendor_params,
+ vendor_session_id: vendor_session_id,
+ applicant_json: applicant.to_json
+ )
+
+ expect { call }.
+ to change { idv_session.async_result_id }.from(nil).to(result_id)
+ end
+ end
+end
diff --git a/spec/services/usps_exporter_spec.rb b/spec/services/usps_exporter_spec.rb
index 2e4eba2144c..a2da4deee36 100644
--- a/spec/services/usps_exporter_spec.rb
+++ b/spec/services/usps_exporter_spec.rb
@@ -2,18 +2,17 @@
describe UspsExporter do
let(:export_file) { Tempfile.new('usps_export.psv') }
- let(:usps_entry) { UspsConfirmationEntry.new_from_hash(pii_attributes) }
let(:pii_attributes) do
- {
- first_name: 'Some',
- last_name: 'One',
- address1: '123 Any St',
- address2: 'Ste 123',
- city: 'Somewhere',
- state: 'KS',
- zipcode: '66666-1234',
- otp: 123,
- }
+ Pii::Attributes.new_from_hash(
+ first_name: { raw: 'Söme', norm: 'Some' },
+ last_name: { raw: 'Öne', norm: 'One' },
+ address1: { raw: '123 Añy St', norm: '123 Any St' },
+ address2: { raw: 'Sté 123', norm: 'Ste 123' },
+ city: { raw: 'Sömewhere', norm: 'Somewhere' },
+ state: { raw: 'KS', norm: 'KS' },
+ zipcode: { raw: '66666-1234', norm: '66666-1234' },
+ otp: { raw: 123, norm: 123 }
+ )
end
let(:service_provider) { ServiceProvider.from_issuer('http://localhost:3000') }
let(:psv_row_contents) do
@@ -23,13 +22,13 @@
due_date = due.strftime('%-B %-e')
values = [
UspsExporter::CONTENT_ROW_ID,
- usps_entry.first_name + ' ' + usps_entry.last_name,
- usps_entry.address1,
- usps_entry.address2,
- usps_entry.city,
- usps_entry.state,
- usps_entry.zipcode,
- usps_entry.otp,
+ pii_attributes.first_name.norm + ' ' + pii_attributes.last_name.norm,
+ pii_attributes.address1.norm,
+ pii_attributes.address2.norm,
+ pii_attributes.city.norm,
+ pii_attributes.state.norm,
+ pii_attributes.zipcode.norm,
+ pii_attributes.otp.norm,
"#{current_date}, #{now.year}",
"#{due_date}, #{due.year}",
service_provider.friendly_name,
diff --git a/spec/services/vendor_validator_result_storage_spec.rb b/spec/services/vendor_validator_result_storage_spec.rb
new file mode 100644
index 00000000000..6cd36a9270f
--- /dev/null
+++ b/spec/services/vendor_validator_result_storage_spec.rb
@@ -0,0 +1,51 @@
+require 'rails_helper'
+
+RSpec.describe VendorValidatorResultStorage do
+ subject(:service) { VendorValidatorResultStorage.new }
+
+ let(:session_id) { SecureRandom.uuid }
+ let(:result_id) { SecureRandom.uuid }
+ let(:original_result) do
+ Idv::VendorResult.new(
+ success: false,
+ session_id: session_id,
+ normalized_applicant: Proofer::Applicant.new(first_name: 'First')
+ )
+ end
+
+ describe '#store_result' do
+ it 'stores the result in redis with a TTL' do
+ key = service.redis_key(result_id)
+
+ before_redis = Sidekiq.redis { |redis| redis.get(key) }
+ expect(before_redis).to be_nil
+
+ service.store(result_id: result_id, result: original_result)
+
+ Sidekiq.redis do |redis|
+ expect(redis.get(key)).to be_present
+ expect(redis.ttl(key)).to be_within(1).of(VendorValidatorResultStorage::TTL)
+ end
+ end
+ end
+
+ describe '#vendor_validator_result' do
+ before { service.store(result_id: result_id, result: original_result) }
+
+ it 'retrieves a stored result' do
+ result = service.load(result_id)
+
+ expect(result.success?).to eq(original_result.success?)
+ expect(result.errors).to eq(original_result.errors)
+ expect(result.reasons).to eq(original_result.reasons)
+ expect(result.normalized_applicant.as_json).
+ to eq(original_result.normalized_applicant.as_json)
+ end
+
+ it 'is nil with a bad result id' do
+ result = service.load(SecureRandom.uuid)
+
+ expect(result).to be_nil
+ end
+ end
+end
diff --git a/spec/support/shared_examples_for_otp_forms.rb b/spec/support/shared_examples_for_otp_forms.rb
index 7e8723de173..38a798dbe38 100644
--- a/spec/support/shared_examples_for_otp_forms.rb
+++ b/spec/support/shared_examples_for_otp_forms.rb
@@ -8,7 +8,7 @@
describe 'tertiary form actions' do
it 'allows the user to cancel out of the sign in process' do
render
- expect(rendered).to have_link(t('links.cancel'), href: destroy_user_session_path)
+ expect(rendered).to have_link(t('links.cancel'), href: sign_out_path)
end
end
end
diff --git a/spec/support/shared_examples_for_phone_validation.rb b/spec/support/shared_examples_for_phone_validation.rb
index a6527b2831b..ac2686f021a 100644
--- a/spec/support/shared_examples_for_phone_validation.rb
+++ b/spec/support/shared_examples_for_phone_validation.rb
@@ -24,19 +24,25 @@
allow(User).to receive(:exists?).with(email: 'new@gmail.com').and_return(false)
allow(User).to receive(:exists?).with(phone: second_user.phone).and_return(true)
- expect(subject.submit(phone: second_user.phone)).to be true
+ result = subject.submit(phone: second_user.phone)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to eq(true)
end
end
context 'when phone is not already taken' do
it 'is valid' do
- expect(subject.submit(phone: '+1 (703) 555-1212')).to be true
+ result = subject.submit(phone: '+1 (703) 555-1212')
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be true
end
end
context 'when phone is same as current user' do
it 'is valid' do
- expect(subject.submit(phone: user.phone)).to be true
+ result = subject.submit(phone: user.phone)
+ expect(result).to be_kind_of(FormResponse)
+ expect(result.success?).to be true
end
end
end
diff --git a/spec/views/devise/mailer/confirmation_instructions.html.slim_spec.rb b/spec/views/devise/mailer/confirmation_instructions.html.slim_spec.rb
index 5d27b671432..b5900800675 100644
--- a/spec/views/devise/mailer/confirmation_instructions.html.slim_spec.rb
+++ b/spec/views/devise/mailer/confirmation_instructions.html.slim_spec.rb
@@ -26,6 +26,21 @@
)
end
+ context 'in a non-default locale' do
+ before { assign(:locale, 'fr') }
+
+ it 'puts the locale in the URL' do
+ assign(:resource, build_stubbed(:user, confirmed_at: Time.zone.now))
+ assign(:token, 'foo')
+ render
+
+ expect(rendered).to have_link(
+ 'http://test.host/fr/sign_up/email/confirm?confirmation_token=foo',
+ href: 'http://test.host/fr/sign_up/email/confirm?confirmation_token=foo'
+ )
+ end
+ end
+
it 'mentions updating an account when user has already been confirmed' do
user = build_stubbed(:user, confirmed_at: Time.zone.now)
presenter = ConfirmationEmailPresenter.new(user, self)
diff --git a/spec/views/devise/passwords/new.html.slim_spec.rb b/spec/views/devise/passwords/new.html.slim_spec.rb
index 2cf85198c02..d6669688339 100644
--- a/spec/views/devise/passwords/new.html.slim_spec.rb
+++ b/spec/views/devise/passwords/new.html.slim_spec.rb
@@ -1,12 +1,21 @@
require 'rails_helper'
describe 'devise/passwords/new.html.slim' do
- let(:user) { build_stubbed(:user) }
-
before do
@password_reset_email_form = PasswordResetEmailForm.new('')
-
- allow(view).to receive(:current_user).and_return(user)
+ sp = build_stubbed(
+ :service_provider,
+ friendly_name: 'Awesome Application!',
+ return_to_sp_url: 'www.awesomeness.com'
+ )
+ view_context = ActionController::Base.new.view_context
+ @decorated_session = DecoratedSession.new(
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
+ ).call
+ allow(view).to receive(:decorated_session).and_return(@decorated_session)
end
it 'has a localized title' do
@@ -26,4 +35,10 @@
expect(rendered).to have_xpath("//form[@autocomplete='off']")
end
+
+ it 'has a cancel link that points to the decorated_session cancel_link_path' do
+ render
+
+ expect(rendered).to have_link(t('links.cancel'), href: @decorated_session.cancel_link_path)
+ end
end
diff --git a/spec/views/devise/sessions/new.html.slim_spec.rb b/spec/views/devise/sessions/new.html.slim_spec.rb
index a5f5855b860..4fce885fbcd 100644
--- a/spec/views/devise/sessions/new.html.slim_spec.rb
+++ b/spec/views/devise/sessions/new.html.slim_spec.rb
@@ -54,7 +54,10 @@
)
view_context = ActionController::Base.new.view_context
@decorated_session = DecoratedSession.new(
- sp: sp, view_context: view_context, sp_session: {}
+ sp: sp,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
).call
allow(view).to receive(:decorated_session).and_return(@decorated_session)
end
diff --git a/spec/views/layouts/application.html.slim_spec.rb b/spec/views/layouts/application.html.slim_spec.rb
index d52f33d2766..ed2129717fa 100644
--- a/spec/views/layouts/application.html.slim_spec.rb
+++ b/spec/views/layouts/application.html.slim_spec.rb
@@ -6,7 +6,12 @@
before do
allow(view).to receive(:user_fully_authenticated?).and_return(true)
allow(view).to receive(:decorated_session).and_return(
- DecoratedSession.new(sp: nil, view_context: nil, sp_session: {}).call
+ DecoratedSession.new(
+ sp: nil,
+ view_context: nil,
+ sp_session: {},
+ service_provider_request: ServiceProviderRequest.new
+ ).call
)
allow(view.request).to receive(:original_url).and_return('http://test.host/foobar')
allow(view).to receive(:current_user).and_return(User.new)
@@ -76,7 +81,12 @@
allow(view).to receive(:current_user).and_return(nil)
allow(view).to receive(:user_fully_authenticated?).and_return(false)
allow(view).to receive(:decorated_session).and_return(
- DecoratedSession.new(sp: nil, view_context: nil, sp_session: {}).call
+ DecoratedSession.new(
+ sp: nil,
+ view_context: nil,
+ sp_session: {},
+ service_provider_request: nil
+ ).call
)
allow(Figaro.env).to receive(:participate_in_dap).and_return('true')
diff --git a/spec/views/shared/_nav_branded.html.slim_spec.rb b/spec/views/shared/_nav_branded.html.slim_spec.rb
index 99a28197491..4a296cb6605 100644
--- a/spec/views/shared/_nav_branded.html.slim_spec.rb
+++ b/spec/views/shared/_nav_branded.html.slim_spec.rb
@@ -9,7 +9,10 @@
:service_provider, logo: 'generic.svg', friendly_name: 'Best SP ever'
)
decorated_session = ServiceProviderSessionDecorator.new(
- sp: sp_with_logo, view_context: view_context, sp_session: {}
+ sp: sp_with_logo,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: nil
)
allow(view).to receive(:decorated_session).and_return(decorated_session)
render
@@ -24,7 +27,10 @@
before do
sp_without_logo = build_stubbed(:service_provider, friendly_name: 'No logo no problem')
decorated_session = ServiceProviderSessionDecorator.new(
- sp: sp_without_logo, view_context: view_context, sp_session: {}
+ sp: sp_without_logo,
+ view_context: view_context,
+ sp_session: {},
+ service_provider_request: nil
)
allow(view).to receive(:decorated_session).and_return(decorated_session)
render
diff --git a/spec/views/sign_up/registrations/new.html.slim_spec.rb b/spec/views/sign_up/registrations/new.html.slim_spec.rb
index 5d13ea5fbe1..54828b53f32 100644
--- a/spec/views/sign_up/registrations/new.html.slim_spec.rb
+++ b/spec/views/sign_up/registrations/new.html.slim_spec.rb
@@ -9,7 +9,7 @@
view_context = ActionController::Base.new.view_context
@decorated_session = DecoratedSession.new(
- sp: nil, view_context: view_context, sp_session: {}
+ sp: nil, view_context: view_context, sp_session: {}, service_provider_request: nil
).call
allow(view).to receive(:decorated_session).and_return(@decorated_session)
end
diff --git a/spec/views/sign_up/registrations/show.html.slim_spec.rb b/spec/views/sign_up/registrations/show.html.slim_spec.rb
index a64fdea7183..ec5c3bd8eaf 100644
--- a/spec/views/sign_up/registrations/show.html.slim_spec.rb
+++ b/spec/views/sign_up/registrations/show.html.slim_spec.rb
@@ -41,7 +41,7 @@
)
view_context = ActionController::Base.new.view_context
@decorated_session = DecoratedSession.new(
- sp: @sp, view_context: view_context, sp_session: {}
+ sp: @sp, view_context: view_context, sp_session: {}, service_provider_request: nil
).call
allow(view).to receive(:decorated_session).and_return(@decorated_session)
end
diff --git a/spec/views/verify/fail.html.slim_spec.rb b/spec/views/verify/fail.html.slim_spec.rb
index a2b80771bc3..404a7ea0d0d 100644
--- a/spec/views/verify/fail.html.slim_spec.rb
+++ b/spec/views/verify/fail.html.slim_spec.rb
@@ -7,7 +7,7 @@
before do
sp = build_stubbed(:service_provider, friendly_name: 'Awesome Application!')
@decorated_session = ServiceProviderSessionDecorator.new(
- sp: sp, view_context: view_context, sp_session: {}
+ sp: sp, view_context: view_context, sp_session: {}, service_provider_request: nil
)
allow(view).to receive(:decorated_session).and_return(@decorated_session)
end
diff --git a/spec/views/verify/review/new.html.slim_spec.rb b/spec/views/verify/review/new.html.slim_spec.rb
index 258c9d201da..3ffe21b10a1 100644
--- a/spec/views/verify/review/new.html.slim_spec.rb
+++ b/spec/views/verify/review/new.html.slim_spec.rb
@@ -48,7 +48,8 @@
end
it 'renders a link telling user why financial info is not visible' do
- expect(rendered).to have_link t('idv.messages.review.financial_info')
+ expect(rendered).
+ to have_link(t('idv.messages.review.financial_info'), href: MarketingSite.help_url)
end
end
end
diff --git a/spec/views/verify/usps/index.html.slim_spec.rb b/spec/views/verify/usps/index.html.slim_spec.rb
new file mode 100644
index 00000000000..c72e0b65701
--- /dev/null
+++ b/spec/views/verify/usps/index.html.slim_spec.rb
@@ -0,0 +1,18 @@
+require 'rails_helper'
+
+describe 'verify/usps/index.html.slim' do
+ it 'calls UspsDecorator#title and #button' do
+ user = build_stubbed(:user, :signed_up)
+ usps_mail_service = Idv::UspsMail.new(user)
+
+ usps_decorator = instance_double(UspsDecorator)
+ allow(UspsDecorator).to receive(:new).with(usps_mail_service).
+ and_return(usps_decorator)
+ @decorated_usps = usps_decorator
+
+ expect(usps_decorator).to receive(:title)
+ expect(usps_decorator).to receive(:button)
+
+ render
+ end
+end