From 9fc64a2e8d24df9cc1d2daa49209f27c0c2a75fe Mon Sep 17 00:00:00 2001 From: Andrew Duthie <1779930+aduth@users.noreply.github.com> Date: Mon, 7 Oct 2024 15:15:29 -0400 Subject: [PATCH 01/12] LG-14389: Add analytics for consent screen email selection (#11321) * LG-14389: Add analytics for consent screen email selection changelog: Upcoming Features, Partner Email Selection, Add analytics for consent screen email selection * Include needs_completion_screen_reason for submission event --- .../sign_up/select_email_controller.rb | 4 ++ .../sign_up/select_email_controller_spec.rb | 56 ++++++++++++++----- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/app/controllers/sign_up/select_email_controller.rb b/app/controllers/sign_up/select_email_controller.rb index e5daaf110de..48e0ff0a821 100644 --- a/app/controllers/sign_up/select_email_controller.rb +++ b/app/controllers/sign_up/select_email_controller.rb @@ -13,12 +13,16 @@ def show @user_emails = user_emails @last_sign_in_email_address = last_email @select_email_form = build_select_email_form + analytics.sp_select_email_visited(needs_completion_screen_reason:) end def create @select_email_form = build_select_email_form result = @select_email_form.submit(form_params) + + analytics.sp_select_email_submitted(**result.to_h, needs_completion_screen_reason:) + if result.success? user_session[:selected_email_id] = form_params[:selected_email_id] redirect_to sign_up_completed_path diff --git a/spec/controllers/sign_up/select_email_controller_spec.rb b/spec/controllers/sign_up/select_email_controller_spec.rb index 8c0f0b63605..8257d6f127d 100644 --- a/spec/controllers/sign_up/select_email_controller_spec.rb +++ b/spec/controllers/sign_up/select_email_controller_spec.rb @@ -1,7 +1,7 @@ require 'rails_helper' RSpec.describe SignUp::SelectEmailController do - let(:user) { create(:user) } + let(:user) { create(:user, :with_multiple_emails) } let(:sp) { create(:service_provider) } before do @@ -29,6 +29,17 @@ describe '#show' do subject(:response) { get :show } + it 'logs analytics event' do + stub_analytics + + response + + expect(@analytics).to have_logged_event( + :sp_select_email_visited, + needs_completion_screen_reason: :new_attributes, + ) + end + it 'assigns view variables' do response @@ -51,32 +62,49 @@ end describe '#create' do - let(:email) { 'michael.motorist@email.com' } - let(:email2) { 'michael.motorist2@email.com' } - let(:email3) { 'david.motorist@email.com' } - let(:params) { { selected_email_id: email2 } } + let(:selected_email) { user.confirmed_email_addresses.sample } + let(:params) { { select_email_form: { selected_email_id: selected_email.id } } } subject(:response) { post :create, params: params } - before do - create(:email_address, user:, email: email) - create(:email_address, user:, email: email2) + it 'updates selected email address' do + response + + expect(controller.user_session[:selected_email_id]).to eq(selected_email.id.to_s) end - it 'updates selected email address' do + it 'logs analytics event' do + stub_analytics + response - expect(user.email_addresses.last.email). - to include('michael.motorist2@email.com') + expect(@analytics).to have_logged_event( + :sp_select_email_submitted, + success: true, + needs_completion_screen_reason: :new_attributes, + ) end context 'with a corrupted email selected_email_id form' do - let(:params) { { selected_email_id: email3 } } + let(:other_user) { create(:user) } + let(:selected_email) { other_user.confirmed_email_addresses.sample } it 'rejects email not belonging to the user' do expect(response).to redirect_to(sign_up_select_email_path) - expect(user.email_addresses.last.email). - to include('michael.motorist2@email.com') + expect(controller.user_session[:selected_email_id]).to eq(nil) + end + + it 'logs analytics event' do + stub_analytics + + response + + expect(@analytics).to have_logged_event( + :sp_select_email_submitted, + success: false, + error_details: { selected_email_id: { not_found: true } }, + needs_completion_screen_reason: :new_attributes, + ) end end From bfb9174cee3fe575ffcb2183b8dbf82d5760b56d Mon Sep 17 00:00:00 2001 From: Samatha Dondeti Date: Tue, 8 Oct 2024 05:39:40 -0700 Subject: [PATCH 02/12] Lg 14425 pii column tag (#11316) * branch sharing * migration for comments * rake task * lg-14425 sensitive column updates changelog: Internal, Reporting,sensitive tag to all columns * delete yml file * update to spec * capture at execution * remove abort * update tests to exit * update tests to exit * update test output --------- Co-authored-by: Aaron Nagucki --- ...41001193936_add_comments_to_all_columns.rb | 469 ++++++++++ db/schema.rb | 848 +++++++++--------- lib/tasks/column_comment_checker.rake | 31 + spec/db/schema_spec.rb | 30 + 4 files changed, 954 insertions(+), 424 deletions(-) create mode 100644 db/primary_migrate/20241001193936_add_comments_to_all_columns.rb create mode 100644 lib/tasks/column_comment_checker.rake create mode 100644 spec/db/schema_spec.rb diff --git a/db/primary_migrate/20241001193936_add_comments_to_all_columns.rb b/db/primary_migrate/20241001193936_add_comments_to_all_columns.rb new file mode 100644 index 00000000000..7d26b3b4fb9 --- /dev/null +++ b/db/primary_migrate/20241001193936_add_comments_to_all_columns.rb @@ -0,0 +1,469 @@ +class AddCommentsToAllColumns < ActiveRecord::Migration[7.1] + def change + ### update known sensitivity flag for table: letter_requests_to_usps_ftp_logs + change_column_comment :letter_requests_to_usps_ftp_logs, :ftp_at, from: "", to: "sensitive=false" + change_column_comment :letter_requests_to_usps_ftp_logs, :letter_requests_count, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: identities + change_column_comment :identities, :service_provider, from: "", to: "sensitive=false" + change_column_comment :identities, :last_authenticated_at, from: "", to: "sensitive=false" + change_column_comment :identities, :user_id, from: "", to: "sensitive=false" + change_column_comment :identities, :created_at, from: "", to: "sensitive=false" + change_column_comment :identities, :updated_at, from: "", to: "sensitive=false" + change_column_comment :identities, :session_uuid, from: "", to: "sensitive=true" + change_column_comment :identities, :uuid, from: "", to: "sensitive=false" + change_column_comment :identities, :nonce, from: "", to: "sensitive=false" + change_column_comment :identities, :ial, from: "", to: "sensitive=false" + change_column_comment :identities, :access_token, from: "", to: "sensitive=true" + change_column_comment :identities, :scope, from: "", to: "sensitive=false" + change_column_comment :identities, :code_challenge, from: "", to: "sensitive=true" + change_column_comment :identities, :rails_session_id, from: "", to: "sensitive=true" + change_column_comment :identities, :verified_attributes, from: "", to: "sensitive=false" + change_column_comment :identities, :verified_at, from: "", to: "sensitive=false" + change_column_comment :identities, :last_consented_at, from: "", to: "sensitive=false" + change_column_comment :identities, :last_ial1_authenticated_at, from: "", to: "sensitive=false" + change_column_comment :identities, :last_ial2_authenticated_at, from: "", to: "sensitive=false" + change_column_comment :identities, :deleted_at, from: "", to: "sensitive=false" + change_column_comment :identities, :aal, from: "", to: "sensitive=false" + change_column_comment :identities, :requested_aal_value, from: "", to: "sensitive=false" + change_column_comment :identities, :vtr, from: "", to: "sensitive=false" + change_column_comment :identities, :acr_values, from: "", to: "sensitive=false" + change_column_comment :identities, :email_address_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: profiles + change_column_comment :profiles, :user_id, from: "", to: "sensitive=false" + change_column_comment :profiles, :active, from: "", to: "sensitive=false" + change_column_comment :profiles, :verified_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :activated_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :created_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :updated_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :encrypted_pii, from: "", to: "sensitive=true" + change_column_comment :profiles, :ssn_signature, from: "", to: "sensitive=true" + change_column_comment :profiles, :encrypted_pii_recovery, from: "", to: "sensitive=true" + change_column_comment :profiles, :deactivation_reason, from: "", to: "sensitive=false" + change_column_comment :profiles, :proofing_components, from: "", to: "sensitive=false" + change_column_comment :profiles, :name_zip_birth_year_signature, from: "", to: "sensitive=true" + change_column_comment :profiles, :initiating_service_provider_issuer, from: "", to: "sensitive=false" + change_column_comment :profiles, :fraud_review_pending_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :fraud_rejection_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :gpo_verification_pending_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :fraud_pending_reason, from: "", to: "sensitive=false" + change_column_comment :profiles, :in_person_verification_pending_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :encrypted_pii_multi_region, from: "", to: "sensitive=false" + change_column_comment :profiles, :encrypted_pii_recovery_multi_region, from: "", to: "sensitive=false" + change_column_comment :profiles, :gpo_verification_expired_at, from: "", to: "sensitive=false" + change_column_comment :profiles, :idv_level, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: users + change_column_comment :users, :reset_password_token, from: "", to: "sensitive=true" + change_column_comment :users, :reset_password_sent_at, from: "", to: "sensitive=false" + change_column_comment :users, :created_at, from: "", to: "sensitive=false" + change_column_comment :users, :updated_at, from: "", to: "sensitive=false" + change_column_comment :users, :confirmed_at, from: "", to: "sensitive=false" + change_column_comment :users, :second_factor_attempts_count, from: "", to: "sensitive=false" + change_column_comment :users, :uuid, from: "", to: "sensitive=false" + change_column_comment :users, :second_factor_locked_at, from: "", to: "sensitive=false" + change_column_comment :users, :phone_confirmed_at, from: "", to: "sensitive=false" + change_column_comment :users, :direct_otp, from: "", to: "sensitive=true" + change_column_comment :users, :direct_otp_sent_at, from: "", to: "sensitive=false" + change_column_comment :users, :unique_session_id, from: "", to: "sensitive=false" + change_column_comment :users, :otp_delivery_preference, from: "", to: "sensitive=false" + change_column_comment :users, :encrypted_password_digest, from: "", to: "sensitive=true" + change_column_comment :users, :encrypted_recovery_code_digest, from: "", to: "sensitive=true" + change_column_comment :users, :remember_device_revoked_at, from: "", to: "sensitive=false" + change_column_comment :users, :email_language, from: "", to: "sensitive=false" + change_column_comment :users, :accepted_terms_at, from: "", to: "sensitive=false" + change_column_comment :users, :encrypted_recovery_code_digest_generated_at, from: "", to: "sensitive=false" + change_column_comment :users, :suspended_at, from: "", to: "sensitive=false" + change_column_comment :users, :reinstated_at, from: "", to: "sensitive=false" + change_column_comment :users, :encrypted_password_digest_multi_region, from: "", to: "sensitive=false" + change_column_comment :users, :encrypted_recovery_code_digest_multi_region, from: "", to: "sensitive=false" + change_column_comment :users, :second_mfa_reminder_dismissed_at, from: "", to: "sensitive=false" + change_column_comment :users, :piv_cac_recommended_dismissed_at, from: "", to: "sensitive=false" + change_column_comment :users, :sign_in_new_device_at, from: "", to: "sensitive=false" + change_column_comment :users, :password_compromised_checked_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: events + change_column_comment :events, :user_id, from: "", to: "sensitive=false" + change_column_comment :events, :event_type, from: "", to: "sensitive=false" + change_column_comment :events, :created_at, from: "", to: "sensitive=false" + change_column_comment :events, :updated_at, from: "", to: "sensitive=false" + change_column_comment :events, :device_id, from: "", to: "sensitive=false" + change_column_comment :events, :ip, from: "", to: "sensitive=false" + change_column_comment :events, :disavowed_at, from: "", to: "sensitive=false" + change_column_comment :events, :disavowal_token_fingerprint, from: "", to: "sensitive=true" + ### update known sensitivity flag for table: usps_confirmation_codes + change_column_comment :usps_confirmation_codes, :profile_id, from: "", to: "sensitive=false" + change_column_comment :usps_confirmation_codes, :otp_fingerprint, from: "", to: "sensitive=true" + change_column_comment :usps_confirmation_codes, :code_sent_at, from: "", to: "sensitive=false" + change_column_comment :usps_confirmation_codes, :created_at, from: "", to: "sensitive=false" + change_column_comment :usps_confirmation_codes, :updated_at, from: "", to: "sensitive=false" + change_column_comment :usps_confirmation_codes, :reminder_sent_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: agency_identities + change_column_comment :agency_identities, :user_id, from: "", to: "sensitive=false" + change_column_comment :agency_identities, :agency_id, from: "", to: "sensitive=false" + change_column_comment :agency_identities, :uuid, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: agencies + change_column_comment :agencies, :name, from: "", to: "sensitive=false" + change_column_comment :agencies, :abbreviation, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: usps_confirmations + change_column_comment :usps_confirmations, :entry, from: "", to: "sensitive=false" + change_column_comment :usps_confirmations, :created_at, from: "", to: "sensitive=false" + change_column_comment :usps_confirmations, :updated_at, from: "", to: "sensitive=false" + change_column_comment :usps_confirmations, :entry_multi_region, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: account_reset_requests + change_column_comment :account_reset_requests, :user_id, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :requested_at, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :request_token, from: "", to: "sensitive=true" + change_column_comment :account_reset_requests, :cancelled_at, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :granted_at, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :granted_token, from: "", to: "sensitive=true" + change_column_comment :account_reset_requests, :created_at, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :updated_at, from: "", to: "sensitive=false" + change_column_comment :account_reset_requests, :requesting_issuer, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: email_addresses + change_column_comment :email_addresses, :user_id, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :confirmation_token, from: "", to: "sensitive=true" + change_column_comment :email_addresses, :confirmed_at, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :confirmation_sent_at, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :email_fingerprint, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :encrypted_email, from: "", to: "sensitive=true" + change_column_comment :email_addresses, :created_at, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :updated_at, from: "", to: "sensitive=false" + change_column_comment :email_addresses, :last_sign_in_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: devices + change_column_comment :devices, :user_id, from: "", to: "sensitive=false" + change_column_comment :devices, :cookie_uuid, from: "", to: "sensitive=false" + change_column_comment :devices, :user_agent, from: "", to: "sensitive=false" + change_column_comment :devices, :last_used_at, from: "", to: "sensitive=false" + change_column_comment :devices, :last_ip, from: "", to: "sensitive=false" + change_column_comment :devices, :created_at, from: "", to: "sensitive=false" + change_column_comment :devices, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: backup_code_configurations + change_column_comment :backup_code_configurations, :user_id, from: "", to: "sensitive=false" + change_column_comment :backup_code_configurations, :used_at, from: "", to: "sensitive=false" + change_column_comment :backup_code_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :backup_code_configurations, :updated_at, from: "", to: "sensitive=false" + change_column_comment :backup_code_configurations, :salted_code_fingerprint, from: "", to: "sensitive=false" + change_column_comment :backup_code_configurations, :code_salt, from: "", to: "sensitive=true" + change_column_comment :backup_code_configurations, :code_cost, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: webauthn_configurations + change_column_comment :webauthn_configurations, :user_id, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :name, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :credential_id, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :credential_public_key, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :updated_at, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :platform_authenticator, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :transports, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :authenticator_data_flags, from: "", to: "sensitive=false" + change_column_comment :webauthn_configurations, :aaguid, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: phone_configurations + change_column_comment :phone_configurations, :user_id, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :encrypted_phone, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :delivery_preference, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :mfa_enabled, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :confirmation_sent_at, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :confirmed_at, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :updated_at, from: "", to: "sensitive=false" + change_column_comment :phone_configurations, :made_default_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: registration_logs + change_column_comment :registration_logs, :user_id, from: "", to: "sensitive=false" + change_column_comment :registration_logs, :registered_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: sp_return_logs + change_column_comment :sp_return_logs, :requested_at, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :request_id, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :ial, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :issuer, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :user_id, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :returned_at, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :billable, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :profile_id, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :profile_verified_at, from: "", to: "sensitive=false" + change_column_comment :sp_return_logs, :profile_requested_issuer, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: piv_cac_configurations + change_column_comment :piv_cac_configurations, :user_id, from: "", to: "sensitive=false" + change_column_comment :piv_cac_configurations, :x509_dn_uuid, from: "", to: "sensitive=false" + change_column_comment :piv_cac_configurations, :name, from: "", to: "sensitive=false" + change_column_comment :piv_cac_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :piv_cac_configurations, :updated_at, from: "", to: "sensitive=false" + change_column_comment :piv_cac_configurations, :x509_issuer, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: auth_app_configurations + change_column_comment :auth_app_configurations, :user_id, from: "", to: "sensitive=false" + change_column_comment :auth_app_configurations, :encrypted_otp_secret_key, from: "", to: "sensitive=true" + change_column_comment :auth_app_configurations, :name, from: "", to: "sensitive=false" + change_column_comment :auth_app_configurations, :totp_timestamp, from: "", to: "sensitive=false" + change_column_comment :auth_app_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :auth_app_configurations, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: deleted_users + change_column_comment :deleted_users, :user_id, from: "", to: "sensitive=false" + change_column_comment :deleted_users, :uuid, from: "", to: "sensitive=false" + change_column_comment :deleted_users, :user_created_at, from: "", to: "sensitive=false" + change_column_comment :deleted_users, :deleted_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: service_providers + change_column_comment :service_providers, :issuer, from: "", to: "sensitive=false" + change_column_comment :service_providers, :friendly_name, from: "", to: "sensitive=false" + change_column_comment :service_providers, :description, from: "", to: "sensitive=false" + change_column_comment :service_providers, :metadata_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :acs_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :assertion_consumer_logout_service_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :logo, from: "", to: "sensitive=false" + change_column_comment :service_providers, :signature, from: "", to: "sensitive=false" + change_column_comment :service_providers, :block_encryption, from: "", to: "sensitive=true" + change_column_comment :service_providers, :sp_initiated_login_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :return_to_sp_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :attribute_bundle, from: "", to: "sensitive=false" + change_column_comment :service_providers, :created_at, from: "", to: "sensitive=false" + change_column_comment :service_providers, :updated_at, from: "", to: "sensitive=false" + change_column_comment :service_providers, :active, from: "", to: "sensitive=false" + change_column_comment :service_providers, :approved, from: "", to: "sensitive=false" + change_column_comment :service_providers, :native, from: "", to: "sensitive=false" + change_column_comment :service_providers, :redirect_uris, from: "", to: "sensitive=false" + change_column_comment :service_providers, :agency_id, from: "", to: "sensitive=false" + change_column_comment :service_providers, :failure_to_proof_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :ial, from: "", to: "sensitive=false" + change_column_comment :service_providers, :piv_cac, from: "", to: "sensitive=false" + change_column_comment :service_providers, :piv_cac_scoped_by_email, from: "", to: "sensitive=false" + change_column_comment :service_providers, :pkce, from: "", to: "sensitive=false" + change_column_comment :service_providers, :push_notification_url, from: "", to: "sensitive=false" + change_column_comment :service_providers, :help_text, from: "", to: "sensitive=false" + change_column_comment :service_providers, :allow_prompt_login, from: "", to: "sensitive=false" + change_column_comment :service_providers, :signed_response_message_requested, from: "", to: "sensitive=false" + change_column_comment :service_providers, :remote_logo_key, from: "", to: "sensitive=false" + change_column_comment :service_providers, :launch_date, from: "", to: "sensitive=false" + change_column_comment :service_providers, :iaa, from: "", to: "sensitive=false" + change_column_comment :service_providers, :iaa_start_date, from: "", to: "sensitive=false" + change_column_comment :service_providers, :iaa_end_date, from: "", to: "sensitive=false" + change_column_comment :service_providers, :app_id, from: "", to: "sensitive=false" + change_column_comment :service_providers, :default_aal, from: "", to: "sensitive=false" + change_column_comment :service_providers, :certs, from: "", to: "sensitive=false" + change_column_comment :service_providers, :email_nameid_format_allowed, from: "", to: "sensitive=false" + change_column_comment :service_providers, :use_legacy_name_id_behavior, from: "", to: "sensitive=false" + change_column_comment :service_providers, :irs_attempts_api_enabled, from: "", to: "sensitive=false" + change_column_comment :service_providers, :in_person_proofing_enabled, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: security_events + change_column_comment :security_events, :user_id, from: "", to: "sensitive=false" + change_column_comment :security_events, :event_type, from: "", to: "sensitive=false" + change_column_comment :security_events, :jti, from: "", to: "sensitive=false" + change_column_comment :security_events, :issuer, from: "", to: "sensitive=false" + change_column_comment :security_events, :created_at, from: "", to: "sensitive=false" + change_column_comment :security_events, :updated_at, from: "", to: "sensitive=false" + change_column_comment :security_events, :occurred_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: sp_costs + change_column_comment :sp_costs, :issuer, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :agency_id, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :cost_type, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :created_at, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :updated_at, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :ial, from: "", to: "sensitive=false" + change_column_comment :sp_costs, :transaction_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: partner_accounts + change_column_comment :partner_accounts, :name, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :description, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :requesting_agency, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :became_partner, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :agency_id, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :partner_account_status_id, from: "", to: "sensitive=false" + change_column_comment :partner_accounts, :crm_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: partner_account_statuses + change_column_comment :partner_account_statuses, :name, from: "", to: "sensitive=false" + change_column_comment :partner_account_statuses, :order, from: "", to: "sensitive=false" + change_column_comment :partner_account_statuses, :partner_name, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: doc_auth_logs + change_column_comment :doc_auth_logs, :user_id, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :welcome_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :welcome_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :upload_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :upload_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :link_sent_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :link_sent_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :front_image_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :front_image_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :front_image_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :front_image_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :back_image_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :back_image_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :back_image_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :back_image_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_front_image_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_front_image_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_back_image_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_back_image_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :ssn_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :ssn_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_phone_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_phone_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :usps_address_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :usps_address_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :encrypt_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :encrypt_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verified_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verified_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :created_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :updated_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_front_image_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_front_image_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_back_image_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_back_image_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :usps_letter_sent_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :usps_letter_sent_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_mobile_back_image_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_mobile_back_image_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_complete_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_complete_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_mobile_back_image_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_mobile_back_image_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :no_sp_session_started_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :choose_method_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :choose_method_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :present_cac_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :present_cac_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :present_cac_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :present_cac_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :enter_info_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :enter_info_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :success_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :success_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :selfie_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :selfie_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :selfie_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :issuer, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :last_document_error, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :document_capture_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :document_capture_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :document_capture_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :document_capture_error_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :agreement_view_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :agreement_view_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :state, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_submit_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_phone_submit_count, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :verify_phone_submit_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :document_capture_submit_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :back_image_submit_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :capture_mobile_back_image_submit_at, from: "", to: "sensitive=false" + change_column_comment :doc_auth_logs, :mobile_back_image_submit_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: document_capture_sessions + change_column_comment :document_capture_sessions, :uuid, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :result_id, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :user_id, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :created_at, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :updated_at, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :requested_at, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :issuer, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :cancelled_at, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :ocr_confirmation_pending, from: "", to: "sensitive=false" + change_column_comment :document_capture_sessions, :last_doc_auth_result, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: integrations + change_column_comment :integrations, :issuer, from: "", to: "sensitive=false" + change_column_comment :integrations, :name, from: "", to: "sensitive=false" + change_column_comment :integrations, :dashboard_identifier, from: "", to: "sensitive=false" + change_column_comment :integrations, :partner_account_id, from: "", to: "sensitive=false" + change_column_comment :integrations, :integration_status_id, from: "", to: "sensitive=false" + change_column_comment :integrations, :service_provider_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: integration_statuses + change_column_comment :integration_statuses, :name, from: "", to: "sensitive=false" + change_column_comment :integration_statuses, :order, from: "", to: "sensitive=false" + change_column_comment :integration_statuses, :partner_name, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: integration_usages + change_column_comment :integration_usages, :iaa_order_id, from: "", to: "sensitive=false" + change_column_comment :integration_usages, :integration_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: iaa_gtcs + change_column_comment :iaa_gtcs, :gtc_number, from: "", to: "sensitive=false" + change_column_comment :iaa_gtcs, :mod_number, from: "", to: "sensitive=false" + change_column_comment :iaa_gtcs, :start_date, from: "", to: "sensitive=false" + change_column_comment :iaa_gtcs, :end_date, from: "", to: "sensitive=false" + change_column_comment :iaa_gtcs, :estimated_amount, from: "", to: "sensitive=false" + change_column_comment :iaa_gtcs, :partner_account_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: iaa_orders + change_column_comment :iaa_orders, :order_number, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :mod_number, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :start_date, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :end_date, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :estimated_amount, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :pricing_model, from: "", to: "sensitive=false" + change_column_comment :iaa_orders, :iaa_gtc_id, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: sign_in_restrictions + change_column_comment :sign_in_restrictions, :user_id, from: "", to: "sensitive=false" + change_column_comment :sign_in_restrictions, :service_provider, from: "", to: "sensitive=false" + change_column_comment :sign_in_restrictions, :created_at, from: "", to: "sensitive=false" + change_column_comment :sign_in_restrictions, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: phone_number_opt_outs + change_column_comment :phone_number_opt_outs, :encrypted_phone, from: "", to: "sensitive=false" + change_column_comment :phone_number_opt_outs, :phone_fingerprint, from: "", to: "sensitive=false" + change_column_comment :phone_number_opt_outs, :uuid, from: "", to: "sensitive=false" + change_column_comment :phone_number_opt_outs, :created_at, from: "", to: "sensitive=false" + change_column_comment :phone_number_opt_outs, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: proofing_components + change_column_comment :proofing_components, :user_id, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :document_check, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :document_type, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :source_check, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :resolution_check, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :address_check, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :verified_at, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :created_at, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :updated_at, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :liveness_check, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :device_fingerprinting_vendor, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :threatmetrix, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :threatmetrix_review_status, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :threatmetrix_risk_rating, from: "", to: "sensitive=false" + change_column_comment :proofing_components, :threatmetrix_policy_score, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: fraud_review_requests + change_column_comment :fraud_review_requests, :user_id, from: "", to: "sensitive=false" + change_column_comment :fraud_review_requests, :uuid, from: "", to: "sensitive=false" + change_column_comment :fraud_review_requests, :irs_session_id, from: "", to: "sensitive=false" + change_column_comment :fraud_review_requests, :login_session_id, from: "", to: "sensitive=false" + change_column_comment :fraud_review_requests, :created_at, from: "", to: "sensitive=false" + change_column_comment :fraud_review_requests, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: in_person_enrollments + change_column_comment :in_person_enrollments, :user_id, from: "Foreign key to the user this enrollment belongs to", to: "Foreign key to the user this enrollment belongs to sensitive=false" + change_column_comment :in_person_enrollments, :profile_id, from: "Foreign key to the profile this enrollment belongs to", to: "Foreign key to the profile this enrollment belongs to sensitive=false" + change_column_comment :in_person_enrollments, :enrollment_code, from: "The code returned by the USPS service", to: "The code returned by the USPS service sensitive=false" + change_column_comment :in_person_enrollments, :status_check_attempted_at, from: "The last time a status check was attempted", to: "The last time a status check was attempted sensitive=false" + change_column_comment :in_person_enrollments, :status_updated_at, from: "The last time the status was successfully updated with a value from the USPS API", to: "The last time the status was successfully updated with a value from the USPS API sensitive=false" + change_column_comment :in_person_enrollments, :status, from: "The status of the enrollment", to: "The status of the enrollment sensitive=false" + change_column_comment :in_person_enrollments, :created_at, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :updated_at, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :current_address_matches_id, from: "True if the user indicates that their current address matches the address on the ID they're bringing to the Post Office.", to: "True if the user indicates that their current address matches the address on the ID they're bringing to the Post Office. sensitive=false" + change_column_comment :in_person_enrollments, :selected_location_details, from: "The location details of the Post Office the user selected (including title, address, hours of operation)", to: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=false" + change_column_comment :in_person_enrollments, :unique_id, from: "Unique ID to use with the USPS service", to: "Unique ID to use with the USPS service sensitive=false" + change_column_comment :in_person_enrollments, :enrollment_established_at, from: "When the enrollment was successfully established", to: "When the enrollment was successfully established sensitive=false" + change_column_comment :in_person_enrollments, :issuer, from: "Issuer associated with the enrollment at time of creation", to: "Issuer associated with the enrollment at time of creation sensitive=false" + change_column_comment :in_person_enrollments, :follow_up_survey_sent, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :early_reminder_sent, from: "early reminder to complete IPP before deadline sent", to: "early reminder to complete IPP before deadline sent sensitive=false" + change_column_comment :in_person_enrollments, :late_reminder_sent, from: "late reminder to complete IPP before deadline sent", to: "late reminder to complete IPP before deadline sent sensitive=false" + change_column_comment :in_person_enrollments, :deadline_passed_sent, from: "deadline passed email sent for expired enrollment", to: "deadline passed email sent for expired enrollment sensitive=false" + change_column_comment :in_person_enrollments, :proofed_at, from: "timestamp when user attempted to proof at a Post Office", to: "timestamp when user attempted to proof at a Post Office sensitive=false" + change_column_comment :in_person_enrollments, :capture_secondary_id_enabled, from: "record and proof state ID and residential addresses separately", to: "record and proof state ID and residential addresses separately sensitive=false" + change_column_comment :in_person_enrollments, :status_check_completed_at, from: "The last time a status check was successfully completed", to: "The last time a status check was successfully completed sensitive=false" + change_column_comment :in_person_enrollments, :ready_for_status_check, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :notification_sent_at, from: "The time a notification was sent", to: "The time a notification was sent sensitive=false" + change_column_comment :in_person_enrollments, :last_batch_claimed_at, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :sponsor_id, from: "", to: "sensitive=false" + change_column_comment :in_person_enrollments, :doc_auth_result, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: notification_phone_configurations + change_column_comment :notification_phone_configurations, :in_person_enrollment_id, from: "", to: "sensitive=false" + change_column_comment :notification_phone_configurations, :encrypted_phone, from: "Encrypted phone number to send notifications to", to: "Encrypted phone number to send notifications to sensitive=true" + change_column_comment :notification_phone_configurations, :created_at, from: "", to: "sensitive=false" + change_column_comment :notification_phone_configurations, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: suspended_emails + change_column_comment :suspended_emails, :email_address_id, from: "", to: "sensitive=false" + change_column_comment :suspended_emails, :digested_base_email, from: "", to: "sensitive=false" + change_column_comment :suspended_emails, :created_at, from: "", to: "sensitive=false" + change_column_comment :suspended_emails, :updated_at, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: federal_email_domains + change_column_comment :federal_email_domains, :name, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: disposable_email_domains + change_column_comment :disposable_email_domains, :name, from: "", to: "sensitive=false" + ### update known sensitivity flag for table: sp_upgraded_biometric_profiles + change_column_comment :sp_upgraded_biometric_profiles, :upgraded_at, from: "", to: "sensitive=false" + change_column_comment :sp_upgraded_biometric_profiles, :user_id, from: "", to: "sensitive=false" + change_column_comment :sp_upgraded_biometric_profiles, :idv_level, from: "", to: "sensitive=false" + change_column_comment :sp_upgraded_biometric_profiles, :issuer, from: "", to: "sensitive=false" + change_column_comment :sp_upgraded_biometric_profiles, :created_at, from: "", to: "sensitive=false" + change_column_comment :sp_upgraded_biometric_profiles, :updated_at, from: "", to: "sensitive=false" + end +end diff --git a/db/schema.rb b/db/schema.rb index cd2309f566f..bd6c67d0f62 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,22 +10,22 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_09_16_202940) do +ActiveRecord::Schema[7.1].define(version: 2024_10_01_193936) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pg_stat_statements" enable_extension "plpgsql" create_table "account_reset_requests", force: :cascade do |t| - t.integer "user_id", null: false - t.datetime "requested_at", precision: nil - t.string "request_token" - t.datetime "cancelled_at", precision: nil - t.datetime "granted_at", precision: nil - t.string "granted_token" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.string "requesting_issuer" + t.integer "user_id", null: false, comment: "sensitive=false" + t.datetime "requested_at", precision: nil, comment: "sensitive=false" + t.string "request_token", comment: "sensitive=true" + t.datetime "cancelled_at", precision: nil, comment: "sensitive=false" + t.datetime "granted_at", precision: nil, comment: "sensitive=false" + t.string "granted_token", comment: "sensitive=true" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.string "requesting_issuer", comment: "sensitive=false" t.index ["cancelled_at", "granted_at", "requested_at"], name: "index_account_reset_requests_on_timestamps" t.index ["granted_token"], name: "index_account_reset_requests_on_granted_token", unique: true t.index ["request_token"], name: "index_account_reset_requests_on_request_token", unique: true @@ -33,179 +33,179 @@ end create_table "agencies", force: :cascade do |t| - t.string "name", null: false - t.string "abbreviation" + t.string "name", null: false, comment: "sensitive=false" + t.string "abbreviation", comment: "sensitive=false" t.index ["abbreviation"], name: "index_agencies_on_abbreviation", unique: true t.index ["name"], name: "index_agencies_on_name", unique: true t.check_constraint "abbreviation IS NOT NULL", name: "agencies_abbreviation_null" end create_table "agency_identities", force: :cascade do |t| - t.integer "user_id", null: false - t.integer "agency_id", null: false - t.string "uuid", null: false + t.integer "user_id", null: false, comment: "sensitive=false" + t.integer "agency_id", null: false, comment: "sensitive=false" + t.string "uuid", null: false, comment: "sensitive=false" t.index ["user_id", "agency_id"], name: "index_agency_identities_on_user_id_and_agency_id", unique: true t.index ["uuid"], name: "index_agency_identities_on_uuid", unique: true end create_table "auth_app_configurations", force: :cascade do |t| - t.integer "user_id", null: false - t.string "encrypted_otp_secret_key", null: false - t.string "name", null: false - t.integer "totp_timestamp" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "encrypted_otp_secret_key", null: false, comment: "sensitive=true" + t.string "name", null: false, comment: "sensitive=false" + t.integer "totp_timestamp", comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" t.index ["user_id", "created_at"], name: "index_auth_app_configurations_on_user_id_and_created_at", unique: true t.index ["user_id", "name"], name: "index_auth_app_configurations_on_user_id_and_name", unique: true end create_table "backup_code_configurations", force: :cascade do |t| - t.integer "user_id", null: false - t.datetime "used_at", precision: nil - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.string "salted_code_fingerprint" - t.string "code_salt" - t.string "code_cost" + t.integer "user_id", null: false, comment: "sensitive=false" + t.datetime "used_at", precision: nil, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.string "salted_code_fingerprint", comment: "sensitive=false" + t.string "code_salt", comment: "sensitive=true" + t.string "code_cost", comment: "sensitive=false" t.index ["user_id", "created_at"], name: "index_backup_code_configurations_on_user_id_and_created_at" t.index ["user_id", "salted_code_fingerprint"], name: "index_backup_codes_on_user_id_and_salted_code_fingerprint" end create_table "deleted_users", force: :cascade do |t| - t.integer "user_id", null: false - t.string "uuid", null: false - t.datetime "user_created_at", precision: nil, null: false - t.datetime "deleted_at", precision: nil, null: false + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "uuid", null: false, comment: "sensitive=false" + t.datetime "user_created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "deleted_at", precision: nil, null: false, comment: "sensitive=false" t.index ["user_id"], name: "index_deleted_users_on_user_id", unique: true t.index ["uuid"], name: "index_deleted_users_on_uuid", unique: true end create_table "devices", force: :cascade do |t| - t.integer "user_id", null: false - t.string "cookie_uuid", null: false - t.string "user_agent", null: false - t.datetime "last_used_at", precision: nil, null: false - t.string "last_ip", limit: 255, null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "cookie_uuid", null: false, comment: "sensitive=false" + t.string "user_agent", null: false, comment: "sensitive=false" + t.datetime "last_used_at", precision: nil, null: false, comment: "sensitive=false" + t.string "last_ip", limit: 255, null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" t.index ["cookie_uuid"], name: "index_devices_on_cookie_uuid" t.index ["user_id", "last_used_at"], name: "index_device_user_id_last_used_at" end create_table "disposable_email_domains", force: :cascade do |t| - t.citext "name", null: false + t.citext "name", null: false, comment: "sensitive=false" t.index ["name"], name: "index_disposable_email_domains_on_name", unique: true end create_table "doc_auth_logs", force: :cascade do |t| - t.integer "user_id", null: false - t.datetime "welcome_view_at", precision: nil - t.integer "welcome_view_count", default: 0 - t.datetime "upload_view_at", precision: nil - t.integer "upload_view_count", default: 0 - t.datetime "link_sent_view_at", precision: nil - t.integer "link_sent_view_count", default: 0 - t.datetime "front_image_view_at", precision: nil - t.integer "front_image_view_count", default: 0 - t.integer "front_image_submit_count", default: 0 - t.integer "front_image_error_count", default: 0 - t.datetime "back_image_view_at", precision: nil - t.integer "back_image_view_count", default: 0 - t.integer "back_image_submit_count", default: 0 - t.integer "back_image_error_count", default: 0 - t.datetime "mobile_front_image_view_at", precision: nil - t.integer "mobile_front_image_view_count", default: 0 - t.datetime "mobile_back_image_view_at", precision: nil - t.integer "mobile_back_image_view_count", default: 0 - t.datetime "ssn_view_at", precision: nil - t.integer "ssn_view_count", default: 0 - t.datetime "verify_view_at", precision: nil - t.integer "verify_view_count", default: 0 - t.integer "verify_submit_count", default: 0 - t.integer "verify_error_count", default: 0 - t.datetime "verify_phone_view_at", precision: nil - t.integer "verify_phone_view_count", default: 0 - t.datetime "usps_address_view_at", precision: nil - t.integer "usps_address_view_count", default: 0 - t.datetime "encrypt_view_at", precision: nil - t.integer "encrypt_view_count", default: 0 - t.datetime "verified_view_at", precision: nil - t.integer "verified_view_count", default: 0 - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.integer "mobile_front_image_submit_count", default: 0 - t.integer "mobile_front_image_error_count", default: 0 - t.integer "mobile_back_image_submit_count", default: 0 - t.integer "mobile_back_image_error_count", default: 0 - t.integer "usps_letter_sent_submit_count", default: 0 - t.integer "usps_letter_sent_error_count", default: 0 - t.datetime "capture_mobile_back_image_view_at", precision: nil - t.integer "capture_mobile_back_image_view_count", default: 0 - t.datetime "capture_complete_view_at", precision: nil - t.integer "capture_complete_view_count", default: 0 - t.integer "capture_mobile_back_image_submit_count", default: 0 - t.integer "capture_mobile_back_image_error_count", default: 0 - t.datetime "no_sp_session_started_at", precision: nil - t.datetime "choose_method_view_at", precision: nil - t.integer "choose_method_view_count", default: 0 - t.datetime "present_cac_view_at", precision: nil - t.integer "present_cac_view_count", default: 0 - t.integer "present_cac_submit_count", default: 0 - t.integer "present_cac_error_count", default: 0 - t.datetime "enter_info_view_at", precision: nil - t.integer "enter_info_view_count", default: 0 - t.datetime "success_view_at", precision: nil - t.integer "success_view_count", default: 0 - t.integer "selfie_view_count", default: 0 - t.integer "selfie_submit_count", default: 0 - t.integer "selfie_error_count", default: 0 - t.string "issuer" - t.string "last_document_error" - t.datetime "document_capture_view_at", precision: nil - t.integer "document_capture_view_count", default: 0 - t.integer "document_capture_submit_count", default: 0 - t.integer "document_capture_error_count", default: 0 - t.datetime "agreement_view_at", precision: nil - t.integer "agreement_view_count", default: 0 - t.string "state" - t.datetime "verify_submit_at", precision: nil - t.integer "verify_phone_submit_count", default: 0 - t.datetime "verify_phone_submit_at", precision: nil - t.datetime "document_capture_submit_at", precision: nil - t.datetime "back_image_submit_at", precision: nil - t.datetime "capture_mobile_back_image_submit_at", precision: nil - t.datetime "mobile_back_image_submit_at", precision: nil + t.integer "user_id", null: false, comment: "sensitive=false" + t.datetime "welcome_view_at", precision: nil, comment: "sensitive=false" + t.integer "welcome_view_count", default: 0, comment: "sensitive=false" + t.datetime "upload_view_at", precision: nil, comment: "sensitive=false" + t.integer "upload_view_count", default: 0, comment: "sensitive=false" + t.datetime "link_sent_view_at", precision: nil, comment: "sensitive=false" + t.integer "link_sent_view_count", default: 0, comment: "sensitive=false" + t.datetime "front_image_view_at", precision: nil, comment: "sensitive=false" + t.integer "front_image_view_count", default: 0, comment: "sensitive=false" + t.integer "front_image_submit_count", default: 0, comment: "sensitive=false" + t.integer "front_image_error_count", default: 0, comment: "sensitive=false" + t.datetime "back_image_view_at", precision: nil, comment: "sensitive=false" + t.integer "back_image_view_count", default: 0, comment: "sensitive=false" + t.integer "back_image_submit_count", default: 0, comment: "sensitive=false" + t.integer "back_image_error_count", default: 0, comment: "sensitive=false" + t.datetime "mobile_front_image_view_at", precision: nil, comment: "sensitive=false" + t.integer "mobile_front_image_view_count", default: 0, comment: "sensitive=false" + t.datetime "mobile_back_image_view_at", precision: nil, comment: "sensitive=false" + t.integer "mobile_back_image_view_count", default: 0, comment: "sensitive=false" + t.datetime "ssn_view_at", precision: nil, comment: "sensitive=false" + t.integer "ssn_view_count", default: 0, comment: "sensitive=false" + t.datetime "verify_view_at", precision: nil, comment: "sensitive=false" + t.integer "verify_view_count", default: 0, comment: "sensitive=false" + t.integer "verify_submit_count", default: 0, comment: "sensitive=false" + t.integer "verify_error_count", default: 0, comment: "sensitive=false" + t.datetime "verify_phone_view_at", precision: nil, comment: "sensitive=false" + t.integer "verify_phone_view_count", default: 0, comment: "sensitive=false" + t.datetime "usps_address_view_at", precision: nil, comment: "sensitive=false" + t.integer "usps_address_view_count", default: 0, comment: "sensitive=false" + t.datetime "encrypt_view_at", precision: nil, comment: "sensitive=false" + t.integer "encrypt_view_count", default: 0, comment: "sensitive=false" + t.datetime "verified_view_at", precision: nil, comment: "sensitive=false" + t.integer "verified_view_count", default: 0, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.integer "mobile_front_image_submit_count", default: 0, comment: "sensitive=false" + t.integer "mobile_front_image_error_count", default: 0, comment: "sensitive=false" + t.integer "mobile_back_image_submit_count", default: 0, comment: "sensitive=false" + t.integer "mobile_back_image_error_count", default: 0, comment: "sensitive=false" + t.integer "usps_letter_sent_submit_count", default: 0, comment: "sensitive=false" + t.integer "usps_letter_sent_error_count", default: 0, comment: "sensitive=false" + t.datetime "capture_mobile_back_image_view_at", precision: nil, comment: "sensitive=false" + t.integer "capture_mobile_back_image_view_count", default: 0, comment: "sensitive=false" + t.datetime "capture_complete_view_at", precision: nil, comment: "sensitive=false" + t.integer "capture_complete_view_count", default: 0, comment: "sensitive=false" + t.integer "capture_mobile_back_image_submit_count", default: 0, comment: "sensitive=false" + t.integer "capture_mobile_back_image_error_count", default: 0, comment: "sensitive=false" + t.datetime "no_sp_session_started_at", precision: nil, comment: "sensitive=false" + t.datetime "choose_method_view_at", precision: nil, comment: "sensitive=false" + t.integer "choose_method_view_count", default: 0, comment: "sensitive=false" + t.datetime "present_cac_view_at", precision: nil, comment: "sensitive=false" + t.integer "present_cac_view_count", default: 0, comment: "sensitive=false" + t.integer "present_cac_submit_count", default: 0, comment: "sensitive=false" + t.integer "present_cac_error_count", default: 0, comment: "sensitive=false" + t.datetime "enter_info_view_at", precision: nil, comment: "sensitive=false" + t.integer "enter_info_view_count", default: 0, comment: "sensitive=false" + t.datetime "success_view_at", precision: nil, comment: "sensitive=false" + t.integer "success_view_count", default: 0, comment: "sensitive=false" + t.integer "selfie_view_count", default: 0, comment: "sensitive=false" + t.integer "selfie_submit_count", default: 0, comment: "sensitive=false" + t.integer "selfie_error_count", default: 0, comment: "sensitive=false" + t.string "issuer", comment: "sensitive=false" + t.string "last_document_error", comment: "sensitive=false" + t.datetime "document_capture_view_at", precision: nil, comment: "sensitive=false" + t.integer "document_capture_view_count", default: 0, comment: "sensitive=false" + t.integer "document_capture_submit_count", default: 0, comment: "sensitive=false" + t.integer "document_capture_error_count", default: 0, comment: "sensitive=false" + t.datetime "agreement_view_at", precision: nil, comment: "sensitive=false" + t.integer "agreement_view_count", default: 0, comment: "sensitive=false" + t.string "state", comment: "sensitive=false" + t.datetime "verify_submit_at", precision: nil, comment: "sensitive=false" + t.integer "verify_phone_submit_count", default: 0, comment: "sensitive=false" + t.datetime "verify_phone_submit_at", precision: nil, comment: "sensitive=false" + t.datetime "document_capture_submit_at", precision: nil, comment: "sensitive=false" + t.datetime "back_image_submit_at", precision: nil, comment: "sensitive=false" + t.datetime "capture_mobile_back_image_submit_at", precision: nil, comment: "sensitive=false" + t.datetime "mobile_back_image_submit_at", precision: nil, comment: "sensitive=false" t.index ["issuer"], name: "index_doc_auth_logs_on_issuer" t.index ["user_id"], name: "index_doc_auth_logs_on_user_id", unique: true t.index ["verified_view_at"], name: "index_doc_auth_logs_on_verified_view_at" end create_table "document_capture_sessions", force: :cascade do |t| - t.string "uuid" - t.string "result_id" - t.bigint "user_id" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.datetime "requested_at", precision: nil - t.string "issuer" - t.datetime "cancelled_at", precision: nil - t.boolean "ocr_confirmation_pending", default: false - t.string "last_doc_auth_result" + t.string "uuid", comment: "sensitive=false" + t.string "result_id", comment: "sensitive=false" + t.bigint "user_id", comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "requested_at", precision: nil, comment: "sensitive=false" + t.string "issuer", comment: "sensitive=false" + t.datetime "cancelled_at", precision: nil, comment: "sensitive=false" + t.boolean "ocr_confirmation_pending", default: false, comment: "sensitive=false" + t.string "last_doc_auth_result", comment: "sensitive=false" t.index ["result_id"], name: "index_document_capture_sessions_on_result_id" t.index ["user_id"], name: "index_document_capture_sessions_on_user_id" t.index ["uuid"], name: "index_document_capture_sessions_on_uuid" end create_table "email_addresses", force: :cascade do |t| - t.bigint "user_id" - t.string "confirmation_token", limit: 255 - t.datetime "confirmed_at", precision: nil - t.datetime "confirmation_sent_at", precision: nil - t.string "email_fingerprint", default: "", null: false - t.string "encrypted_email", default: "", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.datetime "last_sign_in_at", precision: nil + t.bigint "user_id", comment: "sensitive=false" + t.string "confirmation_token", limit: 255, comment: "sensitive=true" + t.datetime "confirmed_at", precision: nil, comment: "sensitive=false" + t.datetime "confirmation_sent_at", precision: nil, comment: "sensitive=false" + t.string "email_fingerprint", default: "", null: false, comment: "sensitive=false" + t.string "encrypted_email", default: "", null: false, comment: "sensitive=true" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "last_sign_in_at", precision: nil, comment: "sensitive=false" t.index ["confirmation_token"], name: "index_email_addresses_on_confirmation_token", unique: true t.index ["email_fingerprint", "user_id"], name: "index_email_addresses_on_email_fingerprint_and_user_id", unique: true t.index ["email_fingerprint"], name: "index_email_addresses_on_email_fingerprint", unique: true, where: "(confirmed_at IS NOT NULL)" @@ -213,41 +213,41 @@ end create_table "events", id: :serial, force: :cascade do |t| - t.integer "user_id", null: false - t.integer "event_type", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.integer "device_id" - t.string "ip" - t.datetime "disavowed_at", precision: nil - t.string "disavowal_token_fingerprint" + t.integer "user_id", null: false, comment: "sensitive=false" + t.integer "event_type", null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.integer "device_id", comment: "sensitive=false" + t.string "ip", comment: "sensitive=false" + t.datetime "disavowed_at", precision: nil, comment: "sensitive=false" + t.string "disavowal_token_fingerprint", comment: "sensitive=true" t.index ["device_id", "created_at"], name: "index_events_on_device_id_and_created_at" t.index ["disavowal_token_fingerprint"], name: "index_events_on_disavowal_token_fingerprint" t.index ["user_id", "created_at"], name: "index_events_on_user_id_and_created_at" end create_table "federal_email_domains", force: :cascade do |t| - t.citext "name", null: false + t.citext "name", null: false, comment: "sensitive=false" t.index ["name"], name: "index_federal_email_domains_on_name", unique: true end create_table "fraud_review_requests", force: :cascade do |t| - t.integer "user_id" - t.string "uuid" - t.string "irs_session_id" - t.string "login_session_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "user_id", comment: "sensitive=false" + t.string "uuid", comment: "sensitive=false" + t.string "irs_session_id", comment: "sensitive=false" + t.string "login_session_id", comment: "sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["user_id"], name: "index_fraud_review_requests_on_user_id" end create_table "iaa_gtcs", force: :cascade do |t| - t.string "gtc_number", null: false - t.integer "mod_number", default: 0, null: false - t.date "start_date" - t.date "end_date" - t.decimal "estimated_amount", precision: 12, scale: 2 - t.bigint "partner_account_id" + t.string "gtc_number", null: false, comment: "sensitive=false" + t.integer "mod_number", default: 0, null: false, comment: "sensitive=false" + t.date "start_date", comment: "sensitive=false" + t.date "end_date", comment: "sensitive=false" + t.decimal "estimated_amount", precision: 12, scale: 2, comment: "sensitive=false" + t.bigint "partner_account_id", comment: "sensitive=false" t.index ["gtc_number"], name: "index_iaa_gtcs_on_gtc_number", unique: true t.index ["partner_account_id"], name: "index_iaa_gtcs_on_partner_account_id" t.check_constraint "end_date IS NOT NULL", name: "iaa_gtcs_end_date_null" @@ -255,13 +255,13 @@ end create_table "iaa_orders", force: :cascade do |t| - t.integer "order_number", null: false - t.integer "mod_number", default: 0, null: false - t.date "start_date" - t.date "end_date" - t.decimal "estimated_amount", precision: 12, scale: 2 - t.integer "pricing_model", default: 2, null: false - t.bigint "iaa_gtc_id" + t.integer "order_number", null: false, comment: "sensitive=false" + t.integer "mod_number", default: 0, null: false, comment: "sensitive=false" + t.date "start_date", comment: "sensitive=false" + t.date "end_date", comment: "sensitive=false" + t.decimal "estimated_amount", precision: 12, scale: 2, comment: "sensitive=false" + t.integer "pricing_model", default: 2, null: false, comment: "sensitive=false" + t.bigint "iaa_gtc_id", comment: "sensitive=false" t.index ["iaa_gtc_id", "order_number"], name: "index_iaa_orders_on_iaa_gtc_id_and_order_number", unique: true t.index ["iaa_gtc_id"], name: "index_iaa_orders_on_iaa_gtc_id" t.check_constraint "end_date IS NOT NULL", name: "iaa_orders_end_date_null" @@ -269,30 +269,30 @@ end create_table "identities", id: :serial, force: :cascade do |t| - t.string "service_provider", limit: 255 - t.datetime "last_authenticated_at", precision: nil - t.integer "user_id" - t.datetime "created_at", precision: nil - t.datetime "updated_at", precision: nil - t.string "session_uuid", limit: 255 - t.string "uuid", null: false - t.string "nonce" - t.integer "ial", default: 1 - t.string "access_token" - t.string "scope" - t.string "code_challenge" - t.string "rails_session_id" - t.json "verified_attributes" - t.datetime "verified_at", precision: nil - t.datetime "last_consented_at", precision: nil - t.datetime "last_ial1_authenticated_at", precision: nil - t.datetime "last_ial2_authenticated_at", precision: nil - t.datetime "deleted_at", precision: nil - t.integer "aal" - t.text "requested_aal_value" - t.string "vtr" - t.string "acr_values" - t.bigint "email_address_id" + t.string "service_provider", limit: 255, comment: "sensitive=false" + t.datetime "last_authenticated_at", precision: nil, comment: "sensitive=false" + t.integer "user_id", comment: "sensitive=false" + t.datetime "created_at", precision: nil, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, comment: "sensitive=false" + t.string "session_uuid", limit: 255, comment: "sensitive=true" + t.string "uuid", null: false, comment: "sensitive=false" + t.string "nonce", comment: "sensitive=false" + t.integer "ial", default: 1, comment: "sensitive=false" + t.string "access_token", comment: "sensitive=true" + t.string "scope", comment: "sensitive=false" + t.string "code_challenge", comment: "sensitive=true" + t.string "rails_session_id", comment: "sensitive=true" + t.json "verified_attributes", comment: "sensitive=false" + t.datetime "verified_at", precision: nil, comment: "sensitive=false" + t.datetime "last_consented_at", precision: nil, comment: "sensitive=false" + t.datetime "last_ial1_authenticated_at", precision: nil, comment: "sensitive=false" + t.datetime "last_ial2_authenticated_at", precision: nil, comment: "sensitive=false" + t.datetime "deleted_at", precision: nil, comment: "sensitive=false" + t.integer "aal", comment: "sensitive=false" + t.text "requested_aal_value", comment: "sensitive=false" + t.string "vtr", comment: "sensitive=false" + t.string "acr_values", comment: "sensitive=false" + t.bigint "email_address_id", comment: "sensitive=false" t.index ["access_token"], name: "index_identities_on_access_token", unique: true t.index ["session_uuid"], name: "index_identities_on_session_uuid", unique: true t.index ["user_id", "service_provider"], name: "index_identities_on_user_id_and_service_provider", unique: true @@ -300,31 +300,31 @@ end create_table "in_person_enrollments", comment: "Details and status of an in-person proofing enrollment for one user and profile", force: :cascade do |t| - t.bigint "user_id", null: false, comment: "Foreign key to the user this enrollment belongs to" - t.bigint "profile_id", comment: "Foreign key to the profile this enrollment belongs to" - t.string "enrollment_code", comment: "The code returned by the USPS service" - t.datetime "status_check_attempted_at", precision: nil, comment: "The last time a status check was attempted" - t.datetime "status_updated_at", precision: nil, comment: "The last time the status was successfully updated with a value from the USPS API" - t.integer "status", default: 0, comment: "The status of the enrollment" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.boolean "current_address_matches_id", comment: "True if the user indicates that their current address matches the address on the ID they're bringing to the Post Office." - t.jsonb "selected_location_details", comment: "The location details of the Post Office the user selected (including title, address, hours of operation)" - t.string "unique_id", comment: "Unique ID to use with the USPS service" - t.datetime "enrollment_established_at", comment: "When the enrollment was successfully established" - t.string "issuer", comment: "Issuer associated with the enrollment at time of creation" - t.boolean "follow_up_survey_sent", default: false - t.boolean "early_reminder_sent", default: false, comment: "early reminder to complete IPP before deadline sent" - t.boolean "late_reminder_sent", default: false, comment: "late reminder to complete IPP before deadline sent" - t.boolean "deadline_passed_sent", default: false, comment: "deadline passed email sent for expired enrollment" - t.datetime "proofed_at", precision: nil, comment: "timestamp when user attempted to proof at a Post Office" - t.boolean "capture_secondary_id_enabled", default: false, comment: "record and proof state ID and residential addresses separately" - t.datetime "status_check_completed_at", comment: "The last time a status check was successfully completed" - t.boolean "ready_for_status_check", default: false - t.datetime "notification_sent_at", comment: "The time a notification was sent" - t.datetime "last_batch_claimed_at" - t.string "sponsor_id", null: false - t.string "doc_auth_result" + t.bigint "user_id", null: false, comment: "Foreign key to the user this enrollment belongs to sensitive=false" + t.bigint "profile_id", comment: "Foreign key to the profile this enrollment belongs to sensitive=false" + t.string "enrollment_code", comment: "The code returned by the USPS service sensitive=false" + t.datetime "status_check_attempted_at", precision: nil, comment: "The last time a status check was attempted sensitive=false" + t.datetime "status_updated_at", precision: nil, comment: "The last time the status was successfully updated with a value from the USPS API sensitive=false" + t.integer "status", default: 0, comment: "The status of the enrollment sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" + t.boolean "current_address_matches_id", comment: "True if the user indicates that their current address matches the address on the ID they're bringing to the Post Office. sensitive=false" + t.jsonb "selected_location_details", comment: "The location details of the Post Office the user selected (including title, address, hours of operation) sensitive=false" + t.string "unique_id", comment: "Unique ID to use with the USPS service sensitive=false" + t.datetime "enrollment_established_at", comment: "When the enrollment was successfully established sensitive=false" + t.string "issuer", comment: "Issuer associated with the enrollment at time of creation sensitive=false" + t.boolean "follow_up_survey_sent", default: false, comment: "sensitive=false" + t.boolean "early_reminder_sent", default: false, comment: "early reminder to complete IPP before deadline sent sensitive=false" + t.boolean "late_reminder_sent", default: false, comment: "late reminder to complete IPP before deadline sent sensitive=false" + t.boolean "deadline_passed_sent", default: false, comment: "deadline passed email sent for expired enrollment sensitive=false" + t.datetime "proofed_at", precision: nil, comment: "timestamp when user attempted to proof at a Post Office sensitive=false" + t.boolean "capture_secondary_id_enabled", default: false, comment: "record and proof state ID and residential addresses separately sensitive=false" + t.datetime "status_check_completed_at", comment: "The last time a status check was successfully completed sensitive=false" + t.boolean "ready_for_status_check", default: false, comment: "sensitive=false" + t.datetime "notification_sent_at", comment: "The time a notification was sent sensitive=false" + t.datetime "last_batch_claimed_at", comment: "sensitive=false" + t.string "sponsor_id", null: false, comment: "sensitive=false" + t.string "doc_auth_result", comment: "sensitive=false" t.index ["profile_id"], name: "index_in_person_enrollments_on_profile_id" t.index ["ready_for_status_check"], name: "index_in_person_enrollments_on_ready_for_status_check", where: "(ready_for_status_check = true)" t.index ["status_check_attempted_at"], name: "index_in_person_enrollments_on_status_check_attempted_at", where: "(status = 1)" @@ -334,28 +334,28 @@ end create_table "integration_statuses", force: :cascade do |t| - t.string "name", null: false - t.integer "order", null: false - t.string "partner_name" + t.string "name", null: false, comment: "sensitive=false" + t.integer "order", null: false, comment: "sensitive=false" + t.string "partner_name", comment: "sensitive=false" t.index ["name"], name: "index_integration_statuses_on_name", unique: true t.index ["order"], name: "index_integration_statuses_on_order", unique: true end create_table "integration_usages", force: :cascade do |t| - t.bigint "iaa_order_id" - t.bigint "integration_id" + t.bigint "iaa_order_id", comment: "sensitive=false" + t.bigint "integration_id", comment: "sensitive=false" t.index ["iaa_order_id", "integration_id"], name: "index_integration_usages_on_iaa_order_id_and_integration_id", unique: true t.index ["iaa_order_id"], name: "index_integration_usages_on_iaa_order_id" t.index ["integration_id"], name: "index_integration_usages_on_integration_id" end create_table "integrations", force: :cascade do |t| - t.string "issuer", null: false - t.string "name", null: false - t.integer "dashboard_identifier" - t.bigint "partner_account_id" - t.bigint "integration_status_id" - t.bigint "service_provider_id" + t.string "issuer", null: false, comment: "sensitive=false" + t.string "name", null: false, comment: "sensitive=false" + t.integer "dashboard_identifier", comment: "sensitive=false" + t.bigint "partner_account_id", comment: "sensitive=false" + t.bigint "integration_status_id", comment: "sensitive=false" + t.bigint "service_provider_id", comment: "sensitive=false" t.index ["dashboard_identifier"], name: "index_integrations_on_dashboard_identifier", unique: true t.index ["integration_status_id"], name: "index_integrations_on_integration_status_id" t.index ["issuer"], name: "index_integrations_on_issuer", unique: true @@ -364,35 +364,35 @@ end create_table "letter_requests_to_usps_ftp_logs", force: :cascade do |t| - t.datetime "ftp_at", precision: nil, null: false - t.integer "letter_requests_count", null: false + t.datetime "ftp_at", precision: nil, null: false, comment: "sensitive=false" + t.integer "letter_requests_count", null: false, comment: "sensitive=false" t.index ["ftp_at"], name: "index_letter_requests_to_usps_ftp_logs_on_ftp_at" end create_table "notification_phone_configurations", force: :cascade do |t| - t.bigint "in_person_enrollment_id", null: false - t.text "encrypted_phone", null: false, comment: "Encrypted phone number to send notifications to" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.bigint "in_person_enrollment_id", null: false, comment: "sensitive=false" + t.text "encrypted_phone", null: false, comment: "Encrypted phone number to send notifications to sensitive=true" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["in_person_enrollment_id"], name: "index_notification_phone_configurations_on_enrollment_id", unique: true end create_table "partner_account_statuses", force: :cascade do |t| - t.string "name", null: false - t.integer "order", null: false - t.string "partner_name" + t.string "name", null: false, comment: "sensitive=false" + t.integer "order", null: false, comment: "sensitive=false" + t.string "partner_name", comment: "sensitive=false" t.index ["name"], name: "index_partner_account_statuses_on_name", unique: true t.index ["order"], name: "index_partner_account_statuses_on_order", unique: true end create_table "partner_accounts", force: :cascade do |t| - t.string "name", null: false - t.text "description" - t.string "requesting_agency", null: false - t.date "became_partner" - t.bigint "agency_id" - t.bigint "partner_account_status_id" - t.bigint "crm_id" + t.string "name", null: false, comment: "sensitive=false" + t.text "description", comment: "sensitive=false" + t.string "requesting_agency", null: false, comment: "sensitive=false" + t.date "became_partner", comment: "sensitive=false" + t.bigint "agency_id", comment: "sensitive=false" + t.bigint "partner_account_status_id", comment: "sensitive=false" + t.bigint "crm_id", comment: "sensitive=false" t.index ["agency_id"], name: "index_partner_accounts_on_agency_id" t.index ["name"], name: "index_partner_accounts_on_name", unique: true t.index ["partner_account_status_id"], name: "index_partner_accounts_on_partner_account_status_id" @@ -400,63 +400,63 @@ end create_table "phone_configurations", force: :cascade do |t| - t.bigint "user_id", null: false - t.text "encrypted_phone", null: false - t.integer "delivery_preference", default: 0, null: false - t.boolean "mfa_enabled", default: true, null: false - t.datetime "confirmation_sent_at", precision: nil - t.datetime "confirmed_at", precision: nil - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.datetime "made_default_at", precision: nil + t.bigint "user_id", null: false, comment: "sensitive=false" + t.text "encrypted_phone", null: false, comment: "sensitive=false" + t.integer "delivery_preference", default: 0, null: false, comment: "sensitive=false" + t.boolean "mfa_enabled", default: true, null: false, comment: "sensitive=false" + t.datetime "confirmation_sent_at", precision: nil, comment: "sensitive=false" + t.datetime "confirmed_at", precision: nil, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "made_default_at", precision: nil, comment: "sensitive=false" t.index ["user_id", "made_default_at", "created_at"], name: "index_phone_configurations_on_made_default_at" end create_table "phone_number_opt_outs", force: :cascade do |t| - t.string "encrypted_phone" - t.string "phone_fingerprint", null: false - t.string "uuid" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.string "encrypted_phone", comment: "sensitive=false" + t.string "phone_fingerprint", null: false, comment: "sensitive=false" + t.string "uuid", comment: "sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["phone_fingerprint"], name: "index_phone_number_opt_outs_on_phone_fingerprint", unique: true t.index ["uuid"], name: "index_phone_number_opt_outs_on_uuid", unique: true end create_table "piv_cac_configurations", force: :cascade do |t| - t.integer "user_id", null: false - t.string "x509_dn_uuid", null: false - t.string "name", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.string "x509_issuer" + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "x509_dn_uuid", null: false, comment: "sensitive=false" + t.string "name", null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.string "x509_issuer", comment: "sensitive=false" t.index ["user_id", "created_at"], name: "index_piv_cac_configurations_on_user_id_and_created_at", unique: true t.index ["user_id", "name"], name: "index_piv_cac_configurations_on_user_id_and_name", unique: true t.index ["x509_dn_uuid"], name: "index_piv_cac_configurations_on_x509_dn_uuid", unique: true end create_table "profiles", id: :serial, force: :cascade do |t| - t.integer "user_id", null: false - t.boolean "active", default: false, null: false - t.datetime "verified_at", precision: nil - t.datetime "activated_at", precision: nil - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.text "encrypted_pii" - t.string "ssn_signature", limit: 64 - t.text "encrypted_pii_recovery" - t.integer "deactivation_reason" - t.jsonb "proofing_components" - t.string "name_zip_birth_year_signature" - t.string "initiating_service_provider_issuer" - t.datetime "fraud_review_pending_at" - t.datetime "fraud_rejection_at" - t.datetime "gpo_verification_pending_at" - t.integer "fraud_pending_reason" - t.datetime "in_person_verification_pending_at" - t.text "encrypted_pii_multi_region" - t.text "encrypted_pii_recovery_multi_region" - t.datetime "gpo_verification_expired_at" - t.integer "idv_level" + t.integer "user_id", null: false, comment: "sensitive=false" + t.boolean "active", default: false, null: false, comment: "sensitive=false" + t.datetime "verified_at", precision: nil, comment: "sensitive=false" + t.datetime "activated_at", precision: nil, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.text "encrypted_pii", comment: "sensitive=true" + t.string "ssn_signature", limit: 64, comment: "sensitive=true" + t.text "encrypted_pii_recovery", comment: "sensitive=true" + t.integer "deactivation_reason", comment: "sensitive=false" + t.jsonb "proofing_components", comment: "sensitive=false" + t.string "name_zip_birth_year_signature", comment: "sensitive=true" + t.string "initiating_service_provider_issuer", comment: "sensitive=false" + t.datetime "fraud_review_pending_at", comment: "sensitive=false" + t.datetime "fraud_rejection_at", comment: "sensitive=false" + t.datetime "gpo_verification_pending_at", comment: "sensitive=false" + t.integer "fraud_pending_reason", comment: "sensitive=false" + t.datetime "in_person_verification_pending_at", comment: "sensitive=false" + t.text "encrypted_pii_multi_region", comment: "sensitive=false" + t.text "encrypted_pii_recovery_multi_region", comment: "sensitive=false" + t.datetime "gpo_verification_expired_at", comment: "sensitive=false" + t.integer "idv_level", comment: "sensitive=false" t.index ["fraud_pending_reason"], name: "index_profiles_on_fraud_pending_reason" t.index ["fraud_rejection_at"], name: "index_profiles_on_fraud_rejection_at" t.index ["fraud_review_pending_at"], name: "index_profiles_on_fraud_review_pending_at" @@ -469,205 +469,205 @@ end create_table "proofing_components", force: :cascade do |t| - t.integer "user_id", null: false - t.string "document_check" - t.string "document_type" - t.string "source_check" - t.string "resolution_check" - t.string "address_check" - t.datetime "verified_at", precision: nil - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.string "liveness_check" - t.string "device_fingerprinting_vendor" - t.boolean "threatmetrix" - t.string "threatmetrix_review_status" - t.string "threatmetrix_risk_rating" - t.string "threatmetrix_policy_score" + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "document_check", comment: "sensitive=false" + t.string "document_type", comment: "sensitive=false" + t.string "source_check", comment: "sensitive=false" + t.string "resolution_check", comment: "sensitive=false" + t.string "address_check", comment: "sensitive=false" + t.datetime "verified_at", precision: nil, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.string "liveness_check", comment: "sensitive=false" + t.string "device_fingerprinting_vendor", comment: "sensitive=false" + t.boolean "threatmetrix", comment: "sensitive=false" + t.string "threatmetrix_review_status", comment: "sensitive=false" + t.string "threatmetrix_risk_rating", comment: "sensitive=false" + t.string "threatmetrix_policy_score", comment: "sensitive=false" t.index ["user_id"], name: "index_proofing_components_on_user_id", unique: true t.index ["verified_at"], name: "index_proofing_components_on_verified_at" end create_table "registration_logs", force: :cascade do |t| - t.integer "user_id", null: false - t.datetime "registered_at", precision: nil + t.integer "user_id", null: false, comment: "sensitive=false" + t.datetime "registered_at", precision: nil, comment: "sensitive=false" t.index ["registered_at"], name: "index_registration_logs_on_registered_at" t.index ["user_id"], name: "index_registration_logs_on_user_id", unique: true end create_table "security_events", force: :cascade do |t| - t.bigint "user_id", null: false - t.string "event_type", null: false - t.string "jti" - t.string "issuer" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.datetime "occurred_at", precision: nil + t.bigint "user_id", null: false, comment: "sensitive=false" + t.string "event_type", null: false, comment: "sensitive=false" + t.string "jti", comment: "sensitive=false" + t.string "issuer", comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "occurred_at", precision: nil, comment: "sensitive=false" t.index ["jti", "user_id", "issuer"], name: "index_security_events_on_jti_and_user_id_and_issuer", unique: true t.index ["user_id"], name: "index_security_events_on_user_id" end create_table "service_providers", id: :serial, force: :cascade do |t| - t.string "issuer", null: false - t.string "friendly_name" - t.text "description" - t.text "metadata_url" - t.text "acs_url" - t.text "assertion_consumer_logout_service_url" - t.text "logo" - t.string "signature" - t.string "block_encryption", default: "aes256-cbc", null: false - t.text "sp_initiated_login_url" - t.text "return_to_sp_url" - t.json "attribute_bundle" - t.datetime "created_at", precision: nil - t.datetime "updated_at", precision: nil - t.boolean "active", default: false, null: false - t.boolean "approved", default: false, null: false - t.boolean "native", default: false, null: false - t.string "redirect_uris", default: [], array: true - t.integer "agency_id" - t.text "failure_to_proof_url" - t.integer "ial" - t.boolean "piv_cac", default: false - t.boolean "piv_cac_scoped_by_email", default: false - t.boolean "pkce" - t.string "push_notification_url" - t.jsonb "help_text", default: {"sign_in"=>{}, "sign_up"=>{}, "forgot_password"=>{}} - t.boolean "allow_prompt_login", default: false - t.boolean "signed_response_message_requested", default: false - t.string "remote_logo_key" - t.date "launch_date" - t.string "iaa" - t.date "iaa_start_date" - t.date "iaa_end_date" - t.string "app_id" - t.integer "default_aal" - t.string "certs", array: true - t.boolean "email_nameid_format_allowed", default: false - t.boolean "use_legacy_name_id_behavior", default: false - t.boolean "irs_attempts_api_enabled" - t.boolean "in_person_proofing_enabled", default: false + t.string "issuer", null: false, comment: "sensitive=false" + t.string "friendly_name", comment: "sensitive=false" + t.text "description", comment: "sensitive=false" + t.text "metadata_url", comment: "sensitive=false" + t.text "acs_url", comment: "sensitive=false" + t.text "assertion_consumer_logout_service_url", comment: "sensitive=false" + t.text "logo", comment: "sensitive=false" + t.string "signature", comment: "sensitive=false" + t.string "block_encryption", default: "aes256-cbc", null: false, comment: "sensitive=true" + t.text "sp_initiated_login_url", comment: "sensitive=false" + t.text "return_to_sp_url", comment: "sensitive=false" + t.json "attribute_bundle", comment: "sensitive=false" + t.datetime "created_at", precision: nil, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, comment: "sensitive=false" + t.boolean "active", default: false, null: false, comment: "sensitive=false" + t.boolean "approved", default: false, null: false, comment: "sensitive=false" + t.boolean "native", default: false, null: false, comment: "sensitive=false" + t.string "redirect_uris", default: [], comment: "sensitive=false", array: true + t.integer "agency_id", comment: "sensitive=false" + t.text "failure_to_proof_url", comment: "sensitive=false" + t.integer "ial", comment: "sensitive=false" + t.boolean "piv_cac", default: false, comment: "sensitive=false" + t.boolean "piv_cac_scoped_by_email", default: false, comment: "sensitive=false" + t.boolean "pkce", comment: "sensitive=false" + t.string "push_notification_url", comment: "sensitive=false" + t.jsonb "help_text", default: {"sign_in"=>{}, "sign_up"=>{}, "forgot_password"=>{}}, comment: "sensitive=false" + t.boolean "allow_prompt_login", default: false, comment: "sensitive=false" + t.boolean "signed_response_message_requested", default: false, comment: "sensitive=false" + t.string "remote_logo_key", comment: "sensitive=false" + t.date "launch_date", comment: "sensitive=false" + t.string "iaa", comment: "sensitive=false" + t.date "iaa_start_date", comment: "sensitive=false" + t.date "iaa_end_date", comment: "sensitive=false" + t.string "app_id", comment: "sensitive=false" + t.integer "default_aal", comment: "sensitive=false" + t.string "certs", comment: "sensitive=false", array: true + t.boolean "email_nameid_format_allowed", default: false, comment: "sensitive=false" + t.boolean "use_legacy_name_id_behavior", default: false, comment: "sensitive=false" + t.boolean "irs_attempts_api_enabled", comment: "sensitive=false" + t.boolean "in_person_proofing_enabled", default: false, comment: "sensitive=false" t.index ["issuer"], name: "index_service_providers_on_issuer", unique: true end create_table "sign_in_restrictions", force: :cascade do |t| - t.integer "user_id", null: false - t.string "service_provider" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.integer "user_id", null: false, comment: "sensitive=false" + t.string "service_provider", comment: "sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["user_id", "service_provider"], name: "index_sign_in_restrictions_on_user_id_and_service_provider", unique: true end create_table "sp_costs", force: :cascade do |t| - t.string "issuer", null: false - t.integer "agency_id", null: false - t.string "cost_type", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.integer "ial" - t.string "transaction_id" + t.string "issuer", null: false, comment: "sensitive=false" + t.integer "agency_id", null: false, comment: "sensitive=false" + t.string "cost_type", null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.integer "ial", comment: "sensitive=false" + t.string "transaction_id", comment: "sensitive=false" t.index ["created_at"], name: "index_sp_costs_on_created_at" end create_table "sp_return_logs", force: :cascade do |t| - t.datetime "requested_at", precision: nil, null: false - t.string "request_id", null: false - t.integer "ial", null: false - t.string "issuer", null: false - t.integer "user_id" - t.datetime "returned_at", precision: nil - t.boolean "billable" - t.bigint "profile_id" - t.datetime "profile_verified_at" - t.string "profile_requested_issuer" + t.datetime "requested_at", precision: nil, null: false, comment: "sensitive=false" + t.string "request_id", null: false, comment: "sensitive=false" + t.integer "ial", null: false, comment: "sensitive=false" + t.string "issuer", null: false, comment: "sensitive=false" + t.integer "user_id", comment: "sensitive=false" + t.datetime "returned_at", precision: nil, comment: "sensitive=false" + t.boolean "billable", comment: "sensitive=false" + t.bigint "profile_id", comment: "sensitive=false" + t.datetime "profile_verified_at", comment: "sensitive=false" + t.string "profile_requested_issuer", comment: "sensitive=false" t.index "((returned_at)::date), issuer", name: "index_sp_return_logs_on_returned_at_date_issuer", where: "((billable = true) AND (returned_at IS NOT NULL))" t.index ["request_id"], name: "index_sp_return_logs_on_request_id", unique: true end create_table "sp_upgraded_biometric_profiles", force: :cascade do |t| - t.datetime "upgraded_at", null: false - t.bigint "user_id", null: false - t.string "idv_level", null: false - t.string "issuer", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "upgraded_at", null: false, comment: "sensitive=false" + t.bigint "user_id", null: false, comment: "sensitive=false" + t.string "idv_level", null: false, comment: "sensitive=false" + t.string "issuer", null: false, comment: "sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["issuer", "upgraded_at"], name: "index_sp_upgraded_biometric_profiles_on_issuer_and_upgraded_at" t.index ["user_id"], name: "index_sp_upgraded_biometric_profiles_on_user_id" end create_table "suspended_emails", force: :cascade do |t| - t.bigint "email_address_id", null: false - t.string "digested_base_email", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.bigint "email_address_id", null: false, comment: "sensitive=false" + t.string "digested_base_email", null: false, comment: "sensitive=false" + t.datetime "created_at", null: false, comment: "sensitive=false" + t.datetime "updated_at", null: false, comment: "sensitive=false" t.index ["digested_base_email"], name: "index_suspended_emails_on_digested_base_email" t.index ["email_address_id"], name: "index_suspended_emails_on_email_address_id" end create_table "users", id: :serial, force: :cascade do |t| - t.string "reset_password_token", limit: 255 - t.datetime "reset_password_sent_at", precision: nil - t.datetime "created_at", precision: nil - t.datetime "updated_at", precision: nil - t.datetime "confirmed_at", precision: nil - t.integer "second_factor_attempts_count", default: 0 - t.string "uuid", limit: 255, null: false - t.datetime "second_factor_locked_at", precision: nil - t.datetime "phone_confirmed_at", precision: nil - t.string "direct_otp" - t.datetime "direct_otp_sent_at", precision: nil - t.string "unique_session_id" - t.integer "otp_delivery_preference", default: 0, null: false - t.string "encrypted_password_digest", default: "" - t.string "encrypted_recovery_code_digest", default: "" - t.datetime "remember_device_revoked_at", precision: nil - t.string "email_language", limit: 10 - t.datetime "accepted_terms_at", precision: nil - t.datetime "encrypted_recovery_code_digest_generated_at", precision: nil - t.datetime "suspended_at" - t.datetime "reinstated_at" - t.string "encrypted_password_digest_multi_region" - t.string "encrypted_recovery_code_digest_multi_region" - t.datetime "second_mfa_reminder_dismissed_at" - t.datetime "piv_cac_recommended_dismissed_at" - t.datetime "sign_in_new_device_at" - t.datetime "password_compromised_checked_at" + t.string "reset_password_token", limit: 255, comment: "sensitive=true" + t.datetime "reset_password_sent_at", precision: nil, comment: "sensitive=false" + t.datetime "created_at", precision: nil, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, comment: "sensitive=false" + t.datetime "confirmed_at", precision: nil, comment: "sensitive=false" + t.integer "second_factor_attempts_count", default: 0, comment: "sensitive=false" + t.string "uuid", limit: 255, null: false, comment: "sensitive=false" + t.datetime "second_factor_locked_at", precision: nil, comment: "sensitive=false" + t.datetime "phone_confirmed_at", precision: nil, comment: "sensitive=false" + t.string "direct_otp", comment: "sensitive=true" + t.datetime "direct_otp_sent_at", precision: nil, comment: "sensitive=false" + t.string "unique_session_id", comment: "sensitive=false" + t.integer "otp_delivery_preference", default: 0, null: false, comment: "sensitive=false" + t.string "encrypted_password_digest", default: "", comment: "sensitive=true" + t.string "encrypted_recovery_code_digest", default: "", comment: "sensitive=true" + t.datetime "remember_device_revoked_at", precision: nil, comment: "sensitive=false" + t.string "email_language", limit: 10, comment: "sensitive=false" + t.datetime "accepted_terms_at", precision: nil, comment: "sensitive=false" + t.datetime "encrypted_recovery_code_digest_generated_at", precision: nil, comment: "sensitive=false" + t.datetime "suspended_at", comment: "sensitive=false" + t.datetime "reinstated_at", comment: "sensitive=false" + t.string "encrypted_password_digest_multi_region", comment: "sensitive=false" + t.string "encrypted_recovery_code_digest_multi_region", comment: "sensitive=false" + t.datetime "second_mfa_reminder_dismissed_at", comment: "sensitive=false" + t.datetime "piv_cac_recommended_dismissed_at", comment: "sensitive=false" + t.datetime "sign_in_new_device_at", comment: "sensitive=false" + t.datetime "password_compromised_checked_at", comment: "sensitive=false" t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true t.index ["sign_in_new_device_at"], name: "index_users_on_sign_in_new_device_at" t.index ["uuid"], name: "index_users_on_uuid", unique: true end create_table "usps_confirmation_codes", force: :cascade do |t| - t.integer "profile_id", null: false - t.string "otp_fingerprint", null: false - t.datetime "code_sent_at", precision: nil, default: -> { "CURRENT_TIMESTAMP" }, null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.datetime "reminder_sent_at", precision: nil + t.integer "profile_id", null: false, comment: "sensitive=false" + t.string "otp_fingerprint", null: false, comment: "sensitive=true" + t.datetime "code_sent_at", precision: nil, default: -> { "CURRENT_TIMESTAMP" }, null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "reminder_sent_at", precision: nil, comment: "sensitive=false" t.index ["otp_fingerprint"], name: "index_usps_confirmation_codes_on_otp_fingerprint" t.index ["profile_id"], name: "index_usps_confirmation_codes_on_profile_id" t.index ["reminder_sent_at"], name: "index_usps_confirmation_codes_on_reminder_sent_at" end create_table "usps_confirmations", id: :serial, force: :cascade do |t| - t.text "entry", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.text "entry_multi_region" + t.text "entry", null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.text "entry_multi_region", comment: "sensitive=false" end create_table "webauthn_configurations", force: :cascade do |t| - t.bigint "user_id", null: false - t.string "name", null: false - t.text "credential_id", null: false - t.text "credential_public_key", null: false - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.boolean "platform_authenticator" - t.string "transports", array: true - t.jsonb "authenticator_data_flags" - t.string "aaguid" + t.bigint "user_id", null: false, comment: "sensitive=false" + t.string "name", null: false, comment: "sensitive=false" + t.text "credential_id", null: false, comment: "sensitive=false" + t.text "credential_public_key", null: false, comment: "sensitive=false" + t.datetime "created_at", precision: nil, null: false, comment: "sensitive=false" + t.datetime "updated_at", precision: nil, null: false, comment: "sensitive=false" + t.boolean "platform_authenticator", comment: "sensitive=false" + t.string "transports", comment: "sensitive=false", array: true + t.jsonb "authenticator_data_flags", comment: "sensitive=false" + t.string "aaguid", comment: "sensitive=false" t.index ["user_id"], name: "index_webauthn_configurations_on_user_id" end diff --git a/lib/tasks/column_comment_checker.rake b/lib/tasks/column_comment_checker.rake new file mode 100644 index 00000000000..56184ea1dcd --- /dev/null +++ b/lib/tasks/column_comment_checker.rake @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +namespace :db do + desc 'Check for columns with sensitivity comments' + task check_for_sensitive_columns: :environment do + puts 'Checking for columns with sensitivity comments...' + tables = ActiveRecord::Base.connection.tables - %w[schema_migrations ar_internal_metadata] + missing_columns = [] + + tables.each do |table| + ActiveRecord::Base.connection.columns(table).each do |column| + next if column.name == 'id' + + if !column.comment&.match?(/sensitive=(true|false)/i) + missing_columns << "#{table}##{column.name}" + end + end + end + + if missing_columns.any? + puts 'Columns with sensitivity comments found:' + missing_columns.each { |column| puts column } + puts <<-INFO + In your migration, add 'comment: sensitive=false'(or true for sensitive data) + to all of the listed columns." + INFO + else + puts 'All columns have sensitivity comments.' + end + end +end diff --git a/spec/db/schema_spec.rb b/spec/db/schema_spec.rb new file mode 100644 index 00000000000..11bd61b29dd --- /dev/null +++ b/spec/db/schema_spec.rb @@ -0,0 +1,30 @@ +require 'rails_helper' + +RSpec.describe 'db:check_for_sensitive_columns' do + before :all do + Rake.application.rake_require 'tasks/column_comment_checker' + Rake::Task.define_task(:environment) + end + + let(:task) { Rake::Task['db:check_for_sensitive_columns'] } + + it 'checks for columns with sensitivity comments' do + expect { task.execute }.to output(/All columns have sensitivity comments./).to_stdout + end + + context 'when a column is missing a sensitivity comment' do + before do + ActiveRecord::Base.connection.add_column :users, :test_column, :string + end + + after do + ActiveRecord::Base.connection.remove_column :users, :test_column + end + + it 'displays the missing column directions' do + expect { task.execute }.to output( + /In your migration, add 'comment: sensitive=false'\(or true for sensitive data\)/, + ).to_stdout + end + end +end From b2e9c467bd1b1d0ff22a8dbc56a42420578c2543 Mon Sep 17 00:00:00 2001 From: Gina <125507397+gina-yamada@users.noreply.github.com> Date: Tue, 8 Oct 2024 08:15:12 -0600 Subject: [PATCH 03/12] LG-13951 Increase Version of @18f/identity-address-search (#11322) * Increased version of @18f/identity-address-search * changelog: Internal, In-person proofing, Increased @18f/identity-address-search version number to next major version * Update changelog for address-search package * Update address-search changelog * Changed version to be minor rather than major --- .../packages/address-search/CHANGELOG.md | 17 ++++++++++++++++- .../packages/address-search/package.json | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/app/javascript/packages/address-search/CHANGELOG.md b/app/javascript/packages/address-search/CHANGELOG.md index a87c968e890..5c682803c51 100644 --- a/app/javascript/packages/address-search/CHANGELOG.md +++ b/app/javascript/packages/address-search/CHANGELOG.md @@ -1,5 +1,20 @@ # `Change Log` -## 1.0.0 +## v3.2.0 (2024-10-01) + +### New feature + +- Add erroneous character checking to address search field +- Display unsupported character error message + +## v3.1.1 (2023-10-03) + +- Conditionally renders "Enter an address to find a Post Office near you." + +## v.3.0.0 (2023-09-25) + +- Make @18f/identity-i18n a peer dependency + +## 1.0.0 (2023-05-18) - Initial release diff --git a/app/javascript/packages/address-search/package.json b/app/javascript/packages/address-search/package.json index 4ab55f98a1b..8afaaa3524d 100644 --- a/app/javascript/packages/address-search/package.json +++ b/app/javascript/packages/address-search/package.json @@ -1,6 +1,6 @@ { "name": "@18f/identity-address-search", - "version": "3.1.1", + "version": "3.2.0", "type": "module", "private": false, "files": [ From d4800ea963d8c2985cf02c6876d0d640e0fa0d91 Mon Sep 17 00:00:00 2001 From: Alex Bradley Date: Tue, 8 Oct 2024 12:50:25 -0400 Subject: [PATCH 04/12] Replace alt text on capture button with new alt text (#11310) * Replace alt text on capture button with new alt text Changed the alt text to "Photo taken. Submit photo" * add changelog changelog: User-Facing Improvements, Doc Auth, change alt text to selfie green checkmark --- config/locales/en.yml | 2 +- config/locales/es.yml | 2 +- config/locales/fr.yml | 2 +- config/locales/zh.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index c0a7e672801..471fca4dacb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -645,7 +645,7 @@ doc_auth.info.selfie_capture_status.too_many_faces: Too many faces doc_auth.info.selfie_capture.action.capture: Take photo doc_auth.info.selfie_capture.action.close: close doc_auth.info.selfie_capture.action.retake: retake -doc_auth.info.selfie_capture.action.submit: Use this photo +doc_auth.info.selfie_capture.action.submit: Photo taken. Submit photo. doc_auth.info.selfie_capture.intro: Camera is on, ready for selfie doc_auth.info.ssn: We need your Social Security number to verify your name, date of birth and address. doc_auth.info.stepping_up_html: Verify your identity again to access this service. %{link_html} diff --git a/config/locales/es.yml b/config/locales/es.yml index d6047a4a6ee..32d1903b196 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -656,7 +656,7 @@ doc_auth.info.selfie_capture_status.too_many_faces: Demasiados rostros doc_auth.info.selfie_capture.action.capture: Tomar la foto doc_auth.info.selfie_capture.action.close: Cerrar doc_auth.info.selfie_capture.action.retake: Volver a tomar foto -doc_auth.info.selfie_capture.action.submit: Usar esta foto +doc_auth.info.selfie_capture.action.submit: Se tomó la foto. Enviar la foto. doc_auth.info.selfie_capture.intro: Cámara encendida, lista para tomar selfie doc_auth.info.ssn: Necesitamos su número de Seguro Social para verificar su nombre, fecha de nacimiento y dirección. doc_auth.info.stepping_up_html: Verifique de nuevo su identidad para acceder a este servicio. %{link_html} diff --git a/config/locales/fr.yml b/config/locales/fr.yml index cdf474933a0..679e6d8a731 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -645,7 +645,7 @@ doc_auth.info.selfie_capture_status.too_many_faces: Trop de visages doc_auth.info.selfie_capture.action.capture: Prendre une photo doc_auth.info.selfie_capture.action.close: éteindre doc_auth.info.selfie_capture.action.retake: nouvelle photo -doc_auth.info.selfie_capture.action.submit: Utiliser cette photo +doc_auth.info.selfie_capture.action.submit: Photo prise. Envoyer la photo. doc_auth.info.selfie_capture.intro: Caméra prête à prendre la photo doc_auth.info.ssn: Nous avons besoin de votre numéro de sécurité sociale pour confirmer vos nom, date de naissance et adresse. doc_auth.info.stepping_up_html: Veuillez confirmer à nouveau votre identité pour accéder à ce service. %{link_html} diff --git a/config/locales/zh.yml b/config/locales/zh.yml index 63847bd1b8f..f57fc759b54 100644 --- a/config/locales/zh.yml +++ b/config/locales/zh.yml @@ -656,7 +656,7 @@ doc_auth.info.selfie_capture_status.too_many_faces: 太多张面孔 doc_auth.info.selfie_capture.action.capture: 拍照 doc_auth.info.selfie_capture.action.close: 关闭 doc_auth.info.selfie_capture.action.retake: 重拍 -doc_auth.info.selfie_capture.action.submit: 使用这张照片 +doc_auth.info.selfie_capture.action.submit: 照片拍摄完毕。提交照片。 doc_auth.info.selfie_capture.intro: 相机已打开,准备自拍 doc_auth.info.ssn: 我们需要你的社会保障号码来证实你的姓名、生日和地址。 doc_auth.info.stepping_up_html: 再次验证你的身份以使用这项服务。 %{link_html} From 0fb7e7003d2264a4442227745cf55c63ba61a02c Mon Sep 17 00:00:00 2001 From: John Maxwell Date: Tue, 8 Oct 2024 13:33:23 -0400 Subject: [PATCH 05/12] Jmax/lg 14005 socure in hybrid handoff (#11289) * created socure_document_capture_controllers * created socure document request * created socure_document_capture show view * set up routing to work correctly Co-authored-by: Amir Reavis-Bey Co-authored-by: Alex Bradley Co-authored-by: Amir Reavis-Bey --- .../idv/document_capture_controller.rb | 7 +- .../idv/hybrid_handoff_controller.rb | 7 +- .../idv/hybrid_mobile/entry_controller.rb | 8 +- .../socure/document_capture_controller.rb | 49 +++++ .../idv/socure/document_capture_controller.rb | 81 ++++++++ app/policies/idv/flow_policy.rb | 1 + app/services/doc_auth/socure/request.rb | 113 ++++++++++++ .../socure/requests/document_request.rb | 64 +++++++ .../socure/document_capture/show.html.erb | 14 ++ .../idv/socure/document_capture/show.html.erb | 14 ++ config/application.yml.default | 1 + config/routes.rb | 4 + ...ure_docv_id_to_document_capture_session.rb | 5 + db/schema.rb | 1 + lib/identity_config.rb | 2 + .../idv/document_capture_controller_spec.rb | 18 ++ .../hybrid_mobile/entry_controller_spec.rb | 76 ++++++-- .../document_capture_controller_spec.rb | 174 ++++++++++++++++++ .../document_capture_controller_spec.rb | 174 ++++++++++++++++++ spec/controllers/idv/ssn_controller_spec.rb | 9 +- spec/requests/csp_spec.rb | 8 +- spec/services/doc_auth/socure/request_spec.rb | 48 +++++ .../socure/requests/document_request_spec.rb | 103 +++++++++++ 23 files changed, 952 insertions(+), 29 deletions(-) create mode 100644 app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb create mode 100644 app/controllers/idv/socure/document_capture_controller.rb create mode 100644 app/services/doc_auth/socure/request.rb create mode 100644 app/services/doc_auth/socure/requests/document_request.rb create mode 100644 app/views/idv/hybrid_mobile/socure/document_capture/show.html.erb create mode 100644 app/views/idv/socure/document_capture/show.html.erb create mode 100644 db/primary_migrate/20240926153042_add_socure_docv_id_to_document_capture_session.rb create mode 100644 spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb create mode 100644 spec/controllers/idv/socure/document_capture_controller_spec.rb create mode 100644 spec/services/doc_auth/socure/request_spec.rb create mode 100644 spec/services/doc_auth/socure/requests/document_request_spec.rb diff --git a/app/controllers/idv/document_capture_controller.rb b/app/controllers/idv/document_capture_controller.rb index 57f15a18acc..df9df2552bd 100644 --- a/app/controllers/idv/document_capture_controller.rb +++ b/app/controllers/idv/document_capture_controller.rb @@ -19,7 +19,12 @@ def show Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). call('document_capture', :view, true) - render :show, locals: extra_view_variables + case doc_auth_vendor + when Idp::Constants::Vendors::SOCURE + redirect_to idv_socure_document_capture_url + when Idp::Constants::Vendors::LEXIS_NEXIS, Idp::Constants::Vendors::MOCK + render :show, locals: extra_view_variables + end end def update diff --git a/app/controllers/idv/hybrid_handoff_controller.rb b/app/controllers/idv/hybrid_handoff_controller.rb index ae41a608240..f5144913502 100644 --- a/app/controllers/idv/hybrid_handoff_controller.rb +++ b/app/controllers/idv/hybrid_handoff_controller.rb @@ -22,15 +22,14 @@ def show @selfie_required = idv_session.selfie_check_required - analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments) - Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]).call( 'upload', :view, true ) - + analytics.idv_doc_auth_hybrid_handoff_visited(**analytics_arguments) # reset if we visit or come back idv_session.skip_doc_auth_from_handoff = nil + render :show, locals: extra_view_variables end @@ -59,7 +58,7 @@ def self.step_info Idv::StepInfo.new( key: :hybrid_handoff, controller: self, - next_steps: [:link_sent, :document_capture], + next_steps: [:link_sent, :document_capture, :socure_document_capture], preconditions: ->(idv_session:, user:) { idv_session.idv_consent_given? && (self.selected_remote(idv_session: idv_session) || # from opt-in screen diff --git a/app/controllers/idv/hybrid_mobile/entry_controller.rb b/app/controllers/idv/hybrid_mobile/entry_controller.rb index ecb93db2cce..fabf2f413de 100644 --- a/app/controllers/idv/hybrid_mobile/entry_controller.rb +++ b/app/controllers/idv/hybrid_mobile/entry_controller.rb @@ -7,13 +7,19 @@ module HybridMobile class EntryController < ApplicationController include Idv::AvailabilityConcern include HybridMobileConcern + include DocAuthVendorConcern def show return handle_invalid_document_capture_session if !validate_document_capture_session_id return handle_invalid_document_capture_session if !validate_document_capture_user_id - redirect_to idv_hybrid_mobile_document_capture_url + case doc_auth_vendor + when Idp::Constants::Vendors::SOCURE + redirect_to idv_hybrid_mobile_socure_document_capture_url + when Idp::Constants::Vendors::MOCK, Idp::Constants::Vendors::LEXIS_NEXIS + redirect_to idv_hybrid_mobile_document_capture_url + end end private diff --git a/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb new file mode 100644 index 00000000000..8c0d2b29fc6 --- /dev/null +++ b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Idv + module HybridMobile + module Socure + class DocumentCaptureController < ApplicationController + include Idv::AvailabilityConcern + include DocumentCaptureConcern + include Idv::HybridMobile::HybridMobileConcern + + before_action :check_valid_document_capture_session, except: [:update] + + def show + Funnel::DocAuth::RegisterStep.new(document_capture_user.id, sp_session[:issuer]). + call('hybrid_mobile_socure_document_capture', :view, true) + + # document request + document_request = DocAuth::Socure::Requests::DocumentRequest.new( + document_capture_session_uuid: document_capture_session_uuid, + redirect_url: idv_hybrid_mobile_socure_document_capture_url, + language: I18n.locale, + ) + document_response = document_request.fetch + + @document_request = document_request + @document_response = document_response + @url = document_response.dig(:data, :url) + + document_capture_session = DocumentCaptureSession.find_by( + uuid: document_capture_session_uuid, + ) + document_capture_session.socure_docv_token = document_response.dig( + :data, + :docvTransactionToken, + ) + document_capture_session.save + + # useful for analytics + @msg = document_response[:msg] + @reference_id = document_response[:referenceId] + end + + def update + render plain: 'stub to ensure Socure callback exists and the route works' + end + end + end + end +end diff --git a/app/controllers/idv/socure/document_capture_controller.rb b/app/controllers/idv/socure/document_capture_controller.rb new file mode 100644 index 00000000000..d34f7ebb69f --- /dev/null +++ b/app/controllers/idv/socure/document_capture_controller.rb @@ -0,0 +1,81 @@ +# frozen_string_literal: true + +module Idv + module Socure + class DocumentCaptureController < ApplicationController + include Idv::AvailabilityConcern + include IdvStepConcern + include DocumentCaptureConcern + + before_action :confirm_not_rate_limited + before_action :confirm_step_allowed + + # reconsider and maybe remove these when implementing the real + # update handler + skip_before_action :redirect_unless_idv_session_user, only: [:update] + skip_before_action :confirm_two_factor_authenticated, only: [:update] + skip_before_action :confirm_idv_needed, only: [:update] + skip_before_action :confirm_not_rate_limited, only: [:update] + skip_before_action :confirm_step_allowed, only: [:update] + + def show + Funnel::DocAuth::RegisterStep.new(current_user.id, sp_session[:issuer]). + call('socure_document_capture', :view, true) + + # document request + document_request = DocAuth::Socure::Requests::DocumentRequest.new( + document_capture_session_uuid: document_capture_session_uuid, + redirect_url: idv_socure_document_capture_url, + language: I18n.locale, + ) + + document_response = document_request.fetch + + @document_request = document_request + @document_response = document_response + @url = document_response.dig(:data, :url) + + document_capture_session = DocumentCaptureSession.find_by( + uuid: document_capture_session_uuid, + ) + + document_capture_session.socure_docv_token = document_response.dig( + :data, + :docvTransactionToken, + ) + document_capture_session.save + + # useful for analytics + @msg = document_response[:msg] + @reference_id = document_response[:referenceId] + end + + def update + render plain: 'stub to ensure Socure callback exists and the route works' + end + + def self.step_info + Idv::StepInfo.new( + key: :socure_document_capture, + controller: self, + next_steps: [:ssn, :ipp_ssn], + preconditions: ->(idv_session:, user:) { + idv_session.flow_path == 'standard' && ( + # mobile + idv_session.skip_doc_auth_from_handoff || + idv_session.skip_hybrid_handoff || + idv_session.skip_doc_auth || + idv_session.skip_doc_auth_from_how_to_verify || + !idv_session.selfie_check_required || + idv_session.desktop_selfie_test_mode_enabled? + ) + }, + undo_step: ->(idv_session:, user:) do + idv_session.pii_from_doc = nil + idv_session.invalidate_in_person_pii_from_user! + end, + ) + end + end + end +end diff --git a/app/policies/idv/flow_policy.rb b/app/policies/idv/flow_policy.rb index 31be5aaed63..fb664bd4991 100644 --- a/app/policies/idv/flow_policy.rb +++ b/app/policies/idv/flow_policy.rb @@ -58,6 +58,7 @@ def steps hybrid_handoff: Idv::HybridHandoffController.step_info, link_sent: Idv::LinkSentController.step_info, document_capture: Idv::DocumentCaptureController.step_info, + socure_document_capture: Idv::Socure::DocumentCaptureController.step_info, ipp_address: Idv::InPerson::AddressController.step_info, ssn: Idv::SsnController.step_info, ipp_ssn: Idv::InPerson::SsnController.step_info, diff --git a/app/services/doc_auth/socure/request.rb b/app/services/doc_auth/socure/request.rb new file mode 100644 index 00000000000..64fa97efd05 --- /dev/null +++ b/app/services/doc_auth/socure/request.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +module DocAuth + module Socure + class Request + def fetch + # return DocAuth::Response with DocAuth::Error if workflow is invalid + http_response = send_http_request + return handle_invalid_response(http_response) unless http_response.success? + + handle_http_response(http_response) + rescue Faraday::ConnectionFailed, Faraday::TimeoutError, Faraday::SSLError => e + handle_connection_error(exception: e) + end + + def metric_name + raise NotImplementedError + end + + private + + def send_http_request + case method.downcase.to_sym + when :post + send_http_post_request + when :get + send_http_get_request + end + end + + def handle_http_response(_response) + raise NotImplementedError + end + + def handle_invalid_response(http_response) + begin + if http_response.body.present? + warn(http_response.body) + JSON.parse(http_response.body) + else + {} + end + rescue JSON::JSONError + {} + end + end + + def send_http_get_request + faraday_connection.get do |req| + req.options.context = { service_name: metric_name } + req.params = params if params&.any? + end + end + + def send_http_post_request + faraday_connection.post do |req| + req.options.context = { service_name: metric_name } + req.body = body + end + end + + def faraday_connection + retry_options = { + max: 2, + interval: 0.05, + interval_randomness: 0.5, + backoff_factor: 2, + retry_statuses: [404, 500], + retry_block: lambda do |env:, options:, retry_count:, exception:, will_retry_in:| + NewRelic::Agent.notice_error(exception, custom_params: { retry: retry_count }) + end, + } + + Faraday.new(url: url.to_s, headers: request_headers) do |conn| + conn.request :retry, retry_options + conn.request :instrumentation, name: 'request_metric.faraday' + conn.adapter :net_http + conn.options.timeout = timeout + conn.options.read_timeout = timeout + conn.options.open_timeout = timeout + conn.options.write_timeout = timeout + end + end + + def endpoint + raise NotImplementedError + end + + def method + :get + end + + def url + URI.join(endpoint) + end + + def request_headers(extras = {}) + { + 'Content-Type': 'application/json', + Authorization: "SocureApiKey #{IdentityConfig.store.socure_idplus_api_key}", + }.merge(extras) + end + + def timeout + 60 + end + + def params + {} + end + end + end +end diff --git a/app/services/doc_auth/socure/requests/document_request.rb b/app/services/doc_auth/socure/requests/document_request.rb new file mode 100644 index 00000000000..796b6cb22b2 --- /dev/null +++ b/app/services/doc_auth/socure/requests/document_request.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +module DocAuth + module Socure + module Requests + class DocumentRequest < DocAuth::Socure::Request + attr_reader :document_type, :redirect_url, :document_capture_session_uuid, :language + + def initialize( + document_capture_session_uuid:, + redirect_url:, + language:, + document_type: 'license' + ) + @document_capture_session_uuid = document_capture_session_uuid + @redirect_url = redirect_url + @document_type = document_type + @language = language + end + + private + + def lang(language) + return 'zh-cn' if language == :zh + language + end + + def body + redirect = { + method: 'POST', + url: redirect_url, + } + + redirect = nil if Rails.env.development? + + { + config: { + documentType: document_type, + redirect: redirect, + language: lang(language), + }, + customerUserId: document_capture_session_uuid, + }.to_json + end + + def handle_http_response(http_response) + JSON.parse(http_response.body, symbolize_names: true) + end + + def method + :post + end + + def endpoint + IdentityConfig.store.socure_document_request_endpoint + end + + def metric_name + 'socure_doc_auth_docv' + end + end + end + end +end diff --git a/app/views/idv/hybrid_mobile/socure/document_capture/show.html.erb b/app/views/idv/hybrid_mobile/socure/document_capture/show.html.erb new file mode 100644 index 00000000000..270691d4fb4 --- /dev/null +++ b/app/views/idv/hybrid_mobile/socure/document_capture/show.html.erb @@ -0,0 +1,14 @@ +<% self.title = t('titles.doc_auth.doc_capture') %> +<% if @url.present? %> + Open this link on your mobile device to open the Socure Capture App +<% else %> + <% if @reference_id.present? %> +

ReferenceId: <%= @reference_id %>

+ <% end %> + <% if @msg.present? %> +

ErrorMessage: <%= @msg %>

+ <% end %> +

Socure is not working

+

Document Request <%= @document_request.inspect %>

+

Document Response <%= @document_response %>

+<% end %> diff --git a/app/views/idv/socure/document_capture/show.html.erb b/app/views/idv/socure/document_capture/show.html.erb new file mode 100644 index 00000000000..270691d4fb4 --- /dev/null +++ b/app/views/idv/socure/document_capture/show.html.erb @@ -0,0 +1,14 @@ +<% self.title = t('titles.doc_auth.doc_capture') %> +<% if @url.present? %> + Open this link on your mobile device to open the Socure Capture App +<% else %> + <% if @reference_id.present? %> +

ReferenceId: <%= @reference_id %>

+ <% end %> + <% if @msg.present? %> +

ErrorMessage: <%= @msg %>

+ <% end %> +

Socure is not working

+

Document Request <%= @document_request.inspect %>

+

Document Response <%= @document_response %>

+<% end %> diff --git a/config/application.yml.default b/config/application.yml.default index ef8d81c4a72..e300932af8d 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -346,6 +346,7 @@ sign_in_user_id_per_ip_attempt_window_exponential_factor: 1.1 sign_in_user_id_per_ip_attempt_window_in_minutes: 720 sign_in_user_id_per_ip_attempt_window_max_minutes: 43_200 sign_in_user_id_per_ip_max_attempts: 50 +socure_document_request_endpoint: '' socure_idplus_api_key: '' socure_idplus_base_url: '' socure_idplus_timeout_in_seconds: 5 diff --git a/config/routes.rb b/config/routes.rb index f998fcba4fd..6952004af90 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -350,12 +350,16 @@ put '/how_to_verify' => 'how_to_verify#update' get '/document_capture' => 'document_capture#show' put '/document_capture' => 'document_capture#update' + get '/socure/document_capture' => 'socure/document_capture#show' + post '/socure/document_capture' => 'socure/document_capture#update' # This route is included in SMS messages sent to users who start the IdV hybrid flow. It # should be kept short, and should not include underscores ("_"). get '/documents' => 'hybrid_mobile/entry#show', as: :hybrid_mobile_entry get '/hybrid_mobile/document_capture' => 'hybrid_mobile/document_capture#show' put '/hybrid_mobile/document_capture' => 'hybrid_mobile/document_capture#update' get '/hybrid_mobile/capture_complete' => 'hybrid_mobile/capture_complete#show' + get '/hybrid_mobile/socure/document_capture' => 'hybrid_mobile/socure/document_capture#show' + post '/hybrid_mobile/socure/document_capture' => 'hybrid_mobile/socure/document_capture#update' get '/hybrid_handoff' => 'hybrid_handoff#show' put '/hybrid_handoff' => 'hybrid_handoff#update' get '/link_sent' => 'link_sent#show' diff --git a/db/primary_migrate/20240926153042_add_socure_docv_id_to_document_capture_session.rb b/db/primary_migrate/20240926153042_add_socure_docv_id_to_document_capture_session.rb new file mode 100644 index 00000000000..84ac01eae46 --- /dev/null +++ b/db/primary_migrate/20240926153042_add_socure_docv_id_to_document_capture_session.rb @@ -0,0 +1,5 @@ +class AddSocureDocvIdToDocumentCaptureSession < ActiveRecord::Migration[7.1] + def change + add_column :document_capture_sessions, :socure_docv_token, :string, comment: 'sensitive=false' + end +end diff --git a/db/schema.rb b/db/schema.rb index bd6c67d0f62..ed1586ab63f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -191,6 +191,7 @@ t.datetime "cancelled_at", precision: nil, comment: "sensitive=false" t.boolean "ocr_confirmation_pending", default: false, comment: "sensitive=false" t.string "last_doc_auth_result", comment: "sensitive=false" + t.string "socure_docv_token", comment: "sensitive=false" t.index ["result_id"], name: "index_document_capture_sessions_on_result_id" t.index ["user_id"], name: "index_document_capture_sessions_on_user_id" t.index ["uuid"], name: "index_document_capture_sessions_on_uuid" diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 281618894f5..36a70f42100 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -400,6 +400,8 @@ def self.store config.add(:sign_in_recaptcha_percent_tested, type: :integer) config.add(:sign_in_recaptcha_score_threshold, type: :float) config.add(:skip_encryption_allowed_list, type: :json) + config.add(:socure_document_request_endpoint, type: :string) + config.add(:socure_idplus_api_key, type: :string) config.add(:socure_webhook_enabled, type: :boolean) config.add(:socure_webhook_secret_key, type: :string) config.add(:socure_webhook_secret_key_queue, type: :json) diff --git a/spec/controllers/idv/document_capture_controller_spec.rb b/spec/controllers/idv/document_capture_controller_spec.rb index 41529df2ddb..13e7f5b8712 100644 --- a/spec/controllers/idv/document_capture_controller_spec.rb +++ b/spec/controllers/idv/document_capture_controller_spec.rb @@ -15,6 +15,7 @@ let(:document_capture_session_uuid) { document_capture_session&.uuid } let(:user) { create(:user) } + let(:ab_test_args) { {} } # selfie related test flags let(:sp_selfie_enabled) { false } @@ -32,6 +33,14 @@ allow(controller).to receive(:resolved_authn_context_result). and_return(resolved_authn_context) subject.idv_session.flow_path = flow_path + allow(subject).to receive(:ab_test_analytics_buckets).and_return(ab_test_args) + + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) end describe '#step_info' do @@ -107,6 +116,15 @@ } end + before do + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) + end + it 'has non-nil presenter' do get :show expect(assigns(:presenter)).to be_kind_of(Idv::InPerson::UspsFormPresenter) diff --git a/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb index bc70f6378d4..1a5f2727c03 100644 --- a/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb +++ b/spec/controllers/idv/hybrid_mobile/entry_controller_spec.rb @@ -12,9 +12,12 @@ end let(:session_uuid) { document_capture_session.uuid } + let(:idv_vendor) { Idp::Constants::Vendors::MOCK } before do stub_analytics + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor) end context 'with no session' do @@ -62,24 +65,47 @@ let(:session) do {} end + let(:idv_vendor) { Idp::Constants::Vendors::MOCK } before do allow(controller).to receive(:session).and_return(session) get :show, params: { 'document-capture-session': session_uuid } end - it 'redirects to the first step' do - expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + context 'doc auth vendor is socure' do + let(:idv_vendor) { Idp::Constants::Vendors::SOCURE } + + it 'redirects to the first step' do + expect(response).to redirect_to idv_hybrid_mobile_socure_document_capture_url + end + + it 'logs an analytics event' do + expect(@analytics).to have_logged_event( + 'Doc Auth', + hash_including( + success: true, + doc_capture_user_id?: false, + ), + ) + end end - it 'logs an analytics event' do - expect(@analytics).to have_logged_event( - 'Doc Auth', - hash_including( - success: true, - doc_capture_user_id?: false, - ), - ) + context 'doc auth vendor is lexis nexis' do + let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS } + + it 'redirects to the first step' do + expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + end + + it 'logs an analytics event' do + expect(@analytics).to have_logged_event( + 'Doc Auth', + hash_including( + success: true, + doc_capture_user_id?: false, + ), + ) + end end context 'but we already had a session' do @@ -111,8 +137,20 @@ ) end - it 'redirects to the document capture screen' do - expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + context 'doc auth vendor is socure' do + let(:idv_vendor) { Idp::Constants::Vendors::SOCURE } + + it 'redirects to the socure document capture screen' do + expect(response).to redirect_to idv_hybrid_mobile_socure_document_capture_url + end + end + + context 'doc auth vendor is lexis nexis' do + let(:idv_vendor) { Idp::Constants::Vendors::LEXIS_NEXIS } + + it 'redirects to the document capture screen' do + expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + end end end end @@ -125,8 +163,18 @@ get :show end - it 'redirects to the first step' do - expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + context 'doc auth vendor is socure' do + let(:idv_vendor) { Idp::Constants::Vendors::SOCURE } + + it 'redirects to the first step' do + expect(response).to redirect_to idv_hybrid_mobile_socure_document_capture_url + end + end + + context 'doc auth vendor is lexis nexis' do + it 'redirects to the first step' do + expect(response).to redirect_to idv_hybrid_mobile_document_capture_url + end end end end diff --git a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb new file mode 100644 index 00000000000..ad6331240c9 --- /dev/null +++ b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb @@ -0,0 +1,174 @@ +require 'rails_helper' + +RSpec.describe Idv::HybridMobile::Socure::DocumentCaptureController do + include FlowPolicyHelper + + let(:idv_vendor) { Idp::Constants::Vendors::SOCURE } + let(:fake_socure_endpoint) { 'https://fake-socure.com' } + let(:user) { create(:user) } + let(:stored_result) { nil } + + let(:document_capture_session) do + DocumentCaptureSession.create( + user: user, + requested_at: Time.zone.now, + ) + end + let(:document_capture_session_uuid) { document_capture_session&.uuid } + + before do + allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + and_return(fake_socure_endpoint) + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor) + + allow(subject).to receive(:stored_result).and_return(stored_result) + end + + describe 'before_actions' do + it 'checks valid document capture session' do + expect(subject).to have_actions( + :before, + :check_valid_document_capture_session, + ) + end + end + + describe '#show' do + let(:request_class) { DocAuth::Socure::Requests::DocumentRequest } + + let(:expected_language) { :en } + let(:response_body) { {} } + + before do + stub_request(:post, fake_socure_endpoint).to_return( + status: 200, + body: JSON.generate(response_body), + ) + + session[:doc_capture_user_id] = user&.id + session[:document_capture_session_uuid] = document_capture_session_uuid + end + + context 'with no user id in session' do + let(:document_capture_session) { nil } + let(:user) { nil } + + it 'redirects to root' do + get :show + expect(response).to redirect_to root_url + end + end + + context 'happy path' do + let(:response_redirect_url) { 'https://idv.test/dance' } + let(:docv_transaction_token) { '176dnc45d-2e34-46f3-82217-6f540ae90673' } + let(:response_body) do + { + referenceId: '123ab45d-2e34-46f3-8d17-6f540ae90303', + data: { + eventId: 'zoYgIxEZUbXBoocYAnbb5DrT', + customerUserId: document_capture_session_uuid, + docvTransactionToken: docv_transaction_token, + qrCode: 'data:image/png;base64,iVBO......K5CYII=', + url: response_redirect_url, + }, + } + end + + before do + allow(I18n).to receive(:locale).and_return(expected_language) + allow(request_class).to receive(:new).and_call_original + get(:show) + end + + it 'creates a DocumentRequest' do + expect(request_class).to have_received(:new). + with( + document_capture_session_uuid: document_capture_session_uuid, + redirect_url: idv_hybrid_mobile_socure_document_capture_url, + language: expected_language, + ) + end + + context 'language is english' do + let(:expected_language) { :en } + + it 'does the correct POST to Socure' do + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with( + body: JSON.generate( + { + config: { + documentType: 'license', + redirect: { + method: 'POST', + url: idv_hybrid_mobile_socure_document_capture_url, + }, + language: expected_language, + }, + customerUserId: document_capture_session_uuid, + }, + ), + ) + end + end + + context 'language is chinese and language should be zn-ch' do + let(:expected_language) { :zh } + + it 'does the correct POST to Socure' do + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with( + body: JSON.generate( + { + config: { + documentType: 'license', + redirect: { + method: 'POST', + url: idv_hybrid_mobile_socure_document_capture_url, + }, + language: 'zh-cn', + }, + customerUserId: document_capture_session_uuid, + }, + ), + ) + end + end + + context 'renders the interstital page' do + render_views + + it 'it includes the socure redirect url' do + expect(response).to have_http_status 200 + expect(response.body).to have_link(href: response_redirect_url) + end + + it 'puts the docvTransactionToken into the document capture session' do + document_capture_session.reload + expect(document_capture_session.socure_docv_token).to eq(docv_transaction_token) + end + end + end + + context 'when we should not redirect because there is no url in the response' do + let(:response_body) { {} } + + it 'does not redirect' do + get(:show) + + expect(response).not_to have_http_status(:redirect) + expect(controller.send(:instance_variable_get, :@url)).not_to be + end + end + end + + describe '#update' do + it 'returns OK (200)' do + post(:update) + + expect(response).to have_http_status(:ok) + end + end +end diff --git a/spec/controllers/idv/socure/document_capture_controller_spec.rb b/spec/controllers/idv/socure/document_capture_controller_spec.rb new file mode 100644 index 00000000000..14d5b05eaf5 --- /dev/null +++ b/spec/controllers/idv/socure/document_capture_controller_spec.rb @@ -0,0 +1,174 @@ +require 'rails_helper' + +RSpec.describe Idv::Socure::DocumentCaptureController do + include FlowPolicyHelper + + let(:idv_vendor) { Idp::Constants::Vendors::SOCURE } + let(:fake_socure_endpoint) { 'https://fake-socure.com' } + let(:user) { create(:user) } + let(:stored_result) { nil } + + let(:document_capture_session) do + DocumentCaptureSession.create( + user: user, + requested_at: Time.zone.now, + ) + end + + before do + allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + and_return(fake_socure_endpoint) + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return(idv_vendor) + + allow(subject).to receive(:stored_result).and_return(stored_result) + + user_session = {} + allow(subject).to receive(:user_session).and_return(user_session) + + subject.idv_session.document_capture_session_uuid = document_capture_session.uuid + end + + describe '#step_info' do + it 'returns a valid StepInfo object' do + expect(described_class.step_info).to be_valid + end + end + + describe 'before_actions' do + it 'includes authentication before_action' do + expect(subject).to have_actions( + :before, + :confirm_two_factor_authenticated, + ) + end + end + + describe '#show' do + let(:request_class) { DocAuth::Socure::Requests::DocumentRequest } + let(:expected_uuid) { document_capture_session.uuid } + let(:expected_language) { :en } + let(:response_body) { {} } + + before do + stub_request(:post, fake_socure_endpoint).to_return( + status: 200, + body: JSON.generate(response_body), + ) + + stub_sign_in(user) + stub_up_to(:hybrid_handoff, idv_session: subject.idv_session) + + subject.idv_session.document_capture_session_uuid = expected_uuid + end + + context 'happy path' do + let(:response_redirect_url) { 'https://idv.test/dance' } + let(:docv_transaction_token) { '176dnc45d-2e34-46f3-82217-6f540ae90673' } + let(:response_body) do + { + referenceId: '123ab45d-2e34-46f3-8d17-6f540ae90303', + data: { + eventId: 'zoYgIxEZUbXBoocYAnbb5DrT', + customerUserId: '121212', + docvTransactionToken: docv_transaction_token, + qrCode: 'data:image/png;base64,iVBO......K5CYII=', + url: response_redirect_url, + }, + } + end + + before do + allow(request_class).to receive(:new).and_call_original + allow(I18n).to receive(:locale).and_return(expected_language) + get(:show) + end + + it 'creates a DocumentRequest' do + expect(request_class).to have_received(:new). + with( + document_capture_session_uuid: expected_uuid, + redirect_url: idv_socure_document_capture_url, + language: expected_language, + ) + end + + context 'language is english' do + let(:expected_language) { :en } + + it 'does the correct POST to Socure' do + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with( + body: JSON.generate( + { + config: { + documentType: 'license', + redirect: { + method: 'POST', + url: idv_socure_document_capture_url, + }, + language: :en, + }, + customerUserId: expected_uuid, + }, + ), + ) + end + end + + context 'language is chinese and language should be zn-ch' do + let(:expected_language) { :zh } + + it 'does the correct POST to Socure' do + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with( + body: JSON.generate( + { + config: { + documentType: 'license', + redirect: { + method: 'POST', + url: idv_socure_document_capture_url, + }, + language: 'zh-cn', + }, + customerUserId: expected_uuid, + }, + ), + ) + end + end + + context 'renders the interstital page' do + render_views + + it 'it includes the socure redirect url' do + expect(response).to have_http_status 200 + expect(response.body).to have_link(href: response_redirect_url) + end + + it 'puts the docvTransactionToken into the document capture session' do + document_capture_session.reload + expect(document_capture_session.socure_docv_token).to eq(docv_transaction_token) + end + end + end + + context 'when we should not redirect because there is no url in the response' do + let(:response_body) { {} } + + it 'does not redirect' do + expect(response).not_to have_http_status(:redirect) + expect(controller.send(:instance_variable_get, :@url)).not_to be + end + end + end + + describe '#update' do + it 'returns OK (200)' do + post(:update) + + expect(response).to have_http_status(:ok) + end + end +end diff --git a/spec/controllers/idv/ssn_controller_spec.rb b/spec/controllers/idv/ssn_controller_spec.rb index 3c85bd46b61..ddd69b18aea 100644 --- a/spec/controllers/idv/ssn_controller_spec.rb +++ b/spec/controllers/idv/ssn_controller_spec.rb @@ -235,9 +235,12 @@ context 'when pii_from_doc is not present' do before do subject.idv_session.pii_from_doc = nil - end - - it 'redirects to DocumentCaptureController on standard flow' do + allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) + allow(IdentityConfig.store).to receive(:doc_auth_vendor_default).and_return( + Idp::Constants::Vendors::LEXIS_NEXIS, + ) put :update expect(response.status).to eq 302 expect(response).to redirect_to idv_document_capture_url diff --git a/spec/requests/csp_spec.rb b/spec/requests/csp_spec.rb index c6cd4378aa4..0a07ae9668f 100644 --- a/spec/requests/csp_spec.rb +++ b/spec/requests/csp_spec.rb @@ -70,9 +70,7 @@ expect(content_security_policy['child-src']).to eq("'self'") expect(content_security_policy['connect-src']).to eq("'self'") expect(content_security_policy['font-src']).to eq("'self' data:") - expect(content_security_policy['form-action']).to eq( - "'self'", - ) + expect(content_security_policy['form-action']).to eq("'self'") expect(content_security_policy['img-src']).to eq( "'self' data: login.gov https://s3.us-west-2.amazonaws.com", ) @@ -90,9 +88,7 @@ content_security_policy = parse_content_security_policy - expect(content_security_policy['form-action']).to eq( - "'self'", - ) + expect(content_security_policy['form-action']).to eq("'self'") end end end diff --git a/spec/services/doc_auth/socure/request_spec.rb b/spec/services/doc_auth/socure/request_spec.rb new file mode 100644 index 00000000000..feedb8318f8 --- /dev/null +++ b/spec/services/doc_auth/socure/request_spec.rb @@ -0,0 +1,48 @@ +require 'rails_helper' + +RSpec.describe DocAuth::Socure::Request do + subject(:request) { described_class.new } + + describe 'a new request' do + it 'exists' do + expect(request).to be + end + end + + describe '#fetch' do + let(:fake_socure_endpoint) { 'https://fake-socure.com/' } + let(:fake_metric_name) { 'fake metric' } + + before do + allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + and_return(fake_socure_endpoint) + allow(request).to receive(:endpoint).and_return(fake_socure_endpoint) + allow(request).to receive(:metric_name).and_return(fake_metric_name) + + stub_request(:get, fake_socure_endpoint).to_return( + status: response_status, + body: response, + ) + end + + context 'with a valid response' do + let(:response) { JSON.generate({ 'url' => 'https://localhost' }) } + let(:response_status) { 200 } + + # Because we have not implemented + # `#handle_http_response`. Remove when we do. + it 'raises a NotImplementedError' do + expect { request.fetch }.to raise_error NotImplementedError + end + end + + context 'with no body in the response' do + let(:response) { nil } + let(:response_status) { 403 } + + it 'returns {}' do + expect(request.fetch).to eq({}) + end + end + end +end diff --git a/spec/services/doc_auth/socure/requests/document_request_spec.rb b/spec/services/doc_auth/socure/requests/document_request_spec.rb new file mode 100644 index 00000000000..46db7c5ed90 --- /dev/null +++ b/spec/services/doc_auth/socure/requests/document_request_spec.rb @@ -0,0 +1,103 @@ +require 'rails_helper' + +RSpec.describe DocAuth::Socure::Requests::DocumentRequest do + let(:document_capture_session_uuid) { 'fake uuid' } + let(:redirect_url) { 'https://idv.test' } + let(:language) { :en } + + subject(:document_request) do + described_class.new( + document_capture_session_uuid:, + redirect_url: redirect_url, + language:, + ) + end + + describe '#fetch' do + let(:document_type) { 'license' } + let(:fake_socure_endpoint) { 'https://fake-socure.com/' } + let(:fake_socure_document_capture_app_url) { 'https://verify.socure.us/something' } + let(:docv_transaction_token) { 'fake docv transaction token' } + let(:fake_socure_response) do + { + referenceId: 'socure-reference-id', + data: { + eventId: 'socure-event-id', + customerUserId: document_capture_session_uuid, + docvTransactionToken: docv_transaction_token, + qrCode: 'qr-code', + url: fake_socure_document_capture_app_url, + }, + } + end + + let(:expected_request_body) do + { + config: + { + documentType: document_type, + redirect: + { + method: 'POST', + url: redirect_url, + }, + language: language, + }, + customerUserId: document_capture_session_uuid, + } + end + let(:fake_socure_status) { 200 } + + before do + allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). + and_return(fake_socure_endpoint) + stub_request(:post, fake_socure_endpoint). + to_return( + status: fake_socure_status, + body: JSON.generate(fake_socure_response), + ) + end + + it 'fetches from the correct url' do + document_request.fetch + + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with(body: JSON.generate(expected_request_body)) + end + + it 'passes the response through' do + response = document_request.fetch + + expect(response).to eq(fake_socure_response) + end + + context 'when the language is Spanish' do + let(:language) { :es } + + it 'includes the correct language in the request_body' do + document_request.fetch + + expect(WebMock).to have_requested(:post, fake_socure_endpoint). + with(body: JSON.generate(expected_request_body)) + end + end + + context 'we get a 403 back' do + let(:fake_socure_response) { {} } + let(:fake_socure_status) { 403 } + + it 'does not raise an exception' do + expect { document_request.fetch }.not_to raise_error + end + end + + context 'we get a 500 back' do + let(:fake_socure_response) { {} } + let(:fake_socure_status) { 500 } + + it 'does not raise an exception' do + expect { document_request.fetch }.not_to raise_error + end + end + end +end From e5c741df60a1066d3ec3c3df5ce652f3d2517087 Mon Sep 17 00:00:00 2001 From: Amir Reavis-Bey Date: Wed, 9 Oct 2024 12:14:36 -0400 Subject: [PATCH 06/12] LG-14734: socure document capture 404 when not enabled (#11327) * socure document capture access behind feature flag [skip changelog] * add socure_enabled FF to socure document capture controllers * fix alphabetical order of config file --- .../socure/document_capture_controller.rb | 2 ++ .../idv/socure/document_capture_controller.rb | 2 ++ app/controllers/socure_webhook_controller.rb | 2 +- config/application.yml.default | 1 + lib/identity_config.rb | 1 + .../document_capture_controller_spec.rb | 21 +++++++++++++++++++ .../document_capture_controller_spec.rb | 21 +++++++++++++++++++ .../socure_webhook_controller_spec.rb | 8 +++---- 8 files changed, 53 insertions(+), 5 deletions(-) diff --git a/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb index 8c0d2b29fc6..71090f7e300 100644 --- a/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb +++ b/app/controllers/idv/hybrid_mobile/socure/document_capture_controller.rb @@ -7,7 +7,9 @@ class DocumentCaptureController < ApplicationController include Idv::AvailabilityConcern include DocumentCaptureConcern include Idv::HybridMobile::HybridMobileConcern + include RenderConditionConcern + check_or_render_not_found -> { IdentityConfig.store.socure_enabled } before_action :check_valid_document_capture_session, except: [:update] def show diff --git a/app/controllers/idv/socure/document_capture_controller.rb b/app/controllers/idv/socure/document_capture_controller.rb index d34f7ebb69f..214918d95f7 100644 --- a/app/controllers/idv/socure/document_capture_controller.rb +++ b/app/controllers/idv/socure/document_capture_controller.rb @@ -6,7 +6,9 @@ class DocumentCaptureController < ApplicationController include Idv::AvailabilityConcern include IdvStepConcern include DocumentCaptureConcern + include RenderConditionConcern + check_or_render_not_found -> { IdentityConfig.store.socure_enabled } before_action :confirm_not_rate_limited before_action :confirm_step_allowed diff --git a/app/controllers/socure_webhook_controller.rb b/app/controllers/socure_webhook_controller.rb index bd46ee15327..2e147566fe9 100644 --- a/app/controllers/socure_webhook_controller.rb +++ b/app/controllers/socure_webhook_controller.rb @@ -4,7 +4,7 @@ class SocureWebhookController < ApplicationController include RenderConditionConcern skip_before_action :verify_authenticity_token - check_or_render_not_found -> { IdentityConfig.store.socure_webhook_enabled } + check_or_render_not_found -> { IdentityConfig.store.socure_enabled } before_action :check_token before_action :check_socure_event diff --git a/config/application.yml.default b/config/application.yml.default index e300932af8d..3cd8963c1ea 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -347,6 +347,7 @@ sign_in_user_id_per_ip_attempt_window_in_minutes: 720 sign_in_user_id_per_ip_attempt_window_max_minutes: 43_200 sign_in_user_id_per_ip_max_attempts: 50 socure_document_request_endpoint: '' +socure_enabled: false socure_idplus_api_key: '' socure_idplus_base_url: '' socure_idplus_timeout_in_seconds: 5 diff --git a/lib/identity_config.rb b/lib/identity_config.rb index 36a70f42100..b00b0e25689 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -403,6 +403,7 @@ def self.store config.add(:socure_document_request_endpoint, type: :string) config.add(:socure_idplus_api_key, type: :string) config.add(:socure_webhook_enabled, type: :boolean) + config.add(:socure_enabled, type: :boolean) config.add(:socure_webhook_secret_key, type: :string) config.add(:socure_webhook_secret_key_queue, type: :json) config.add(:sp_handoff_bounce_max_seconds, type: :integer) diff --git a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb index ad6331240c9..7d3bf05ca5f 100644 --- a/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb +++ b/spec/controllers/idv/hybrid_mobile/socure/document_capture_controller_spec.rb @@ -7,6 +7,7 @@ let(:fake_socure_endpoint) { 'https://fake-socure.com' } let(:user) { create(:user) } let(:stored_result) { nil } + let(:socure_enabled) { true } let(:document_capture_session) do DocumentCaptureSession.create( @@ -17,6 +18,8 @@ let(:document_capture_session_uuid) { document_capture_session&.uuid } before do + allow(IdentityConfig.store).to receive(:socure_enabled). + and_return(socure_enabled) allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). and_return(fake_socure_endpoint) allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) @@ -162,6 +165,15 @@ expect(controller.send(:instance_variable_get, :@url)).not_to be end end + + context 'when socure is disabled' do + let(:socure_enabled) { false } + it 'the webhook route does not exist' do + get(:show) + + expect(response).to be_not_found + end + end end describe '#update' do @@ -170,5 +182,14 @@ expect(response).to have_http_status(:ok) end + + context 'when socure is disabled' do + let(:socure_enabled) { false } + it 'the webhook route does not exist' do + post(:update) + + expect(response).to be_not_found + end + end end end diff --git a/spec/controllers/idv/socure/document_capture_controller_spec.rb b/spec/controllers/idv/socure/document_capture_controller_spec.rb index 14d5b05eaf5..e2fcfa51a1c 100644 --- a/spec/controllers/idv/socure/document_capture_controller_spec.rb +++ b/spec/controllers/idv/socure/document_capture_controller_spec.rb @@ -7,6 +7,7 @@ let(:fake_socure_endpoint) { 'https://fake-socure.com' } let(:user) { create(:user) } let(:stored_result) { nil } + let(:socure_enabled) { true } let(:document_capture_session) do DocumentCaptureSession.create( @@ -16,6 +17,8 @@ end before do + allow(IdentityConfig.store).to receive(:socure_enabled). + and_return(socure_enabled) allow(IdentityConfig.store).to receive(:socure_document_request_endpoint). and_return(fake_socure_endpoint) allow(IdentityConfig.store).to receive(:doc_auth_vendor).and_return(idv_vendor) @@ -162,6 +165,15 @@ expect(controller.send(:instance_variable_get, :@url)).not_to be end end + + context 'when socure is disabled' do + let(:socure_enabled) { false } + it 'the webhook route does not exist' do + get(:show) + + expect(response).to be_not_found + end + end end describe '#update' do @@ -170,5 +182,14 @@ expect(response).to have_http_status(:ok) end + + context 'when socure is disabled' do + let(:socure_enabled) { false } + it 'the webhook route does not exist' do + post(:update) + + expect(response).to be_not_found + end + end end end diff --git a/spec/controllers/socure_webhook_controller_spec.rb b/spec/controllers/socure_webhook_controller_spec.rb index 036314a1bb9..09d4baea428 100644 --- a/spec/controllers/socure_webhook_controller_spec.rb +++ b/spec/controllers/socure_webhook_controller_spec.rb @@ -6,7 +6,7 @@ describe 'POST /api/webhooks/socure/event' do let(:socure_secret_key) { 'this-is-a-secret' } let(:socure_secret_key_queue) { ['this-is-an-old-secret', 'this-is-an-older-secret'] } - let(:socure_webhook_enabled) { true } + let(:socure_enabled) { true } let(:webhook_body) do { event: { @@ -31,8 +31,8 @@ and_return(socure_secret_key) allow(IdentityConfig.store).to receive(:socure_webhook_secret_key_queue). and_return(socure_secret_key_queue) - allow(IdentityConfig.store).to receive(:socure_webhook_enabled). - and_return(socure_webhook_enabled) + allow(IdentityConfig.store).to receive(:socure_enabled). + and_return(socure_enabled) stub_analytics end @@ -79,7 +79,7 @@ end context 'when socure webhook disabled' do - let(:socure_webhook_enabled) { false } + let(:socure_enabled) { false } it 'the webhook route does not exist' do request.headers['Authorization'] = socure_secret_key post :create, params: webhook_body From 0e9bba2636b9bf407e019003131870a5522ababd Mon Sep 17 00:00:00 2001 From: Lauren George Date: Wed, 9 Oct 2024 12:46:43 -0400 Subject: [PATCH 07/12] Use a list to log resolved ACRs in events (#11307) * Use a list to log resolved ACRs in events ### Why - Deter using the resolved ACRs directly in reporting. The resolved list of ACRs (or Vectors of Trust) is volatile and subject to relatively frequent change as service features are added, removed, or modified over time. Instead, most analytics users should use the resolved attribute flags (e.g., `facial_match` or `enhanced_ipp`) for event filtering. Going forward, the purpose of the `sp_request.component_values` field should be solely to support investigating unexpected behavior. - Eliminate unexpected "reformatting" or "truncation" of resolved ACR names. While truncating these strings makes composing queries simpler, it introduces complexity and confusion when triaging and debugging requests. Originally, the purpose was to be able to do simple filters like `properties.sp_request.component_values.Pb`, but with the deprecation of the VoT interfaces (and thereby the 2-character string names), the filter becomes ``sp_request.component_values.`urn:acr.login.gov:verified` ``, which is much less legible than: `'urn:acr.login.gov:verified' IN sp_request.component_names` - Why not store component values as a string? Basically, VoT/vtr style components and ACR style components "stringify" differently, which would make query composition even more complex, instead of less. ### How - Adds an array of strings containing the ACR name that will be stored in `sp_request.component_names`. - No other changes have been made to structure of `sp_request`. - New specs have been added for all IAL ACRs. changelog: Internal, Analytics, Streamlining inclusion of resolved ACR names resolves: https://gitlab.login.gov/lg-people/Melba/backlog-fy24/-/issues/112 --- app/services/analytics.rb | 7 +- lib/saml_idp_constants.rb | 13 +- spec/services/analytics_spec.rb | 297 ++++++++++++++++++-------------- 3 files changed, 181 insertions(+), 136 deletions(-) diff --git a/app/services/analytics.rb b/app/services/analytics.rb index 378a89712e7..d03005fcddd 100644 --- a/app/services/analytics.rb +++ b/app/services/analytics.rb @@ -131,8 +131,9 @@ def sp_request_attributes attributes = resolved_result.to_h attributes[:component_values] = resolved_result.component_values.map do |v| - [v.name.sub('http://idmanagement.gov/ns/assurance/', ''), true] + [v.name.sub("#{Saml::Idp::Constants::LEGACY_ACR_PREFIX}/", ''), true] end.to_h + attributes[:component_names] = resolved_result.component_names attributes.reject! { |_key, value| value == false } if differentiator.present? @@ -157,7 +158,9 @@ def differentiator end def resolved_authn_context_result - return nil if sp.nil? || session[:sp].blank? + return nil if sp.blank? || + session[:sp].blank? || + (session[:sp][:vtr].blank? && session[:sp][:acr_values].blank?) return @resolved_authn_context_result if defined?(@resolved_authn_context_result) service_provider = ServiceProvider.find_by(issuer: sp) diff --git a/lib/saml_idp_constants.rb b/lib/saml_idp_constants.rb index 67a738998a8..d02892bec02 100644 --- a/lib/saml_idp_constants.rb +++ b/lib/saml_idp_constants.rb @@ -6,10 +6,13 @@ module Saml module Idp module Constants - LOA1_AUTHN_CONTEXT_CLASSREF = 'http://idmanagement.gov/ns/assurance/loa/1' - LOA3_AUTHN_CONTEXT_CLASSREF = 'http://idmanagement.gov/ns/assurance/loa/3' + LEGACY_ACR_NS = 'http://idmanagement.gov/ns' + LEGACY_ACR_PREFIX = "#{LEGACY_ACR_NS}/assurance".freeze - IAL_AUTHN_CONTEXT_PREFIX = 'http://idmanagement.gov/ns/assurance/ial' + LOA1_AUTHN_CONTEXT_CLASSREF = "#{LEGACY_ACR_PREFIX}/loa/1".freeze + LOA3_AUTHN_CONTEXT_CLASSREF = "#{LEGACY_ACR_PREFIX}/loa/3".freeze + + IAL_AUTHN_CONTEXT_PREFIX = "#{LEGACY_ACR_PREFIX}/ial".freeze IAL1_AUTHN_CONTEXT_CLASSREF = "#{IAL_AUTHN_CONTEXT_PREFIX}/1".freeze IAL2_AUTHN_CONTEXT_CLASSREF = "#{IAL_AUTHN_CONTEXT_PREFIX}/2".freeze IALMAX_AUTHN_CONTEXT_CLASSREF = "#{IAL_AUTHN_CONTEXT_PREFIX}/0".freeze @@ -29,7 +32,7 @@ module Constants ].freeze DEFAULT_AAL_AUTHN_CONTEXT_CLASSREF = 'urn:gov:gsa:ac:classes:sp:PasswordProtectedTransport:duo' - AAL_AUTHN_CONTEXT_PREFIX = 'http://idmanagement.gov/ns/assurance/aal' + AAL_AUTHN_CONTEXT_PREFIX = "#{LEGACY_ACR_PREFIX}/aal".freeze AAL1_AUTHN_CONTEXT_CLASSREF = "#{AAL_AUTHN_CONTEXT_PREFIX}/1".freeze AAL2_AUTHN_CONTEXT_CLASSREF = "#{AAL_AUTHN_CONTEXT_PREFIX}/2".freeze AAL2_PHISHING_RESISTANT_AUTHN_CONTEXT_CLASSREF = "#{AAL_AUTHN_CONTEXT_PREFIX}/2?phishing_resistant=true".freeze @@ -42,7 +45,7 @@ module Constants NAME_ID_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified' VALID_NAME_ID_FORMATS = [NAME_ID_FORMAT_PERSISTENT, NAME_ID_FORMAT_EMAIL].freeze - REQUESTED_ATTRIBUTES_CLASSREF = 'http://idmanagement.gov/ns/requested_attributes?ReqAttr=' + REQUESTED_ATTRIBUTES_CLASSREF = "#{LEGACY_ACR_NS}/requested_attributes?ReqAttr=".freeze VALID_AUTHN_CONTEXTS = (if FeatureManagement.use_semantic_authn_contexts? IdentityConfig.store.valid_authn_contexts_semantic diff --git a/spec/services/analytics_spec.rb b/spec/services/analytics_spec.rb index 76b797e27d4..23143e426fd 100644 --- a/spec/services/analytics_spec.rb +++ b/spec/services/analytics_spec.rb @@ -183,6 +183,7 @@ context 'when should_log says not to' do let(:should_log) { /some other event/ } + it 'does not include ab_test in logged event' do expect(ahoy).to receive(:track).with( 'Trackable Event', @@ -217,11 +218,14 @@ context 'with an SP request vtr saved in the session' do context 'identity verified' do let(:session) { { sp: { vtr: ['C1.P1'] } } } + let(:component_names) { ['C1', 'C2', 'P1'] } + let(:component_values) { component_names.index_with(true) } let(:expected_attributes) do { sp_request: { aal2: true, - component_values: { 'C1' => true, 'C2' => true, 'P1' => true }, + component_names:, + component_values:, identity_proofing: true, component_separator: '.', }, @@ -238,15 +242,8 @@ context 'phishing resistant and requiring facial match comparison' do let(:session) { { sp: { vtr: ['Ca.Pb'] } } } - let(:component_values) do - { - 'C1' => true, - 'C2' => true, - 'Ca' => true, - 'P1' => true, - 'Pb' => true, - } - end + let(:component_names) { ['C1', 'C2', 'Ca', 'P1', 'Pb'] } + let(:component_values) { component_names.index_with(true) } let(:expected_attributes) do { @@ -255,6 +252,7 @@ facial_match: true, two_pieces_of_fair_evidence: true, component_values:, + component_names:, identity_proofing: true, phishing_resistant: true, component_separator: '.', @@ -271,169 +269,210 @@ end end - context 'with SP request acr_values saved in the session' do - context 'IAL1' do - let(:session) { { sp: { acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF } } } - let(:expected_attributes) do - { - sp_request: { - component_values: { 'ial/1' => true }, - component_separator: ' ', - }, - } + shared_context '#sp_request_attributes[acr_values]' do + let(:acr_values) { [] } + let(:sp_request) { {} } + let(:session) do + { + sp: { + acr_values: acr_values.join(' '), + }, + } + end + let(:expected_attributes) do + { + sp_request: { + component_separator: ' ', + component_names: acr_values, + component_values: acr_values.map do |v| + v.sub("#{Saml::Idp::Constants::LEGACY_ACR_PREFIX}/", '') + end.index_with(true), + **sp_request, + }, + } + end + + shared_examples 'track event with :sp_request' do + it 'then #sp_request_attributes() matches :sp_request' do + expect(analytics.sp_request_attributes).to match(expected_attributes) end - it 'includes the sp_request' do + it 'then includes :sp_request in the event' do expect(ahoy).to receive(:track). with('Trackable Event', hash_including(expected_attributes)) analytics.track_event('Trackable Event') end end + end - context 'IAL2' do - let(:session) { { sp: { acr_values: Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF } } } - let(:expected_attributes) do - { - sp_request: { - aal2: true, - component_values: { 'ial/2' => true }, - identity_proofing: true, - component_separator: ' ', - }, - } - end + context 'when acr_values are saved in the session' do + include_context '#sp_request_attributes[acr_values]' - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) + shared_examples 'using acrs for all user scenarios' do |acr_values_list| + let(:acr_values) { acr_values_list } - analytics.track_event('Trackable Event') - end - end + context "using #{acr_values_list}" do + context 'when the user has not been identity verified' do + let(:current_user) { build(:user, :fully_registered) } - context 'IAL2 with facial match' do - let(:session) do - { sp: { acr_values: Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF } } - end - let(:expected_attributes) do - { - sp_request: { - aal2: true, - facial_match: true, - two_pieces_of_fair_evidence: true, - component_values: { 'ial/2?bio=required' => true }, - identity_proofing: true, - component_separator: ' ', - }, - } - end + include_examples 'track event with :sp_request' + end - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) + context 'when the identity verified user has not proofed with facial match' do + let(:current_user) { build(:user, :proofed) } - analytics.track_event('Trackable Event') + include_examples 'track event with :sp_request' + end + + context 'when the identity verified user has proofed with facial match' do + let(:current_user) { build(:user, :proofed_with_selfie) } + + include_examples 'track event with :sp_request' + end end end - context 'acr_values IALMAX' do - let(:session) { { sp: { acr_values: Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF } } } - let(:expected_attributes) do + context 'and does not require any identity proofing' do + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL_AUTH_ONLY_ACR] + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF] + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::LOA1_AUTHN_CONTEXT_CLASSREF] + end + + context 'and selects any variant of identity proofing' do + let(:sp_request) do { - sp_request: { - aal2: true, - component_values: { 'ial/0' => true }, - component_separator: ' ', - ialmax: true, - }, + aal2: true, + identity_proofing: true, } end - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) - - analytics.track_event('Trackable Event') - end + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL_VERIFIED_ACR] + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF] + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::LOA3_AUTHN_CONTEXT_CLASSREF] end - end - - context 'with an SP request_url saved in the session' do - context 'no request_url' do - let(:session) { { sp: { acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF } } } - let(:expected_attributes) do + context 'and selects required facial match identity proofing' do + let(:sp_request) do { - sp_request: { - component_values: { 'ial/1' => true }, - component_separator: ' ', - }, + aal2: true, + facial_match: true, + two_pieces_of_fair_evidence: true, + identity_proofing: true, } end - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL_VERIFIED_FACIAL_MATCH_REQUIRED_ACR] - analytics.track_event('Trackable Event') - end + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF] end - context 'a request_url without login_gov_app_differentiator ' do - let(:session) do - { - sp: { - acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - request_url: 'http://localhost:3000/openid_connect/authorize?whatever=something_else', - }, - } + context 'and selects optional facial match identity proofing' do + shared_examples 'with user scenarios' do |acr_values_list| + context "using #{acr_values_list}" do + let(:acr_values) { acr_values_list } + + context 'when the user has not been identity verified' do + let(:sp_request) do + { + aal2: true, + facial_match: true, + two_pieces_of_fair_evidence: true, + identity_proofing: true, + } + end + let(:current_user) { build(:user, :fully_registered) } + + include_examples 'track event with :sp_request' + end + + context 'when the identity verified user has not proofed with facial match' do + let(:current_user) { build(:user, :proofed) } + let(:sp_request) do + { + aal2: true, + identity_proofing: true, + } + end + + include_examples 'track event with :sp_request' + end + + context 'when the identity verified user has proofed with facial match' do + let(:sp_request) do + { + aal2: true, + facial_match: true, + two_pieces_of_fair_evidence: true, + identity_proofing: true, + } + end + let(:current_user) { build(:user, :proofed_with_selfie) } + + include_examples 'track event with :sp_request' + end + end end - let(:expected_attributes) do + include_examples 'with user scenarios', + [Saml::Idp::Constants::IAL_VERIFIED_FACIAL_MATCH_PREFERRED_ACR] + include_examples 'with user scenarios', + [Saml::Idp::Constants::IAL2_BIO_PREFERRED_AUTHN_CONTEXT_CLASSREF] + end + + context 'and selects the IALMax step-up flow' do + let(:sp_request) do { - sp_request: { - component_values: { 'ial/1' => true }, - component_separator: ' ', - }, + aal2: true, + ialmax: true, } end - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) + include_examples 'using acrs for all user scenarios', + [Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF] + end + end - analytics.track_event('Trackable Event') - end + context 'with an SP request_url saved in the session' do + include_context '#sp_request_attributes[acr_values]' + let(:acr_values) { [Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF] } + let(:request_url) { nil } + let(:session) do + { + sp: { + acr_values: acr_values.join(' '), + request_url:, + }.compact, + } end - context 'a request_url with login_gov_app_differentiator ' do - let(:session) do - { - sp: { - acr_values: Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - request_url: - 'http://localhost:3000/openid_connect/authorize?login_gov_app_differentiator=NY', - }, - } - end + context 'no request_url' do + include_examples 'track event with :sp_request' + end - let(:expected_attributes) do + context 'a request_url without login_gov_app_differentiator ' do + let(:request_url) { 'http://localhost:3000/openid_connect/authorize?whatever=something_else' } + + include_examples 'track event with :sp_request' + end + + context 'a request_url with login_gov_app_differentiator ' do + let(:request_url) { 'http://localhost:3000/openid_connect/authorize?login_gov_app_differentiator=NY' } + let(:sp_request) do { - sp_request: { - component_values: { 'ial/1' => true }, - component_separator: ' ', - app_differentiator: 'NY', - }, + app_differentiator: 'NY', } end - it 'includes the sp_request' do - expect(ahoy).to receive(:track). - with('Trackable Event', hash_including(expected_attributes)) - - analytics.track_event('Trackable Event') - end + include_examples 'track event with :sp_request' end end end From 6c2bf3cfb58235f46000f3847e37d5681490ca1b Mon Sep 17 00:00:00 2001 From: Will Birdsall Date: Wed, 9 Oct 2024 13:04:42 -0400 Subject: [PATCH 08/12] changelog: Bug Fixes, In-person Proofing, Fix 404 Error on PO Search Page (#11287) --- app/javascript/packs/document-capture.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/app/javascript/packs/document-capture.tsx b/app/javascript/packs/document-capture.tsx index 4061335eee9..c4c13cc10ef 100644 --- a/app/javascript/packs/document-capture.tsx +++ b/app/javascript/packs/document-capture.tsx @@ -15,7 +15,6 @@ import { import { isCameraCapableMobile } from '@18f/identity-device'; import { FlowContext } from '@18f/identity-verify-flow'; import { trackEvent as baseTrackEvent } from '@18f/identity-analytics'; -import { extendSession } from '@18f/identity-session'; import type { FlowPath, DeviceContextValue } from '@18f/identity-document-capture'; /** @@ -202,12 +201,7 @@ const App = composeComponents( maxSubmissionAttemptsBeforeNativeCamera: Number(maxSubmissionAttemptsBeforeNativeCamera), }, ], - [ - DocumentCapture, - { - onStepChange: extendSession, - }, - ], + [DocumentCapture], ); render(, appRoot); From 56dd54beb0ff5358c69f9b0490c23edc6dfccd80 Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Wed, 9 Oct 2024 16:54:39 -0400 Subject: [PATCH 09/12] Cuts over review app deployment to kustomize overlay/base instead of helm chart (#11125) * Swapping review apps to ArgoCD application and kustomize --- .gitlab-ci.yml | 138 +------ dockerfiles/application.yaml | 565 ++++++++++++++++++++++++++ dockerfiles/idp_review_app.Dockerfile | 1 + 3 files changed, 580 insertions(+), 124 deletions(-) create mode 100644 dockerfiles/application.yaml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index db9eab7cdb4..75b6ba435fd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -11,6 +11,7 @@ variables: IDP_CI_SHA: 'sha256:5c4953f8efba18b7a6d6a9a961cb77ba7143059cbb2176499432b4275fbe67db' PKI_IMAGE_TAG: 'main' DASHBOARD_IMAGE_TAG: 'main' + APPLICATION_MANIFEST: dockerfiles/application.yaml default: image: '${ECR_REGISTRY}/idp/ci@${IDP_CI_SHA}' @@ -433,129 +434,18 @@ trigger_devops: - export SANITIZED_BRANCH_NAME=$(echo "$CI_COMMIT_REF_NAME" | tr '/' '-' | tr -c '[:alnum:]-_' '-' | sed 's/-*$//') - echo "${CI_COMMIT_REF_NAME}" - echo "${SANITIZED_BRANCH_NAME}" - - |- - export IDP_CONFIG=$(cat <- - helm upgrade --install --namespace review-apps - --debug - --set global.labels.branch="${SANITIZED_BRANCH_NAME}" - --set env="reviewapps-$CI_ENVIRONMENT_SLUG" - --set idp.image.repository="${ECR_REGISTRY}/identity-idp/review" - --set idp.image.tag="${CI_COMMIT_SHA}" - --set worker.image.repository="${ECR_REGISTRY}/identity-idp/review" - --set worker.image.tag="${CI_COMMIT_SHA}" - --set pivcac.image.repository="${ECR_REGISTRY}/identity-pivcac/review" - --set pivcac.image.tag="${PKI_IMAGE_TAG}" - --set pivcac.image.pullPolicy="Always" - --set dashboard.image.repository="${ECR_REGISTRY}/identity-dashboard/review" - --set dashboard.image.tag="${DASHBOARD_IMAGE_TAG}" - --set dashboard.image.pullPolicy="Always" - --set-json dashboard.config="$DASHBOARD_CONFIG" - --set-json dashboard.enabled=true - --set-json idp.config="$IDP_CONFIG" - --set-json worker.config="$WORKER_CONFIG" - --set-json pivcac.config="$PIVCAC_CONFIG" - --set-json idp.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.reviewapps.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" - --set-json pivcac.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG.pivcac.reviewapps.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" - --set-json dashboard.ingress.hosts="[{\"host\": \"$CI_ENVIRONMENT_SLUG-dashboard.reviewapps.identitysandbox.gov\", \"paths\": [{\"path\": \"/\", \"pathType\": \"Prefix\"}]}]" - $CI_ENVIRONMENT_SLUG ./identity-idp-helm-chart + #TODO put in kustomize based deploy + # Dynamically populate review environment settings + - sed -i "s|{{ENVIRONMENT}}|${CI_ENVIRONMENT_SLUG}|g" ${APPLICATION_MANIFEST} + - sed -i "s|{{SANITIZED_BRANCH_NAME}}|${SANITIZED_BRANCH_NAME}|g" ${APPLICATION_MANIFEST} + - sed -i "s|{{IDP_CONTAINER_TAG}}|${CI_COMMIT_SHA}|g" ${APPLICATION_MANIFEST} + - sed -i "s|{{DASHBOARD_CONTAINER_TAG}}|${DASHBOARD_IMAGE_TAG}|g" ${APPLICATION_MANIFEST} + - sed -i "s|{{PIVCAC_CONTAINER_TAG}}|${PKI_IMAGE_TAG}|g" ${APPLICATION_MANIFEST} + - sed -i "s|{{ECR_REGISTRY}}|${ECR_REGISTRY}|g" ${APPLICATION_MANIFEST} + - cat ${APPLICATION_MANIFEST} + # Apply our ArgoCD Application + - kubectl apply -f ${APPLICATION_MANIFEST} -n argocd + - echo "View your applications deployment progress at https://argocd.reviewapp.identitysandbox.gov/applications/argocd/${CI_ENVIRONMENT_SLUG}?view=tree&resource=" - echo "DNS may take a while to propagate, so be patient if it doesn't show up right away" - echo "To access the rails console, first run 'aws-vault exec sandbox-power -- aws eks update-kubeconfig --name reviewapp'" - echo "Then run aws-vault exec sandbox-power -- kubectl exec -it service/$CI_ENVIRONMENT_SLUG-login-chart-idp -n review-apps -- /app/bin/rails console" @@ -589,7 +479,7 @@ stop-review-app: script: - export CONTEXT=$(kubectl config get-contexts | grep reviewapp | awk '{print $1}' | head -1) - kubectl config use-context "$CONTEXT" - - helm uninstall --namespace review-apps $CI_ENVIRONMENT_SLUG + - kubectl delete application $CI_ENVIRONMENT_SLUG -n argocd stage: review image: name: dtzar/helm-kubectl:latest diff --git a/dockerfiles/application.yaml b/dockerfiles/application.yaml new file mode 100644 index 00000000000..96599270750 --- /dev/null +++ b/dockerfiles/application.yaml @@ -0,0 +1,565 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: {{ENVIRONMENT}} + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + source: + repoURL: 'git@gitlab.login.gov:lg-public/identity-eks-control.git' + targetRevision: main + path: cluster-reviewapp/envs/reviewapps + kustomize: + namePrefix: "{{ENVIRONMENT}}-" + commonLabels: + env: {{ENVIRONMENT}} + branch: {{SANITIZED_BRANCH_NAME}} + # ArgoCD does not support patchesStrategicMerge + patches: + # Patch ConfigMap for IDP + - target: + kind: ConfigMap + name: idp-config + patch: |- + - op: add + path: /data/ASSET_HOST + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/DASHBOARD_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-idp-pg.review-apps" + - op: add + path: /data/POSTGRES_NAME + value: "idp" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/LOGIN_ENV + value: "{{ENVIRONMENT}}" + - op: add + path: /data/LOGIN_HOST_ROLE + value: "idp" + - op: add + path: /data/LOGIN_SKIP_REMOTE_CONFIG + value: "true" + - op: add + path: /data/PIV_CAC_SERVICE_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/PIV_CAC_VERIFY_TOKEN_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/NEW_RELIC_LOG + value: "stdout" + - op: add + path: /data/PIDFILE + value: "/dev/null" + - op: add + path: /data/ENABLE_BOOTSNAP + value: "false" + - op: add + path: /data/BOOTSNAP_READONLY + value: "true" + - op: add + path: /data/REDIS_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379" + - op: add + path: /data/REDIS_THROTTLE_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379/1" + - op: add + path: /data/REDIS_IRS_ATTEMPTS_API_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379/2" + - target: + kind: ConfigMap + name: idp-config-dbsetup + patch: |- + - op: add + path: /data/ASSET_HOST + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/DASHBOARD_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-idp-pg.review-apps" + - op: add + path: /data/POSTGRES_NAME + value: "idp" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/LOGIN_ENV + value: "{{ENVIRONMENT}}" + - op: add + path: /data/LOGIN_HOST_ROLE + value: "idp" + - op: add + path: /data/LOGIN_SKIP_REMOTE_CONFIG + value: "true" + - op: add + path: /data/PIV_CAC_SERVICE_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/PIV_CAC_VERIFY_TOKEN_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/NEW_RELIC_LOG + value: "stdout" + - op: add + path: /data/PIDFILE + value: "/dev/null" + - op: add + path: /data/ENABLE_BOOTSNAP + value: "false" + - op: add + path: /data/BOOTSNAP_READONLY + value: "true" + - op: add + path: /data/REDIS_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379" + - op: add + path: /data/REDIS_THROTTLE_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379/1" + - op: add + path: /data/REDIS_IRS_ATTEMPTS_API_URL + value: "redis://{{ENVIRONMENT}}-redis.review-apps:6379/2" + # Patch ConfigMap for Worker + - target: + kind: ConfigMap + name: worker-config + patch: |- + - op: add + path: /data/DASHBOARD_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/POSTGRES_NAME + value: "idp" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-idp-pg.review-apps" + - op: add + path: /data/LOGIN_ENV + value: "{{ENVIRONMENT}}" + - op: add + path: /data/LOGIN_HOST_ROLE + value: "worker" + - op: add + path: /data/LOGIN_SKIP_REMOTE_CONFIG + value: "true" + - op: add + path: /data/PIV_CAC_SERVICE_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/PIV_CAC_VERIFY_TOKEN_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - target: + kind: ConfigMap + name: worker-config-dbsetup + patch: |- + - op: add + path: /data/DASHBOARD_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/POSTGRES_NAME + value: "idp" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-idp-pg.review-apps" + - op: add + path: /data/LOGIN_ENV + value: "{{ENVIRONMENT}}" + - op: add + path: /data/LOGIN_HOST_ROLE + value: "worker" + - op: add + path: /data/LOGIN_SKIP_REMOTE_CONFIG + value: "true" + - op: add + path: /data/PIV_CAC_SERVICE_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/PIV_CAC_VERIFY_TOKEN_URL + value: "https://{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov/" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + # Patch ConfigMap for PIVCAC + - target: + kind: ConfigMap + name: pivcac-config + patch: |- + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/CLIENT_CERT_S3_BUCKET + value: "login-gov-pivcac-public-cert-reviewapps.894947205914-us-west-2" + - op: add + path: /data/POSTGRES_NAME + value: "identity_pki_production" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-pivcac-pg.review-apps" + - op: add + path: /data/PIDFILE + value: "/dev/null" + - op: add + path: /data/IDP_HOST + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov" + - target: + kind: ConfigMap + name: pivcac-config-dbsetup + patch: |- + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/CLIENT_CERT_S3_BUCKET + value: "login-gov-pivcac-public-cert-reviewapps.894947205914-us-west-2" + - op: add + path: /data/POSTGRES_NAME + value: "identity_pki_production" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-pivcac-pg.review-apps" + - op: add + path: /data/PIDFILE + value: "/dev/null" + - op: add + path: /data/IDP_HOST + value: "{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "{{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov" + # Patch ConfigMap for Dashboard + - target: + kind: ConfigMap + name: dashboard-config + patch: |- + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_NAME + value: "dashboard" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-dashboard-pg.review-apps" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/NEW_RELIC_ENABLED + value: "false" + - op: add + path: /data/SAML_SP_ISSUER + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/IDP_URL + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/IDP_SP_URL + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/POST_LOGOUT_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - target: + kind: ConfigMap + name: dashboard-config-dbsetup + patch: |- + - op: add + path: /data/KUBERNETES_REVIEW_APP + value: "true" + - op: add + path: /data/POSTGRES_NAME + value: "dashboard" + - op: add + path: /data/POSTGRES_HOST + value: "{{ENVIRONMENT}}-dashboard-pg.review-apps" + - op: add + path: /data/POSTGRES_SSLMODE + value: "prefer" + - op: add + path: /data/NEW_RELIC_ENABLED + value: "false" + - op: add + path: /data/SAML_SP_ISSUER + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/IDP_URL + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/IDP_SP_URL + value: "https://{{ENVIRONMENT}}.reviewapps.identitysandbox.gov" + - op: add + path: /data/POST_LOGOUT_URL + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + - op: add + path: /data/DOMAIN_NAME + value: "https://{{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov" + # Patch ConfigMap for Dashboard service_providers.yml + - target: + kind: ConfigMap + name: service-providers-yml + patch: |- + - op: replace + path: /data/service_providers.yml + value: | + production: + 'urn:gov:gsa:openidconnect.profiles:sp:sso:gsa:dashboard': + friendly_name: 'Dashboard' + agency: 'GSA' + agency_id: 2 + logo: '18f.svg' + certs: + - 'identity_dashboard_cert' + return_to_sp_url: 'https://dashboard.{{ENVIRONMENT}}.identitysandbox.gov/' + redirect_uris: + - 'https://dashboard.{{ENVIRONMENT}}.identitysandbox.gov/auth/logindotgov/callback' + - 'https://dashboard.{{ENVIRONMENT}}.identitysandbox.gov' + push_notification_url: 'https://dashboard.{{ENVIRONMENT}}.identitysandbox.gov/api/security_events' + # Patch idp database setup jobs + - target: + kind: Job + name: create-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-idp/review:{{IDP_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: Job + name: migrate-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-idp/review:{{IDP_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: Job + name: seed-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-idp/review:{{IDP_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + # Patch dashboard database setup jobs + - target: + kind: Job + name: create-dashboard-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-dashboard/review:{{DASHBOARD_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: Job + name: migrate-dashboard-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-dashboard/review:{{DASHBOARD_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: Job + name: seed-dashboard-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-dashboard/review:{{DASHBOARD_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + # Patch pivcac database jobs/update crl CronJob + - target: + kind: Job + name: create-pivcac-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-pivcac/pivcac:{{PIVCAC_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: Job + name: migrate-pivcac-database + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-pivcac/pivcac:{{PIVCAC_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - target: + kind: CronJob + name: update-pivcac-crls + patch: |- + - op: replace + path: /spec/jobTemplate/spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-pivcac/pivcac:{{PIVCAC_CONTAINER_TAG}} + - op: replace + path: /spec/jobTemplate/spec/template/spec/containers/0/imagePullPolicy + value: Always + # Patch IDP image + - target: + kind: Rollout + name: idp-rollout + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-idp/review:{{IDP_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + # Patch Worker Image + - target: + kind: Deployment + name: worker + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-idp/review:{{IDP_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + # Patch PIVCAC Image + - target: + kind: Deployment + name: pivcac + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-pivcac/pivcac:{{PIVCAC_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/1/image + value: {{ECR_REGISTRY}}/identity-pivcac/nginx:{{PIVCAC_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - op: replace + path: /spec/template/spec/containers/1/imagePullPolicy + value: Always + # Patch Dashboard Image + - target: + kind: Deployment + name: dashboard + patch: |- + - op: replace + path: /spec/template/spec/containers/0/image + value: {{ECR_REGISTRY}}/identity-dashboard/review:{{DASHBOARD_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/initContainers/0/image + value: {{ECR_REGISTRY}}/identity-dashboard/review:{{DASHBOARD_CONTAINER_TAG}} + - op: replace + path: /spec/template/spec/containers/0/imagePullPolicy + value: Always + - op: replace + path: /spec/template/spec/initContainers/0/imagePullPolicy + value: Always + # Patch in lower pod number in IDP HPA + - target: + kind: HorizontalPodAutoscaler + name: idp + patch: |- + - op: replace + path: /spec/minReplicas + value: 1 + - op: replace + path: /spec/maxReplicas + value: 2 + # Patch ingress names + - target: + kind: Ingress + name: idp + patch: |- + - op: replace + path: /metadata/annotations/alb.ingress.kubernetes.io~1group.name + value: review-app + - op: replace + path: /spec/rules/0/host + value: {{ENVIRONMENT}}.reviewapps.identitysandbox.gov + - op: replace + path: /spec/rules/0/http/paths/0/backend/service/port/name + value: https + - target: + kind: Ingress + name: dashboard + patch: |- + - op: replace + path: /metadata/annotations/alb.ingress.kubernetes.io~1group.name + value: review-app-dashboard + - op: replace + path: /spec/rules/0/host + value: {{ENVIRONMENT}}-dashboard.reviewapps.identitysandbox.gov + - target: + kind: Ingress + name: pivcac + patch: |- + - op: replace + path: /spec/rules/0/host + value: {{ENVIRONMENT}}.pivcac.reviewapps.identitysandbox.gov + + destination: + server: 'https://kubernetes.default.svc' + namespace: review-apps + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true \ No newline at end of file diff --git a/dockerfiles/idp_review_app.Dockerfile b/dockerfiles/idp_review_app.Dockerfile index 298223e3da7..8aab8bd6afd 100644 --- a/dockerfiles/idp_review_app.Dockerfile +++ b/dockerfiles/idp_review_app.Dockerfile @@ -22,6 +22,7 @@ ENV POSTGRES_WORKER_NAME idp-worker-jobs ENV POSTGRES_WORKER_HOST postgres-worker ENV POSTGRES_WORKER_USERNAME postgres ENV POSTGRES_WORKER_PASSWORD postgres +ENV REDIS_IRS_ATTEMPTS_API_URL redis://redis:6379/2 ENV REDIS_THROTTLE_URL redis://redis:6379/1 ENV REDIS_URL redis://redis:6379 ENV ASSET_HOST http://localhost:3000 From 3bff32058d4f9470bcb3dba28c1c34eaa5467f3a Mon Sep 17 00:00:00 2001 From: Mitchell Henke Date: Wed, 9 Oct 2024 16:03:40 -0500 Subject: [PATCH 10/12] Fix account reset cancel race condition (#11329) * add failing spec * change database update strategy for account reset cancellation * Do not send notifications if request is not successful changelog: Bug Fixes, Account Reset, Fix race condition where concurrent requests to cancel could result in an exception * absorb NotifyUserOfRequestCancellation into PendingRequestForUser * Remove unused true/false return Co-authored-by: Andrew Duthie <1779930+aduth@users.noreply.github.com> * remove unnecessary allowed extra analytics --------- Co-authored-by: Andrew Duthie <1779930+aduth@users.noreply.github.com> --- .../account_reset/pending_controller.rb | 9 +- app/controllers/users/sessions_controller.rb | 4 +- .../account_reset/cancel_request_for_user.rb | 22 --- .../find_pending_request_for_user.rb | 23 ---- .../notify_user_of_request_cancellation.rb | 35 ----- .../account_reset/pending_request_for_user.rb | 63 +++++++++ .../account_reset/pending_controller_spec.rb | 4 + .../idv/phone_otp_rate_limiting_spec.rb | 2 +- .../cancel_request_for_user_spec.rb | 36 ----- .../find_pending_request_for_user_spec.rb | 64 --------- ...otify_user_of_request_cancellation_spec.rb | 38 ------ .../pending_request_for_user_spec.rb | 127 ++++++++++++++++++ 12 files changed, 203 insertions(+), 224 deletions(-) delete mode 100644 app/services/account_reset/cancel_request_for_user.rb delete mode 100644 app/services/account_reset/find_pending_request_for_user.rb delete mode 100644 app/services/account_reset/notify_user_of_request_cancellation.rb create mode 100644 app/services/account_reset/pending_request_for_user.rb delete mode 100644 spec/services/account_reset/cancel_request_for_user_spec.rb delete mode 100644 spec/services/account_reset/find_pending_request_for_user_spec.rb delete mode 100644 spec/services/account_reset/notify_user_of_request_cancellation_spec.rb create mode 100644 spec/services/account_reset/pending_request_for_user_spec.rb diff --git a/app/controllers/account_reset/pending_controller.rb b/app/controllers/account_reset/pending_controller.rb index 8216687c82b..61843f06ed2 100644 --- a/app/controllers/account_reset/pending_controller.rb +++ b/app/controllers/account_reset/pending_controller.rb @@ -19,7 +19,10 @@ def confirm def cancel analytics.pending_account_reset_cancelled - AccountReset::CancelRequestForUser.new(current_user).call + AccountReset::PendingRequestForUser.new(current_user).cancel_account_reset_request!( + account_reset_request_id: pending_account_reset_request.id, + cancelled_at: Time.zone.now, + ) end private @@ -29,9 +32,9 @@ def confirm_account_reset_request_exists end def pending_account_reset_request - @pending_account_reset_request ||= AccountReset::FindPendingRequestForUser.new( + @pending_account_reset_request ||= AccountReset::PendingRequestForUser.new( current_user, - ).call + ).get_account_reset_request end end end diff --git a/app/controllers/users/sessions_controller.rb b/app/controllers/users/sessions_controller.rb index 97ab15c0645..14e5fd4df50 100644 --- a/app/controllers/users/sessions_controller.rb +++ b/app/controllers/users/sessions_controller.rb @@ -261,9 +261,9 @@ def next_url_after_valid_authentication end def pending_account_reset_request - AccountReset::FindPendingRequestForUser.new( + AccountReset::PendingRequestForUser.new( current_user, - ).call + ).get_account_reset_request end def override_csp_for_google_analytics diff --git a/app/services/account_reset/cancel_request_for_user.rb b/app/services/account_reset/cancel_request_for_user.rb deleted file mode 100644 index 3ef053f2ab5..00000000000 --- a/app/services/account_reset/cancel_request_for_user.rb +++ /dev/null @@ -1,22 +0,0 @@ -# frozen_string_literal: true - -module AccountReset - class CancelRequestForUser - attr_reader :user - - def initialize(user) - @user = user - end - - def call(now: Time.zone.now) - account_reset_request.update!(cancelled_at: now) - NotifyUserOfRequestCancellation.new(user).call - end - - private - - def account_reset_request - FindPendingRequestForUser.new(user).call - end - end -end diff --git a/app/services/account_reset/find_pending_request_for_user.rb b/app/services/account_reset/find_pending_request_for_user.rb deleted file mode 100644 index 99ad93b7e57..00000000000 --- a/app/services/account_reset/find_pending_request_for_user.rb +++ /dev/null @@ -1,23 +0,0 @@ -# frozen_string_literal: true - -module AccountReset - class FindPendingRequestForUser - include AccountResetConcern - attr_reader :user - - def initialize(user) - @user = user - end - - def call - AccountResetRequest.where( - user: user, - granted_at: nil, - cancelled_at: nil, - ).where( - 'requested_at > ?', - account_reset_wait_period_days(user).ago, - ).order(requested_at: :asc).first - end - end -end diff --git a/app/services/account_reset/notify_user_of_request_cancellation.rb b/app/services/account_reset/notify_user_of_request_cancellation.rb deleted file mode 100644 index 37bba9d379a..00000000000 --- a/app/services/account_reset/notify_user_of_request_cancellation.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -module AccountReset - class NotifyUserOfRequestCancellation - attr_reader :user - - def initialize(user) - @user = user - end - - def call - notify_user_via_email_of_account_reset_cancellation - notify_user_via_phone_of_account_reset_cancellation - end - - private - - def notify_user_via_email_of_account_reset_cancellation - user.confirmed_email_addresses.each do |email_address| - UserMailer.with(user: user, email_address: email_address).account_reset_cancel. - deliver_now_or_later - end - end - - def notify_user_via_phone_of_account_reset_cancellation - MfaContext.new(user).phone_configurations.each do |phone_configuration| - phone = phone_configuration.phone - Telephony.send_account_reset_cancellation_notice( - to: phone, - country_code: Phonelib.parse(phone).country, - ) - end - end - end -end diff --git a/app/services/account_reset/pending_request_for_user.rb b/app/services/account_reset/pending_request_for_user.rb new file mode 100644 index 00000000000..0760767918a --- /dev/null +++ b/app/services/account_reset/pending_request_for_user.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module AccountReset + class PendingRequestForUser + include AccountResetConcern + attr_reader :user + + def initialize(user) + @user = user + end + + def get_account_reset_request + AccountResetRequest.where( + user: user, + granted_at: nil, + cancelled_at: nil, + ).where( + 'requested_at > ?', + account_reset_wait_period_days(user).ago, + ).order(requested_at: :asc).first + end + + def cancel_account_reset_request!(account_reset_request_id:, cancelled_at:) + # rubocop:disable Rails/SkipsModelValidations + result = AccountResetRequest.where( + id: account_reset_request_id, + user: user, + granted_at: nil, + cancelled_at: nil, + ).where( + 'requested_at > ?', + account_reset_wait_period_days(user).ago, + ).update_all(cancelled_at: cancelled_at, updated_at: Time.zone.now) + # rubocop:enable Rails/SkipsModelValidations + + notify_user! if result == 1 + end + + def notify_user! + notify_user_via_email_of_account_reset_cancellation + notify_user_via_phone_of_account_reset_cancellation + end + + private + + def notify_user_via_email_of_account_reset_cancellation + user.confirmed_email_addresses.each do |email_address| + UserMailer.with(user: user, email_address: email_address).account_reset_cancel. + deliver_now_or_later + end + end + + def notify_user_via_phone_of_account_reset_cancellation + MfaContext.new(user).phone_configurations.each do |phone_configuration| + phone = phone_configuration.phone + Telephony.send_account_reset_cancellation_notice( + to: phone, + country_code: Phonelib.parse(phone).country, + ) + end + end + end +end diff --git a/spec/controllers/account_reset/pending_controller_spec.rb b/spec/controllers/account_reset/pending_controller_spec.rb index ca02b49d59d..5bf5bed1b78 100644 --- a/spec/controllers/account_reset/pending_controller_spec.rb +++ b/spec/controllers/account_reset/pending_controller_spec.rb @@ -87,6 +87,10 @@ post :cancel expect(account_reset_request.reload.cancelled_at).to_not be_nil + expect_delivered_email( + to: [user.email_addresses.first.email], + subject: t('user_mailer.account_reset_cancel.subject'), + ) end context 'when the account reset request does not exist' do diff --git a/spec/features/idv/phone_otp_rate_limiting_spec.rb b/spec/features/idv/phone_otp_rate_limiting_spec.rb index a1ccdce5b88..c5d3c14ba72 100644 --- a/spec/features/idv/phone_otp_rate_limiting_spec.rb +++ b/spec/features/idv/phone_otp_rate_limiting_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper' -RSpec.feature 'phone otp rate limiting', :js, allowed_extra_analytics: [:*] do +RSpec.feature 'phone otp rate limiting', :js do include IdvStepHelper let(:user) { user_with_2fa } diff --git a/spec/services/account_reset/cancel_request_for_user_spec.rb b/spec/services/account_reset/cancel_request_for_user_spec.rb deleted file mode 100644 index 78f6ca04d26..00000000000 --- a/spec/services/account_reset/cancel_request_for_user_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'rails_helper' - -RSpec.describe AccountReset::CancelRequestForUser do - let(:user) { create(:user) } - let!(:account_reset_request) { AccountResetRequest.create(user: user, requested_at: 1.hour.ago) } - - subject { described_class.new(user) } - - describe '#call' do - let(:now) { Time.zone.now } - it 'cancels the account reset request' do - subject.call(now: now) - - expect(account_reset_request.reload.cancelled_at.to_i).to eq(now.to_i) - end - - it 'does not cancel account reset requests for a different user' do - other_user = create(:user) - other_request = AccountResetRequest.create(user: other_user, requested_at: 1.hour.ago) - - subject.call - - expect(other_request.reload.cancelled_at).to be_nil - end - - it "sends an email to the user's confirmed email addresses" do - notify_user_of_cancellation = instance_double(AccountReset::NotifyUserOfRequestCancellation) - expect(AccountReset::NotifyUserOfRequestCancellation).to receive(:new). - with(user). - and_return(notify_user_of_cancellation) - expect(notify_user_of_cancellation).to receive(:call) - - subject.call - end - end -end diff --git a/spec/services/account_reset/find_pending_request_for_user_spec.rb b/spec/services/account_reset/find_pending_request_for_user_spec.rb deleted file mode 100644 index bad22dc2f96..00000000000 --- a/spec/services/account_reset/find_pending_request_for_user_spec.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'rails_helper' - -RSpec.describe AccountReset::FindPendingRequestForUser do - describe '#call' do - let(:user) { create(:user) } - let(:granted_at) { nil } - let(:cancelled_at) { nil } - let(:requested_at) { 1.hour.ago } - - let!(:account_reset_request) do - AccountResetRequest.create( - user: user, - granted_at: granted_at, - cancelled_at: cancelled_at, - requested_at: requested_at, - ) - end - - subject { described_class.new(user) } - - context 'when a request exists' do - it { expect(subject.call).to eq(account_reset_request) } - end - - context 'when a request does not exist' do - let!(:account_reset_request) { nil } - - it { expect(subject.call).to be_nil } - end - - context 'when a request exists, but it has been granted' do - let(:granted_at) { 1.hour.ago } - - it { expect(subject.call).to be_nil } - end - - context 'when a request exists, but it is expired' do - let(:requested_at) { 1.year.ago } - - it { expect(subject.call).to be_nil } - end - - context 'when a request exists, but it has been cancelled' do - let(:cancelled_at) { 1.hour.ago } - - it { expect(subject.call).to be_nil } - end - - context 'fraud user' do - let(:user) { create(:user, :fraud_review_pending) } - let(:user2) { create(:user, :fraud_rejection) } - context 'when a request exists, and it hasnt been granted yet but over a day' do - let(:requested_at) { 30.hours.ago } - - it { expect(subject.call).to eq(account_reset_request) } - end - context 'when a request has expired' do - let(:requested_at) { 1.year.ago } - - it { expect(subject.call).to be_nil } - end - end - end -end diff --git a/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb b/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb deleted file mode 100644 index 9b0d1d9acf2..00000000000 --- a/spec/services/account_reset/notify_user_of_request_cancellation_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'rails_helper' - -RSpec.describe AccountReset::NotifyUserOfRequestCancellation do - let(:user) { create(:user) } - - subject { described_class.new(user) } - - describe '#call' do - it 'sends an email to all of the user email addresses' do - email_address1 = user.email_addresses.first - email_address2 = create(:email_address, user: user) - - subject.call - - expect_delivered_email_count(2) - expect_delivered_email( - to: [email_address1.email], - subject: t('user_mailer.account_reset_cancel.subject'), - ) - expect_delivered_email( - to: [email_address2.email], - subject: t('user_mailer.account_reset_cancel.subject'), - ) - end - - it 'sends a text to all of the user phone numbers' do - phone_config1 = create(:phone_configuration, user: user) - phone_config2 = create(:phone_configuration, user: user) - - expect(Telephony).to receive(:send_account_reset_cancellation_notice). - with(to: phone_config1.phone, country_code: 'US') - expect(Telephony).to receive(:send_account_reset_cancellation_notice). - with(to: phone_config2.phone, country_code: 'US') - - subject.call - end - end -end diff --git a/spec/services/account_reset/pending_request_for_user_spec.rb b/spec/services/account_reset/pending_request_for_user_spec.rb new file mode 100644 index 00000000000..764e23e13b9 --- /dev/null +++ b/spec/services/account_reset/pending_request_for_user_spec.rb @@ -0,0 +1,127 @@ +require 'rails_helper' + +RSpec.describe AccountReset::PendingRequestForUser do + let(:user) { create(:user) } + let(:granted_at) { nil } + let(:cancelled_at) { nil } + let(:requested_at) { 1.hour.ago } + let!(:account_reset_request) do + AccountResetRequest.create( + user: user, + granted_at: granted_at, + cancelled_at: cancelled_at, + requested_at: requested_at, + ) + end + subject { described_class.new(user) } + + describe '#get_account_reset_request' do + context 'when a request exists' do + it { expect(subject.get_account_reset_request).to eq(account_reset_request) } + end + + context 'when a request does not exist' do + let!(:account_reset_request) { nil } + + it { expect(subject.get_account_reset_request).to be_nil } + end + + context 'when a request exists, but it has been granted' do + let(:granted_at) { 1.hour.ago } + + it { expect(subject.get_account_reset_request).to be_nil } + end + + context 'when a request exists, but it is expired' do + let(:requested_at) { 1.year.ago } + + it { expect(subject.get_account_reset_request).to be_nil } + end + + context 'when a request exists, but it has been cancelled' do + let(:cancelled_at) { 1.hour.ago } + + it { expect(subject.get_account_reset_request).to be_nil } + end + + context 'fraud user' do + let(:user) { create(:user, :fraud_review_pending) } + context 'when a valid request exists for a user pending fraud review' do + let(:requested_at) { 30.hours.ago } + + it { expect(subject.get_account_reset_request).to eq(account_reset_request) } + end + context 'when a request has expired' do + let(:requested_at) { 1.year.ago } + + it { expect(subject.get_account_reset_request).to be_nil } + end + end + end + + describe '#cancel_account_reset_request!' do + let(:now) { Time.zone.now } + it 'cancels the account reset request' do + subject.cancel_account_reset_request!( + account_reset_request_id: account_reset_request.id, + cancelled_at: now, + ) + + expect(account_reset_request.reload.cancelled_at.to_i).to eq(now.to_i) + end + + it "sends an email to the user's confirmed email addresses" do + expect(subject).to receive(:notify_user!) + + subject.cancel_account_reset_request!( + account_reset_request_id: account_reset_request.id, + cancelled_at: now, + ) + end + + context 'with no existing pending request' do + let(:cancelled_at) { Time.zone.now } + it 'fails gracefully and does not send email' do + expect(subject).to_not receive(:notify_user!) + + expect do + subject.cancel_account_reset_request!( + account_reset_request_id: account_reset_request.id, + cancelled_at: now, + ) + end.to_not change { account_reset_request.cancelled_at } + end + end + end + + describe '#notify_user!' do + it 'sends an email to all of the user email addresses' do + email_address1 = user.email_addresses.first + email_address2 = create(:email_address, user: user) + + subject.notify_user! + + expect_delivered_email_count(2) + expect_delivered_email( + to: [email_address1.email], + subject: t('user_mailer.account_reset_cancel.subject'), + ) + expect_delivered_email( + to: [email_address2.email], + subject: t('user_mailer.account_reset_cancel.subject'), + ) + end + + it 'sends a text to all of the user phone numbers' do + phone_config1 = create(:phone_configuration, user: user) + phone_config2 = create(:phone_configuration, user: user) + + expect(Telephony).to receive(:send_account_reset_cancellation_notice). + with(to: phone_config1.phone, country_code: 'US') + expect(Telephony).to receive(:send_account_reset_cancellation_notice). + with(to: phone_config2.phone, country_code: 'US') + + subject.notify_user! + end + end +end From 99695711fc4b5ac20a59260a5fd92cba49f55188 Mon Sep 17 00:00:00 2001 From: A Shukla Date: Thu, 10 Oct 2024 08:39:41 -0500 Subject: [PATCH 11/12] Lg-14513 update selfie text french (#11330) * User-Facing Improvements, camera capture, update french text during camera capture * changelog: User-Facing Improvements, camera capture, update french text during camera capture * changelog: User-Facing Improvements, camera capture, update french text during camera capture --- config/locales/fr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 679e6d8a731..f489171a2f5 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -643,7 +643,7 @@ doc_auth.info.selfie_capture_status.face_not_found: Visage non trouvé doc_auth.info.selfie_capture_status.face_too_small: Visage trop petit doc_auth.info.selfie_capture_status.too_many_faces: Trop de visages doc_auth.info.selfie_capture.action.capture: Prendre une photo -doc_auth.info.selfie_capture.action.close: éteindre +doc_auth.info.selfie_capture.action.close: fermer doc_auth.info.selfie_capture.action.retake: nouvelle photo doc_auth.info.selfie_capture.action.submit: Photo prise. Envoyer la photo. doc_auth.info.selfie_capture.intro: Caméra prête à prendre la photo From a2e33f7ac6ea389e40de4883434391fe6c8ebac4 Mon Sep 17 00:00:00 2001 From: Vraj Mohan Date: Thu, 10 Oct 2024 06:43:47 -0700 Subject: [PATCH 12/12] LG-14397: Remove use_fed_domain_class (#11326) * LG-14397 Remove use_fed_domain_class config changelog: Internal, Configuration, Remove configuration not needed any longer * Fix incorrect example description * Add test for "email addresses" wihout domains --- app/models/email_address.rb | 7 +- config/application.yml.default | 3 - lib/identity_config.rb | 1 - ...or_authentication_setup_controller_spec.rb | 52 +----- .../piv_recommended_after_sign_in_spec.rb | 167 +++++------------- spec/features/users/sign_up_spec.rb | 98 +++------- spec/models/email_address_spec.rb | 53 ++---- spec/models/user_spec.rb | 17 +- 8 files changed, 113 insertions(+), 285 deletions(-) diff --git a/app/models/email_address.rb b/app/models/email_address.rb index 67c58d9f687..ca3a7b53c01 100644 --- a/app/models/email_address.rb +++ b/app/models/email_address.rb @@ -42,12 +42,7 @@ def fed_or_mil_email? end def fed_email? - if IdentityConfig.store.use_fed_domain_class - return false unless domain - FederalEmailDomain.fed_domain?(domain) - else - email.end_with?('.gov') - end + FederalEmailDomain.fed_domain?(domain) end def mil_email? diff --git a/config/application.yml.default b/config/application.yml.default index 3cd8963c1ea..8bb1fb5aaf3 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -366,7 +366,6 @@ test_ssn_allowed_list: '' totp_code_interval: 30 unauthorized_scope_enabled: false use_dashboard_service_providers: false -use_fed_domain_class: false use_kms: false use_vot_in_sp_requests: true usps_auth_token_refresh_job_enabled: false @@ -451,7 +450,6 @@ development: state_tracking_enabled: true telephony_adapter: test use_dashboard_service_providers: true - use_fed_domain_class: true usps_eipp_sponsor_id: '222222222222222' usps_ipp_sponsor_id: '111111111111111' usps_ipp_transliteration_enabled: true @@ -584,7 +582,6 @@ test: telephony_adapter: test test_ssn_allowed_list: '999999999' totp_code_interval: 3 - use_fed_domain_class: true usps_eipp_sponsor_id: '222222222222222' usps_ipp_root_url: 'http://localhost:1000' usps_ipp_sponsor_id: '111111111111111' diff --git a/lib/identity_config.rb b/lib/identity_config.rb index b00b0e25689..1fd5df98fcf 100644 --- a/lib/identity_config.rb +++ b/lib/identity_config.rb @@ -425,7 +425,6 @@ def self.store config.add(:usps_auth_token_refresh_job_enabled, type: :boolean) config.add(:usps_confirmation_max_days, type: :integer) config.add(:usps_eipp_sponsor_id, type: :string) - config.add(:use_fed_domain_class, type: :boolean) config.add(:usps_ipp_client_id, type: :string) config.add(:usps_ipp_password, type: :string) config.add(:usps_ipp_request_timeout, type: :integer) diff --git a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb index 5672ea37eb6..578cdb6e2dc 100644 --- a/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb +++ b/spec/controllers/users/two_factor_authentication_setup_controller_spec.rb @@ -19,51 +19,19 @@ ) end - context 'with user having gov or mil email and use_fed_domain_class set to false' do - let(:user) do - create(:user, email: 'example@example.gov', piv_cac_recommended_dismissed_at: Time.zone.now) - end + context 'with user having gov or mil email' do let!(:federal_domain) { create(:federal_email_domain, name: 'gsa.gov') } - - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) + let(:user) do + create( + :user, + email: 'example@gsa.gov', + piv_cac_recommended_dismissed_at: interstitial_dismissed_at, + ) end context 'having already visited the PIV interstitial page' do - it 'tracks the visit in analytics' do - get :index - - expect(@analytics).to have_logged_event( - 'User Registration: 2FA Setup visited', - enabled_mfa_methods_count: 0, - gov_or_mil_email: true, - ) - end - end + let(:interstitial_dismissed_at) { Time.zone.now } - context 'directed to page without having visited PIV interstitial page' do - let(:user) do - create(:user, email: 'example@example.gov') - end - - it 'redirects user to piv_recommended_path' do - get :index - - expect(response).to redirect_to(login_piv_cac_recommended_url) - end - end - end - - context 'with user having gov or mil email and use_fed_domain_class set to true' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) - end - - let!(:federal_domain) { create(:federal_email_domain, name: 'gsa.gov') } - let(:user) do - create(:user, email: 'example@gsa.gov', piv_cac_recommended_dismissed_at: Time.zone.now) - end - context 'having already visited the PIV interstitial page' do it 'tracks the visit in analytics' do get :index @@ -76,9 +44,7 @@ end context 'directed to page without having visited PIV interstitial page' do - let(:user) do - create(:user, email: 'example@gsa.gov') - end + let(:interstitial_dismissed_at) { nil } it 'redirects user to piv_recommended_path' do get :index diff --git a/spec/features/sign_in/piv_recommended_after_sign_in_spec.rb b/spec/features/sign_in/piv_recommended_after_sign_in_spec.rb index 2906a36a34e..f22a2d7f02c 100644 --- a/spec/features/sign_in/piv_recommended_after_sign_in_spec.rb +++ b/spec/features/sign_in/piv_recommended_after_sign_in_spec.rb @@ -1,132 +1,63 @@ require 'rails_helper' RSpec.feature 'Piv recommended after Sign in' do - context 'use_fed_domain_class set to true' do - let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } - - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) - end - - scenario 'User with valid fed email directed to recommend page and get to setup piv' do - user = create(:user, :with_phone, { email: 'example@gsa.gov' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) - expect(page).to have_current_path(setup_piv_cac_path) - end - - scenario 'User with mil email directed to recommended PIV page and goes to add piv page' do - user = create(:user, :with_phone, { email: 'example@army.mil' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) - expect(page).to have_current_path(setup_piv_cac_path) - end - - scenario 'User with fed email and skips recommendation page' do - user = create(:user, :with_phone, { email: 'example@gsa.gov' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.skip')) - expect(page).to have_current_path(account_path) - end - - scenario 'User with mil email and skips recommendation page' do - user = create(:user, :with_phone, { email: 'example@army.mil' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.skip')) - expect(page).to have_current_path(account_path) - end - - scenario 'User with invalid .gov email directed to account page' do - user = create(:user, :with_phone, { email: 'example@bad.gov' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(account_path) - end + let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } + + scenario 'User with valid fed email directed to recommend page and get to setup piv' do + user = create(:user, :with_phone, { email: 'example@gsa.gov' }) + + visit new_user_session_path + fill_in_credentials_and_submit(user.email, user.password) + fill_in_code_with_last_phone_otp + click_submit_default + expect(page).to have_current_path(login_piv_cac_recommended_path) + click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) + expect(page).to have_current_path(setup_piv_cac_path) end - context 'use_fed_domain_class set to false' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) - end - scenario 'User with .gov email directed to recommend page and get to setup piv' do - user = create(:user, :with_phone, { email: 'example@good.gov' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) - expect(page).to have_current_path(setup_piv_cac_path) - end + scenario 'User with mil email directed to recommended PIV page and goes to add piv page' do + user = create(:user, :with_phone, { email: 'example@army.mil' }) - scenario 'User with .mil email directed to recommended PIV page and goes to add piv page' do - user = create(:user, :with_phone, { email: 'example@army.mil' }) - - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) - expect(page).to have_current_path(setup_piv_cac_path) - end + visit new_user_session_path + fill_in_credentials_and_submit(user.email, user.password) + fill_in_code_with_last_phone_otp + click_submit_default + expect(page).to have_current_path(login_piv_cac_recommended_path) + click_button(t('two_factor_authentication.piv_cac_upsell.add_piv')) + expect(page).to have_current_path(setup_piv_cac_path) + end - scenario 'User with fed email and skips recommendation page' do - user = create(:user, :with_phone, { email: 'example@example.gov' }) + scenario 'User with fed email and skips recommendation page' do + user = create(:user, :with_phone, { email: 'example@gsa.gov' }) - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.skip')) - expect(page).to have_current_path(account_path) - end + visit new_user_session_path + fill_in_credentials_and_submit(user.email, user.password) + fill_in_code_with_last_phone_otp + click_submit_default + expect(page).to have_current_path(login_piv_cac_recommended_path) + click_button(t('two_factor_authentication.piv_cac_upsell.skip')) + expect(page).to have_current_path(account_path) + end - scenario 'User with mil email and skips recommendation page' do - user = create(:user, :with_phone, { email: 'example@army.mil' }) + scenario 'User with mil email and skips recommendation page' do + user = create(:user, :with_phone, { email: 'example@army.mil' }) - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(login_piv_cac_recommended_path) - click_button(t('two_factor_authentication.piv_cac_upsell.skip')) - expect(page).to have_current_path(account_path) - end + visit new_user_session_path + fill_in_credentials_and_submit(user.email, user.password) + fill_in_code_with_last_phone_otp + click_submit_default + expect(page).to have_current_path(login_piv_cac_recommended_path) + click_button(t('two_factor_authentication.piv_cac_upsell.skip')) + expect(page).to have_current_path(account_path) + end - scenario 'User with invalid no .gov or .mil email directed to account page' do - user = create(:user, :with_phone, { email: 'example@bad.com' }) + scenario 'User with invalid .gov email directed to account page' do + user = create(:user, :with_phone, { email: 'example@bad.gov' }) - visit new_user_session_path - fill_in_credentials_and_submit(user.email, user.password) - fill_in_code_with_last_phone_otp - click_submit_default - expect(page).to have_current_path(account_path) - end + visit new_user_session_path + fill_in_credentials_and_submit(user.email, user.password) + fill_in_code_with_last_phone_otp + click_submit_default + expect(page).to have_current_path(account_path) end end diff --git a/spec/features/users/sign_up_spec.rb b/spec/features/users/sign_up_spec.rb index b6fef7c4ab1..7dad8caa392 100644 --- a/spec/features/users/sign_up_spec.rb +++ b/spec/features/users/sign_up_spec.rb @@ -512,20 +512,18 @@ def clipboard_text end describe 'User Directed to Piv Cac recommended' do - context 'set config use_fed_domain_class to false' do - let(:email) { 'test@test.gov' } - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) - end + let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } + let(:email) { 'test@gsa.gov' } + context 'when the user has a fed email' do it 'should land user on piv cac suggestion page' do confirm_email(email) submit_form_with_valid_password expect(current_path).to eq login_piv_cac_recommended_path end - context 'user can skip piv cac prompt' do - it 'should skip piv cac prompt and land on mfa screen' do + context 'when the user chooses to skip adding piv' do + it 'should land on mfa screen' do confirm_email(email) submit_form_with_valid_password expect(current_path).to eq login_piv_cac_recommended_path @@ -535,8 +533,8 @@ def clipboard_text end end - context 'user who selects to add piv is directed to piv screen' do - it 'should be directed straight to piv add screen' do + context 'when the user chooses to add piv' do + it 'should land on piv add screen' do confirm_email(email) submit_form_with_valid_password expect(current_path).to eq login_piv_cac_recommended_path @@ -547,81 +545,43 @@ def clipboard_text end end - context 'set config use_fed_domain_class to true' do - let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } - let(:email) { 'test@gsa.gov' } - - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) + context 'when the user has a mil email' do + let(:email) { 'test@example.mil' } + it 'should land user on piv cac suggestion page' do + confirm_email(email) + submit_form_with_valid_password + expect(current_path).to eq login_piv_cac_recommended_path end - context 'valid fed email' do - it 'should land user on piv cac suggestion page when fed government' do + + context 'when the user chooses to skip adding piv' do + it 'should land on mfa screen' do confirm_email(email) submit_form_with_valid_password expect(current_path).to eq login_piv_cac_recommended_path - end - - context 'user can skip piv cac prompt' do - it 'should skip piv cac prompt and land on mfa screen' do - confirm_email(email) - submit_form_with_valid_password - expect(current_path).to eq login_piv_cac_recommended_path - click_button t('two_factor_authentication.piv_cac_upsell.choose_other_method') - - expect(current_path).to eq authentication_methods_setup_path - end - end - - context 'user who selects to add piv is directed to piv screen' do - it 'should be directed straight to piv add screen' do - confirm_email(email) - submit_form_with_valid_password - expect(current_path).to eq login_piv_cac_recommended_path - click_button t('two_factor_authentication.piv_cac_upsell.add_piv') + click_button t('two_factor_authentication.piv_cac_upsell.choose_other_method') - expect(current_path).to eq setup_piv_cac_path - end + expect(current_path).to eq authentication_methods_setup_path end end - context 'any mil email' do - let(:email) { 'test@example.mil' } - it 'should land user on piv cac suggestion page when fed government' do + context 'when the user chooses to add piv' do + it 'should land on piv add screen' do confirm_email(email) submit_form_with_valid_password expect(current_path).to eq login_piv_cac_recommended_path - end - - context 'user can skip piv cac prompt' do - it 'should skip piv cac prompt and land on mfa screen' do - confirm_email(email) - submit_form_with_valid_password - expect(current_path).to eq login_piv_cac_recommended_path - click_button t('two_factor_authentication.piv_cac_upsell.choose_other_method') - - expect(current_path).to eq authentication_methods_setup_path - end - end - - context 'user who selects to add piv is directed to piv screen' do - it 'should be directed straight to piv add screen' do - confirm_email(email) - submit_form_with_valid_password - expect(current_path).to eq login_piv_cac_recommended_path - click_button t('two_factor_authentication.piv_cac_upsell.add_piv') + click_button t('two_factor_authentication.piv_cac_upsell.add_piv') - expect(current_path).to eq setup_piv_cac_path - end + expect(current_path).to eq setup_piv_cac_path end end + end - context 'invalid fed email' do - let(:email) { 'test@example.gov' } - it 'should land user on piv cac suggestion page when fed government' do - confirm_email(email) - submit_form_with_valid_password - expect(current_path).to eq authentication_methods_setup_path - end + context 'when the user does not have a fed or mil email' do + let(:email) { 'test@example.gov' } + it 'should skip piv cac recommendation page' do + confirm_email(email) + submit_form_with_valid_password + expect(current_path).to eq authentication_methods_setup_path end end end diff --git a/spec/models/email_address_spec.rb b/spec/models/email_address_spec.rb index eb9b3f9f0d1..764d2afc148 100644 --- a/spec/models/email_address_spec.rb +++ b/spec/models/email_address_spec.rb @@ -92,51 +92,36 @@ describe '#fed_or_mil_email?' do subject(:result) { email_address.fed_or_mil_email? } - context 'with an email domain that is a fed email' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) - end - let(:email) { 'example@example.gov' } + context 'with an email that is in federal_email_domains' do + let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } + let(:email) { 'example@gsa.gov' } it { expect(result).to eq(true) } end - context 'with an email that is a mil email' do + context 'with an email that has a .mil TLD' do let(:email) { 'example@example.mil' } it { expect(result).to eq(true) } end - context 'with an email that is not a mil or fed email' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) - end - + context 'with an email that is neither in federal_email_domains nor has a .mil TLD' do let(:email) { 'example@bad.gov' } it { expect(result).to eq(false) } end - - context 'with a non fed email while use_fed_domain_class set to true' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) - end - let(:email) { 'example@good.gov' } - - it { expect(result).to eq(false) } - end end describe '#mil_email?' do subject(:result) { email_address.mil_email? } - context 'with an email domain not a mil email' do + context 'with an email that does not have a .mil TLD' do let(:email) { 'example@example.gov' } it { expect(result).to eq(false) } end - context 'with an email domain ending in a mil domain email' do + context 'with an email that has a .mil TLD' do let(:email) { 'example@example.mil' } it { expect(result).to eq(true) } @@ -165,22 +150,22 @@ subject(:result) { email_address.fed_email? } let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } - context 'use_fed_domain_class set to true' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(true) - end + context 'with an email domain that is not in federal_email_domains' do + let(:email) { 'example@bad.gov' } - context 'with an email domain not a fed email' do - let(:email) { 'example@bad.gov' } + it { expect(result).to eq(false) } + end - it { expect(result).to eq(false) } - end + context 'with an email domain that is in federal_email_domains' do + let(:email) { 'example@gsa.gov' } - context 'with an email domain ending in a fed domain email' do - let(:email) { 'example@gsa.gov' } + it { expect(result).to eq(true) } + end - it { expect(result).to eq(true) } - end + context 'with a bad email address that has no domain' do + let(:email) { 'email_with_no_domain' } + + it { expect(result).to eq(false) } end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 387260dace3..94c77ed8f6c 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1695,24 +1695,19 @@ def it_should_not_send_survey end describe '#has_fed_or_mil_email?' do - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) - end + let!(:federal_email_domain) { create(:federal_email_domain, name: 'gsa.gov') } - context 'with a valid fed email in domain file' do - let(:user) { create(:user, email: 'example@example.gov') } + context 'with an email in federal_email_domains' do + let(:user) { create(:user, email: 'example@gsa.gov') } it 'should return true' do expect(user.has_fed_or_mil_email?).to eq(true) end end - context 'with use_fed_domain_class set to false and random .gov email' do + context 'with an email not in federal_email_domains' do let(:user) { create(:user, email: 'example@example.gov') } - before do - allow(IdentityConfig.store).to receive(:use_fed_domain_class).and_return(false) - end - it 'should return true' do - expect(user.has_fed_or_mil_email?).to eq(true) + it 'should return false' do + expect(user.has_fed_or_mil_email?).to eq(false) end end