diff --git a/Gemfile.lock b/Gemfile.lock index f01a154c105..4d234ca5b0b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -418,7 +418,7 @@ GEM net-protocol timeout net-ssh (6.1.0) - newrelic_rpm (8.8.0) + newrelic_rpm (8.12.0) nio4r (2.5.8) nokogiri (1.13.9) mini_portile2 (~> 2.8.0) diff --git a/app/assets/stylesheets/components/_password-toggle.scss b/app/assets/stylesheets/components/_password-toggle.scss index 39a9d11312f..469b26d5cb3 100644 --- a/app/assets/stylesheets/components/_password-toggle.scss +++ b/app/assets/stylesheets/components/_password-toggle.scss @@ -1,22 +1,12 @@ lg-password-toggle { display: block; - position: relative; - &.password-toggle--toggle-top .password-toggle__toggle-label { - @include u-pin-right; - top: -24px; + .validated-field__input-wrapper { + margin-bottom: 0; } - - &.password-toggle--toggle-bottom .validated-field__input-wrapper { - margin-bottom: units(2); - } -} - -.password-toggle__toggle-wrapper { - display: flex; - justify-content: end; } .password-toggle__toggle-label.usa-checkbox__label { @include u-margin-y(0); + @include u-padding-y(1); } diff --git a/app/components/password_toggle_component.html.erb b/app/components/password_toggle_component.html.erb index 5875ef810a3..e617a47820e 100644 --- a/app/components/password_toggle_component.html.erb +++ b/app/components/password_toggle_component.html.erb @@ -1,27 +1,25 @@ -<%= content_tag(:'lg-password-toggle', class: css_class) do %> +<%= content_tag(:'lg-password-toggle', **tag_options) do %> <%= render ValidatedFieldComponent.new( form: form, name: :password, type: :password, - label: label, + label: default_label, **field_options, input_html: field_options[:input_html].to_h.merge( id: input_id, class: ['password-toggle__input', *field_options.dig(:input_html, :class)], ), ) %> -
- - -
+ + <% end %> diff --git a/app/components/password_toggle_component.rb b/app/components/password_toggle_component.rb index 7b693f64c6f..b861653780f 100644 --- a/app/components/password_toggle_component.rb +++ b/app/components/password_toggle_component.rb @@ -1,25 +1,21 @@ class PasswordToggleComponent < BaseComponent - attr_reader :form, :label, :toggle_label, :toggle_position, :field_options + attr_reader :form, :label, :toggle_label, :field_options, :tag_options def initialize( form:, - label: t('components.password_toggle.label'), toggle_label: t('components.password_toggle.toggle_label'), - toggle_position: :top, - **field_options + field_options: {}, + **tag_options ) @form = form @label = label @toggle_label = toggle_label - @toggle_position = toggle_position @field_options = field_options + @tag_options = tag_options end - def css_class - classes = [] - classes << 'password-toggle--toggle-top' if toggle_position == :top - classes << 'password-toggle--toggle-bottom' if toggle_position == :bottom - classes + def default_label + t('components.password_toggle.label') end def toggle_id diff --git a/app/controllers/api/irs_attempts_api_controller.rb b/app/controllers/api/irs_attempts_api_controller.rb index 49632706c1f..068759865e7 100644 --- a/app/controllers/api/irs_attempts_api_controller.rb +++ b/app/controllers/api/irs_attempts_api_controller.rb @@ -23,7 +23,8 @@ def create headers['X-Payload-Key'] = Base64.strict_encode64(result.encrypted_key) headers['X-Payload-IV'] = Base64.strict_encode64(result.iv) - send_data Base64.strict_encode64(result.encrypted_data), + + send_data result.encrypted_data, disposition: "filename=#{result.filename}" else render json: { status: :unprocessable_entity, description: 'Invalid timestamp parameter' }, @@ -42,25 +43,21 @@ def authenticate_client end end + # @return [Array] JWE strings def security_event_tokens - return {} unless timestamp + return [] unless timestamp events = redis_client.read_events(timestamp: timestamp) - sets = {} - events.each_pair do |k, v| - key_id, jti = k.split(':') - sets[jti] = { key_id => v } - end - sets + events.values end def encrypted_security_event_log_result - json = security_event_tokens.to_json + events = security_event_tokens.join("\r\n") decoded_key_der = Base64.strict_decode64(IdentityConfig.store.irs_attempt_api_public_key) pub_key = OpenSSL::PKey::RSA.new(decoded_key_der) IrsAttemptsApi::EnvelopeEncryptor.encrypt( - data: json, timestamp: timestamp, public_key: pub_key, + data: events, timestamp: timestamp, public_key: pub_key, ) end @@ -74,7 +71,7 @@ def valid_auth_tokens def analytics_properties { - rendered_event_count: security_event_tokens.keys.count, + rendered_event_count: security_event_tokens.count, timestamp: timestamp&.iso8601, success: timestamp.present?, } @@ -84,7 +81,9 @@ def timestamp timestamp_param = params.permit(:timestamp)[:timestamp] return nil if timestamp_param.nil? - Time.strptime(timestamp_param, '%Y-%m-%dT%H:%M:%S%z') + date_fmt = timestamp_param.include?('.') ? '%Y-%m-%dT%H:%M:%S.%N%z' : '%Y-%m-%dT%H:%M:%S%z' + + Time.strptime(timestamp_param, date_fmt) rescue ArgumentError nil end diff --git a/app/controllers/concerns/billable_event_trackable.rb b/app/controllers/concerns/billable_event_trackable.rb index 0f08e90debf..80850d0b6e6 100644 --- a/app/controllers/concerns/billable_event_trackable.rb +++ b/app/controllers/concerns/billable_event_trackable.rb @@ -3,7 +3,6 @@ def track_billing_events if current_session_has_been_billed? create_sp_return_log(billable: false) else - increment_sp_monthly_auths create_sp_return_log(billable: true) mark_current_session_billed end @@ -11,14 +10,6 @@ def track_billing_events private - def increment_sp_monthly_auths - MonthlySpAuthCount.increment( - user_id: current_user.id, - service_provider: current_sp, - ial: sp_session_ial, - ) - end - def create_sp_return_log(billable:) user_ial_context = IalContext.new( ial: ial_context.ial, service_provider: current_sp, user: current_user, diff --git a/app/controllers/concerns/two_factor_authenticatable_methods.rb b/app/controllers/concerns/two_factor_authenticatable_methods.rb index b80401b0fb7..e46d852f8ea 100644 --- a/app/controllers/concerns/two_factor_authenticatable_methods.rb +++ b/app/controllers/concerns/two_factor_authenticatable_methods.rb @@ -223,10 +223,10 @@ def phone_confirmed end def send_phone_added_email - event = create_user_event_with_disavowal(:phone_added, current_user) + _event, disavowal_token = create_user_event_with_disavowal(:phone_added, current_user) current_user.confirmed_email_addresses.each do |email_address| UserMailer.with(user: current_user, email_address: email_address). - phone_added(disavowal_token: event.disavowal_token).deliver_now_or_later + phone_added(disavowal_token: disavowal_token).deliver_now_or_later end end diff --git a/app/controllers/concerns/unconfirmed_user_concern.rb b/app/controllers/concerns/unconfirmed_user_concern.rb index 8cc637457c4..5742e67cff1 100644 --- a/app/controllers/concerns/unconfirmed_user_concern.rb +++ b/app/controllers/concerns/unconfirmed_user_concern.rb @@ -35,12 +35,12 @@ def track_user_already_confirmed_event def stop_if_invalid_token result = email_confirmation_token_validator.submit analytics.user_registration_email_confirmation(**result.to_h) + return if result.success? irs_attempts_api_tracker.user_registration_email_confirmation( email: @email_address&.email, - success: result.success?, + success: false, failure_reason: irs_attempts_api_tracker.parse_failure_reason(result), ) - return if result.success? process_unsuccessful_confirmation end diff --git a/app/controllers/idv/gpo_verify_controller.rb b/app/controllers/idv/gpo_verify_controller.rb index 6b4bca2cb8d..2da6832d51b 100644 --- a/app/controllers/idv/gpo_verify_controller.rb +++ b/app/controllers/idv/gpo_verify_controller.rb @@ -42,12 +42,12 @@ def create if result.extra[:pending_in_person_enrollment] redirect_to idv_in_person_ready_to_verify_url else - event = create_user_event_with_disavowal(:account_verified) + event, disavowal_token = create_user_event_with_disavowal(:account_verified) UserAlerts::AlertUserAboutAccountVerified.call( user: current_user, date_time: event.created_at, sp_name: decorated_session.sp_name, - disavowal_token: event.disavowal_token, + disavowal_token: disavowal_token, ) flash[:success] = t('account.index.verification.success') redirect_to sign_up_completed_url diff --git a/app/controllers/idv/in_person/address_search_controller.rb b/app/controllers/idv/in_person/address_search_controller.rb new file mode 100644 index 00000000000..f1bc0151f2f --- /dev/null +++ b/app/controllers/idv/in_person/address_search_controller.rb @@ -0,0 +1,31 @@ +module Idv + module InPerson + class AddressSearchController < ApplicationController + include RenderConditionConcern + + check_or_render_not_found -> { IdentityConfig.store.arcgis_search_enabled } + + def index + render json: addresses + end + + protected + + def addresses + suggestion = geocoder.suggest(permitted_params[:address]).first + return [] unless suggestion + geocoder.find_address_candidates(suggestion.magic_key).slice(0, 1) + rescue Faraday::ConnectionFailed + [] + end + + def geocoder + @geocoder ||= ArcgisApi::Geocoder.new + end + + def permitted_params + params.permit(:address) + end + end + end +end diff --git a/app/controllers/idv/review_controller.rb b/app/controllers/idv/review_controller.rb index c1559de514d..4172962da8a 100644 --- a/app/controllers/idv/review_controller.rb +++ b/app/controllers/idv/review_controller.rb @@ -96,12 +96,12 @@ def init_profile end if idv_session.profile.active? - event = create_user_event_with_disavowal(:account_verified) + event, disavowal_token = create_user_event_with_disavowal(:account_verified) UserAlerts::AlertUserAboutAccountVerified.call( user: current_user, date_time: event.created_at, sp_name: decorated_session.sp_name, - disavowal_token: event.disavowal_token, + disavowal_token: disavowal_token, ) end end diff --git a/app/controllers/sign_up/email_confirmations_controller.rb b/app/controllers/sign_up/email_confirmations_controller.rb index 21e66b4399e..65a315deb59 100644 --- a/app/controllers/sign_up/email_confirmations_controller.rb +++ b/app/controllers/sign_up/email_confirmations_controller.rb @@ -21,6 +21,11 @@ def clear_setup_piv_cac_from_sign_in def process_successful_confirmation process_valid_confirmation_token + irs_attempts_api_tracker.user_registration_email_confirmation( + email: @email_address&.email, + success: true, + failure_reason: nil, + ) request_id = params.fetch(:_request_id, '') redirect_to sign_up_enter_password_url( request_id: request_id, confirmation_token: @confirmation_token, diff --git a/app/controllers/two_factor_authentication/personal_key_verification_controller.rb b/app/controllers/two_factor_authentication/personal_key_verification_controller.rb index 3f8dc40d29d..ec13ab82b87 100644 --- a/app/controllers/two_factor_authentication/personal_key_verification_controller.rb +++ b/app/controllers/two_factor_authentication/personal_key_verification_controller.rb @@ -35,8 +35,8 @@ def presenter_for_two_factor_authentication_method def handle_result(result) if result.success? - event = create_user_event_with_disavowal(:personal_key_used) - alert_user_about_personal_key_sign_in(event) + _event, disavowal_token = create_user_event_with_disavowal(:personal_key_used) + alert_user_about_personal_key_sign_in(disavowal_token) generate_new_personal_key_for_verified_users_otherwise_retire_the_key_and_ensure_two_mfa handle_valid_otp else @@ -44,10 +44,8 @@ def handle_result(result) end end - def alert_user_about_personal_key_sign_in(event) - response = UserAlerts::AlertUserAboutPersonalKeySignIn.call( - current_user, event.disavowal_token - ) + def alert_user_about_personal_key_sign_in(disavowal_token) + response = UserAlerts::AlertUserAboutPersonalKeySignIn.call(current_user, disavowal_token) analytics.personal_key_alert_about_sign_in(**response.to_h) end diff --git a/app/controllers/users/passwords_controller.rb b/app/controllers/users/passwords_controller.rb index b9a482d2ca7..8824dcee62c 100644 --- a/app/controllers/users/passwords_controller.rb +++ b/app/controllers/users/passwords_controller.rb @@ -56,8 +56,8 @@ def send_password_reset_risc_event end def create_event_and_notify_user_about_password_change - event = create_user_event_with_disavowal(:password_changed) - UserAlerts::AlertUserAboutPasswordChange.call(current_user, event.disavowal_token) + _event, disavowal_token = create_user_event_with_disavowal(:password_changed) + UserAlerts::AlertUserAboutPasswordChange.call(current_user, disavowal_token) end def handle_invalid_password diff --git a/app/controllers/users/reset_passwords_controller.rb b/app/controllers/users/reset_passwords_controller.rb index 412863ef0ad..898d136cbd2 100644 --- a/app/controllers/users/reset_passwords_controller.rb +++ b/app/controllers/users/reset_passwords_controller.rb @@ -149,8 +149,8 @@ def handle_unsuccessful_password_reset(result) end def create_reset_event_and_send_notification - event = create_user_event_with_disavowal(:password_changed, resource) - UserAlerts::AlertUserAboutPasswordChange.call(resource, event.disavowal_token) + _event, disavowal_token = create_user_event_with_disavowal(:password_changed, resource) + UserAlerts::AlertUserAboutPasswordChange.call(resource, disavowal_token) end def user_params diff --git a/app/decorators/user_decorator.rb b/app/decorators/user_decorator.rb index 8441c138546..265d018068c 100644 --- a/app/decorators/user_decorator.rb +++ b/app/decorators/user_decorator.rb @@ -62,8 +62,8 @@ def identity_verified?(service_provider: nil) end def reproof_for_irs?(service_provider:) - return false unless user.active_profile.present? return false unless service_provider&.irs_attempts_api_enabled + return false unless user.active_profile.present? !user.active_profile.initiating_service_provider&.irs_attempts_api_enabled end diff --git a/app/jobs/irs_attempts_events_batch_job.rb b/app/jobs/irs_attempts_events_batch_job.rb new file mode 100644 index 00000000000..55d2cc31583 --- /dev/null +++ b/app/jobs/irs_attempts_events_batch_job.rb @@ -0,0 +1,30 @@ +class IrsAttemptsEventsBatchJob < ApplicationJob + queue_as :default + + def perform(timestamp: Time.zone.now - 1.hour, dir_path: './attempts_api_output') + return nil unless IdentityConfig.store.irs_attempt_api_enabled + + events = IrsAttemptsApi::RedisClient.new.read_events(timestamp: timestamp) + event_values = events.values.join("\r\n") + + decoded_key_der = Base64.strict_decode64(IdentityConfig.store.irs_attempt_api_public_key) + pub_key = OpenSSL::PKey::RSA.new(decoded_key_der) + + result = IrsAttemptsApi::EnvelopeEncryptor.encrypt( + data: event_values, timestamp: timestamp, public_key: pub_key, + ) + + # write to a file and store on the disk until S3 is setup + FileUtils.mkdir_p(dir_path) + + file_path = "#{dir_path}/#{result.filename}" + + File.open(file_path, 'wb') do |file| + file.write(result.encrypted_data) + end + + return { encryptor_result: result, file_path: file_path } + + # Write the file to S3 instead of whatever dir_path winds up being + end +end diff --git a/app/jobs/reports/daily_dropoffs_report.rb b/app/jobs/reports/daily_dropoffs_report.rb index 52fa0dd03c9..101f7848a37 100644 --- a/app/jobs/reports/daily_dropoffs_report.rb +++ b/app/jobs/reports/daily_dropoffs_report.rb @@ -4,6 +4,20 @@ module Reports class DailyDropoffsReport < BaseReport REPORT_NAME = 'daily-dropoffs-report' + STEPS = %w[ + welcome + agreement + capture_document + cap_doc_submit + ssn + verify_info + verify_submit + phone + encrypt + personal_key + verified + ].freeze + include GoodJob::ActiveJobExtensions::Concurrency good_job_control_concurrency_with( @@ -50,7 +64,7 @@ def report_body agency start finish - ] + Db::DocAuthLog::DropOffRatesHelper::STEPS + ] + STEPS query_results.each do |sp_result| csv << [ @@ -60,7 +74,7 @@ def report_body sp_result['agency'], start.iso8601, finish.iso8601, - *Db::DocAuthLog::DropOffRatesHelper::STEPS.map { |step| sp_result[step].to_i }, + *STEPS.map { |step| sp_result[step].to_i }, ] end end diff --git a/app/jobs/reports/doc_auth_drop_off_rates_per_sprint_report.rb b/app/jobs/reports/doc_auth_drop_off_rates_per_sprint_report.rb deleted file mode 100644 index 9f60736b64c..00000000000 --- a/app/jobs/reports/doc_auth_drop_off_rates_per_sprint_report.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'identity/hostdata' - -module Reports - class DocAuthDropOffRatesPerSprintReport < BaseReport - REPORT_NAME = 'doc-auth-drop-offs-per-sprint-report'.freeze - FIRST_SPRINT_DATE = '10-10-2019'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - ret = generate_report - save_report(REPORT_NAME, ret.join, extension: 'txt') - end - - private - - def generate_report - date = Date.strptime(FIRST_SPRINT_DATE, '%m-%d-%Y') - ret = [] - today_date = Time.zone.today - generate_sprints(ret, date, today_date) - ret - end - - def generate_sprints(ret, date, today_date) - while date < today_date - transaction_with_timeout do - start = date - finish = date.next_day(14) - ret << Db::DocAuthLog::BlanketDropOffRatesAllSpsInRange.new. - call('Sprint', fmt(start), fmt(finish)) - date = finish - end - end - end - - def fmt(date) - date.strftime('%m-%d-%Y') - end - end -end diff --git a/app/jobs/reports/doc_auth_drop_off_rates_report.rb b/app/jobs/reports/doc_auth_drop_off_rates_report.rb deleted file mode 100644 index 5b43d713e22..00000000000 --- a/app/jobs/reports/doc_auth_drop_off_rates_report.rb +++ /dev/null @@ -1,132 +0,0 @@ -require 'identity/hostdata' - -module Reports - class DocAuthDropOffRatesReport < BaseReport - REPORT_NAME = 'doc-auth-drop-off-rates-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - ret = generate_report - save_report(REPORT_NAME, ret.join, extension: 'txt') - end - - private - - def generate_report - ret = [] - transaction_with_timeout do - generate_blanket_report_all_sps(ret) - generate_blanket_reports_per_sp(ret) - generate_overall_report_all_sps(ret) - generate_overall_reports_per_sp(ret) - end - ret - end - - def generate_blanket_reports_per_sp(ret) - ServiceProvider.where(ial: 2).each do |sp| - generate_blanket_report_per_sp(sp, ret) - end - end - - def generate_overall_reports_per_sp(ret) - ServiceProvider.where(ial: 2).each do |sp| - generate_overall_report_per_sp(sp, ret) - end - end - - def generate_blanket_report_per_sp(sp, ret) - blanket_drop_off_rates_per_sp_all_time(ret, sp) - blanket_drop_off_rates_per_sp_last_30_days(ret, sp) - blanket_drop_off_rates_per_sp_last_24_hours(ret, sp) - end - - def generate_overall_report_per_sp(sp, ret) - overall_drop_off_rates_per_sp_all_time(ret, sp) - overall_drop_off_rates_per_sp_last_30_days(ret, sp) - overall_drop_off_rates_per_sp_last_24_hours(ret, sp) - end - - def generate_blanket_report_all_sps(ret) - blanket_drop_off_rates_all_sps_all_time(ret) - blanket_drop_off_rates_all_sps_last_30_days(ret) - blanket_drop_off_rates_all_sps_last_24_hours(ret) - end - - def generate_overall_report_all_sps(ret) - overall_drop_off_rates_all_sps_all_time(ret) - overall_drop_off_rates_all_sps_last_30_days(ret) - overall_drop_off_rates_all_sps_last_24_hours(ret) - end - - def overall_drop_off_rates_all_sps_all_time(ret) - ret << Db::DocAuthLog::OverallDropOffRatesAllSpsAllTime.new. - call('Overall drop off rates for all SPs all time') - end - - def overall_drop_off_rates_all_sps_last_24_hours(ret) - ret << Db::DocAuthLog::OverallDropOffRatesAllSpsInRange.new. - call('Overall drop off rates for all SPs last 24 hours', Date.yesterday, today) - end - - def overall_drop_off_rates_all_sps_last_30_days(ret) - ret << Db::DocAuthLog::OverallDropOffRatesAllSpsInRange.new. - call('Overall drop off rates for all SPs last 30 days', today - 30.days, today) - end - - def blanket_drop_off_rates_all_sps_all_time(ret) - ret << Db::DocAuthLog::BlanketDropOffRatesAllSpsAllTime.new. - call('Blanket drop off rates for all SPs all time') - end - - def blanket_drop_off_rates_all_sps_last_24_hours(ret) - ret << Db::DocAuthLog::BlanketDropOffRatesAllSpsInRange.new. - call('Blanket drop off rates for all SPs last 24 hours', Date.yesterday, today) - end - - def blanket_drop_off_rates_all_sps_last_30_days(ret) - ret << Db::DocAuthLog::BlanketDropOffRatesAllSpsInRange.new. - call('Blanket drop off rates for all SPs last 30 days', today - 30.days, today) - end - - def blanket_drop_off_rates_per_sp_all_time(ret, sp) - ret << Db::DocAuthLog::BlanketDropOffRatesPerSpAllTime.new. - call('Blanket drop off rates per SP all time', sp.issuer) - end - - def blanket_drop_off_rates_per_sp_last_24_hours(ret, sp) - ret << Db::DocAuthLog::BlanketDropOffRatesPerSpInRange.new. - call('Blanket drop off rates last 24 hours', sp.issuer, Date.yesterday, today) - end - - def blanket_drop_off_rates_per_sp_last_30_days(ret, sp) - ret << Db::DocAuthLog::BlanketDropOffRatesPerSpInRange.new. - call('Blanket drop off rates last 30 days', sp.issuer, today - 30.days, today) - end - - def overall_drop_off_rates_per_sp_all_time(ret, sp) - ret << Db::DocAuthLog::OverallDropOffRatesPerSpAllTime.new. - call('Overall drop off rates per SP all time', sp.issuer) - end - - def overall_drop_off_rates_per_sp_last_24_hours(ret, sp) - ret << Db::DocAuthLog::OverallDropOffRatesPerSpInRange.new. - call('Overall drop off rates last 24 hours', sp.issuer, Date.yesterday, today) - end - - def overall_drop_off_rates_per_sp_last_30_days(ret, sp) - ret << Db::DocAuthLog::OverallDropOffRatesPerSpInRange.new. - call('Overall drop off rates last 30 days', sp.issuer, today - 30.days, today) - end - - def today - @today ||= Date.current - end - end -end diff --git a/app/jobs/reports/doc_auth_funnel_report.rb b/app/jobs/reports/doc_auth_funnel_report.rb deleted file mode 100644 index ac6b81d9b3e..00000000000 --- a/app/jobs/reports/doc_auth_funnel_report.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'identity/hostdata' - -module Reports - class DocAuthFunnelReport < BaseReport - REPORT_NAME = 'doc-auth-funnel-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - report = transaction_with_timeout do - Db::DocAuthLog::DocAuthFunnelSummaryStats.new.call - end - save_report(REPORT_NAME, report.to_json, extension: 'json') - end - end -end diff --git a/app/jobs/reports/iaa_billing_report.rb b/app/jobs/reports/iaa_billing_report.rb deleted file mode 100644 index 808aab32de4..00000000000 --- a/app/jobs/reports/iaa_billing_report.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'identity/hostdata' - -module Reports - class IaaBillingReport < BaseReport - REPORT_NAME = 'iaa-billing-report'.freeze - - def perform(_today) - @sps_for_iaa = {} - @today = Time.zone.today - results = [] - transaction_with_timeout do - unique_iaa_sps.each do |sp| - iaa_results = active_ial2_counts_for_iaa(sp) - auth_counts = auth_counts_for_iaa(sp.iaa) - iaa_results['auth_counts'] = auth_counts - results << iaa_results - end - end - save_report(REPORT_NAME, results.to_json, extension: 'json') - end - - private - - attr_accessor :today, :sps_for_iaa - - def auth_counts_for_iaa(iaa) - results = [] - sps_for_iaa[iaa].each do |sp| - results << sp_month_auth_counts(sp, 1) - results << sp_month_auth_counts(sp, 2) - end - results - end - - def sp_month_auth_counts(sp, ial) - count = Db::MonthlySpAuthCount::SpMonthTotalAuthCounts.call(today, sp.issuer, ial) - { issuer: sp.issuer, ial: ial, count: count }.stringify_keys - end - - def active_ial2_counts_for_iaa(sp) - count = Db::Identity::IaaActiveUserCount.new( - sp.iaa, - sp.iaa_start_date, - sp.iaa_end_date, - ).call(2, today) - { iaa: sp.iaa, - iaa_start_date: sp.iaa_start_date.strftime('%Y-%m-%d'), - iaa_end_date: sp.iaa_end_date.strftime('%Y-%m-%d'), - ial2_active_count: count }.stringify_keys - end - - def unique_iaa_sps - iaa_done = {} - sps = [] - ServiceProvider.where.not( - iaa: nil, - ).where.not( - iaa_start_date: nil, - ).where.not( - iaa_end_date: nil, - ).sort_by(&:issuer).each do |sp| - iaa = sp.iaa - (sps_for_iaa[iaa] ||= []) << sp - next if iaa_done[iaa] - sps << sp - iaa_done[iaa] = true - end - sps - end - end -end diff --git a/app/jobs/reports/omb_fitara_report.rb b/app/jobs/reports/omb_fitara_report.rb deleted file mode 100644 index 93f06eca58b..00000000000 --- a/app/jobs/reports/omb_fitara_report.rb +++ /dev/null @@ -1,64 +0,0 @@ -require 'identity/hostdata' - -module Reports - class OmbFitaraReport < BaseReport - OLDEST_TIMESTAMP = '2016-01-01 00:00:00'.freeze - MOST_RECENT_MONTHS_COUNT = 2 - REPORT_NAME = 'omb-fitara-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - results = transaction_with_timeout do - report_hash - end - save_report(REPORT_NAME, results.to_json, extension: 'json') - end - - private - - def report_hash - month, year = current_month - counts = [] - MOST_RECENT_MONTHS_COUNT.times do - counts << { month: "#{year}#{format('%02d', month)}", count: count_for_month(month, year) } - month, year = previous_month(month, year) - end - { counts: counts } - end - - def count_for_month(month, year) - month, year = next_month(month, year) - finish = "#{year}-#{month}-01 00:00:00" - Funnel::Registration::RangeRegisteredCount.call(OLDEST_TIMESTAMP, finish) - end - - def current_month - today = Time.zone.today - [today.strftime('%m').to_i, today.strftime('%Y').to_i] - end - - def next_month(month, year) - month += 1 - if month > 12 - month = 1 - year += 1 - end - [month, year] - end - - def previous_month(month, year) - month -= 1 - if month.zero? - month = 12 - year -= 1 - end - [month, year] - end - end -end diff --git a/app/jobs/reports/sp_active_users_over_period_of_performance_report.rb b/app/jobs/reports/sp_active_users_over_period_of_performance_report.rb deleted file mode 100644 index dc3294034b8..00000000000 --- a/app/jobs/reports/sp_active_users_over_period_of_performance_report.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'identity/hostdata' - -module Reports - class SpActiveUsersOverPeriodOfPerformanceReport < BaseReport - REPORT_NAME = 'sp-active-users-over-period-of-performance-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - results = transaction_with_timeout do - Db::Identity::SpActiveUserCountsWithinIaaWindow.call - end - save_report(REPORT_NAME, results.to_json, extension: 'json') - end - end -end diff --git a/app/jobs/reports/sp_cost_report.rb b/app/jobs/reports/sp_cost_report.rb deleted file mode 100644 index 63d906e786a..00000000000 --- a/app/jobs/reports/sp_cost_report.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'identity/hostdata' - -module Reports - class SpCostReport < BaseReport - REPORT_NAME = 'sp-cost-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - results = transaction_with_timeout do - Db::SpCost::SpCostSummary.call(first_of_this_month, end_of_today) - end - save_report(REPORT_NAME, results.to_json, extension: 'json') - end - end -end diff --git a/app/jobs/reports/total_sp_cost_report.rb b/app/jobs/reports/total_sp_cost_report.rb deleted file mode 100644 index dc171ce7529..00000000000 --- a/app/jobs/reports/total_sp_cost_report.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'identity/hostdata' - -module Reports - class TotalSpCostReport < BaseReport - REPORT_NAME = 'total-sp-cost-report'.freeze - - include GoodJob::ActiveJobExtensions::Concurrency - - good_job_control_concurrency_with( - total_limit: 1, - key: -> { "#{REPORT_NAME}-#{arguments.first}" }, - ) - - def perform(_date) - auth_counts = transaction_with_timeout do - Db::SpCost::TotalSpCostSummary.call(first_of_this_month, end_of_today) - end - save_report(REPORT_NAME, auth_counts.to_json, extension: 'json') - end - end -end diff --git a/app/jobs/reports/unique_monthly_auths_report.rb b/app/jobs/reports/unique_monthly_auths_report.rb deleted file mode 100644 index 058c2d80a96..00000000000 --- a/app/jobs/reports/unique_monthly_auths_report.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'identity/hostdata' - -module Reports - class UniqueMonthlyAuthsReport < BaseReport - REPORT_NAME = 'unique-monthly-auths-report'.freeze - - def perform(_date) - auth_counts = transaction_with_timeout do - Db::MonthlySpAuthCount::UniqueMonthlyAuthCounts.call - end - save_report(REPORT_NAME, auth_counts.to_json, extension: 'json') - end - end -end diff --git a/app/jobs/reports/unique_yearly_auths_report.rb b/app/jobs/reports/unique_yearly_auths_report.rb deleted file mode 100644 index f44046d22dd..00000000000 --- a/app/jobs/reports/unique_yearly_auths_report.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'identity/hostdata' - -module Reports - class UniqueYearlyAuthsReport < BaseReport - REPORT_NAME = 'unique-yearly-auths-report'.freeze - - def perform(_date) - auth_counts = transaction_with_timeout do - Db::MonthlySpAuthCount::UniqueYearlyAuthCounts.call - end - save_report(REPORT_NAME, auth_counts.to_json, extension: 'json') - end - end -end diff --git a/app/models/doc_auth_log.rb b/app/models/doc_auth_log.rb index 032b9c09e8f..b79991975bc 100644 --- a/app/models/doc_auth_log.rb +++ b/app/models/doc_auth_log.rb @@ -6,8 +6,4 @@ class DocAuthLog < ApplicationRecord foreign_key: 'issuer', primary_key: 'issuer' # rubocop:enable Rails/InverseOf - - def self.verified_users_count - Profile.where.not(verified_at: nil).count - end end diff --git a/app/models/event.rb b/app/models/event.rb index 3b7e9a05763..43e2e22ca89 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -2,8 +2,6 @@ class Event < ApplicationRecord belongs_to :user belongs_to :device - attr_accessor :disavowal_token - enum event_type: { account_created: 1, phone_confirmed: 2, diff --git a/app/models/monthly_sp_auth_count.rb b/app/models/monthly_sp_auth_count.rb deleted file mode 100644 index b84a86608c3..00000000000 --- a/app/models/monthly_sp_auth_count.rb +++ /dev/null @@ -1,29 +0,0 @@ -class MonthlySpAuthCount < ApplicationRecord - # rubocop:disable Rails/InverseOf - belongs_to :user - belongs_to :service_provider, - foreign_key: 'issuer', - primary_key: 'issuer' - # rubocop:enable Rails/InverseOf - - def self.increment(user_id:, service_provider:, ial:) - # The following sql offers superior db performance with one write and no locking overhead - sql = <<~SQL - INSERT INTO monthly_sp_auth_counts (issuer, ial, year_month, user_id, auth_count) - VALUES (?, ?, ?, ?, 1) - ON CONFLICT (issuer, ial, year_month, user_id) DO UPDATE - SET auth_count = monthly_sp_auth_counts.auth_count + 1 - SQL - year_month = Time.zone.today.strftime('%Y%m') - current_user = User.find_by(id: user_id) - ial_context = IalContext.new(ial: ial, service_provider: service_provider, user: current_user) - query = sanitize_sql_array( - [sql, - service_provider&.issuer.to_s, - ial_context.bill_for_ial_1_or_2, - year_month, - user_id], - ) - MonthlySpAuthCount.connection.execute(query) - end -end diff --git a/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_all_time.rb b/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_all_time.rb deleted file mode 100644 index 25d3bde407f..00000000000 --- a/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_all_time.rb +++ /dev/null @@ -1,25 +0,0 @@ -module Db - module DocAuthLog - class BlanketDropOffRatesAllSpsAllTime - include DropOffRatesHelper - - def call(title) - drop_off_rates(title: title) - end - - private - - def verified_user_counts_query - <<~SQL - #{select_count_from_profiles_where_verified_and_active} - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_in_range.rb b/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_in_range.rb deleted file mode 100644 index 7ad6eece1ff..00000000000 --- a/app/services/db/doc_auth_log/blanket_drop_off_rates_all_sps_in_range.rb +++ /dev/null @@ -1,26 +0,0 @@ -module Db - module DocAuthLog - class BlanketDropOffRatesAllSpsInRange - include DropOffRatesHelper - - def call(title, start, finish) - drop_off_rates(title: title, start: start, finish: finish) - end - - private - - def verified_user_counts_query - <<~SQL - #{select_count_from_profiles_where_verified_and_active} - and user_id in (select user_id from doc_auth_logs where #{start} <= welcome_view_at and welcome_view_at < #{finish}) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} where #{start} <= welcome_view_at and welcome_view_at < #{finish} - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_all_time.rb b/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_all_time.rb deleted file mode 100644 index 69b15bc4351..00000000000 --- a/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_all_time.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Db - module DocAuthLog - class BlanketDropOffRatesPerSpAllTime - include DropOffRatesHelper - - def call(title, issuer) - drop_off_rates(title: title, issuer: issuer, start: oldest_ial2_date, finish: Date.tomorrow) - end - - private - - def verified_user_counts_query - <<~SQL - select count(*) from identities - where ial>=2 and service_provider = '#{@issuer}' and - user_id in (select user_id from profiles) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} where issuer='#{@issuer}' - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_in_range.rb b/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_in_range.rb deleted file mode 100644 index 4d7b2898d18..00000000000 --- a/app/services/db/doc_auth_log/blanket_drop_off_rates_per_sp_in_range.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Db - module DocAuthLog - class BlanketDropOffRatesPerSpInRange - include DropOffRatesHelper - - def call(title, issuer, start, finish) - drop_off_rates(title: title, issuer: issuer, start: start, finish: finish) - end - - private - - def verified_user_counts_query - <<~SQL - select count(*) from identities - where ial>=2 and service_provider = '#{@issuer}' and #{start} <= created_at and created_at < #{finish} - and user_id in (select user_id from profiles) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} where #{start} <= welcome_view_at and welcome_view_at < #{finish} and issuer='#{@issuer}' - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/doc_auth_funnel_summary_stats.rb b/app/services/db/doc_auth_log/doc_auth_funnel_summary_stats.rb deleted file mode 100644 index b6f55ca5049..00000000000 --- a/app/services/db/doc_auth_log/doc_auth_funnel_summary_stats.rb +++ /dev/null @@ -1,59 +0,0 @@ -module Db - module DocAuthLog - class DocAuthFunnelSummaryStats - SKIP_FIELDS = - %w[id user_id created_at updated_at no_sp_session_started_at issuer - last_document_error state aamva].freeze - - def call - total_count = ::DocAuthLog.count - return {} if total_count.zero? - convert_percentages(total_count) - results['total_verified_users_count'] = ::DocAuthLog.verified_users_count - results['total_verify_attempted_users_count'] = total_count - results - end - - private - - def convert_percentages(total_count) - results.each do |key, value| - multiplier = key.end_with?('_percent') ? 100.0 : 1.0 - results[key] = (value * multiplier / total_count).round(2) - end - end - - def results - @results ||= execute_funnel_sql - end - - def execute_funnel_sql - sep = '' - ::DocAuthLog.new.attributes.keys.each do |attribute| - next unless append_sql(sql_a, attribute, sep) - sep = ',' - end - sql_a << ' FROM doc_auth_logs' - ActiveRecord::Base.connection.execute(sql_a.join)[0] - end - - def append_sql(sql_a, attribute, sep) - return if SKIP_FIELDS.index(attribute) - sql_a << aggregate_sql(attribute, sep) - true - end - - def sql_a - @sql_a ||= ['SELECT '] - end - - def aggregate_sql(attribute, sep) - if attribute.end_with?('_at') - "#{sep}count(#{attribute}) AS #{attribute[0..-3]}percent" - else - "#{sep}sum(#{attribute}) AS #{attribute}_average" - end - end - end - end -end diff --git a/app/services/db/doc_auth_log/drop_off_rates_helper.rb b/app/services/db/doc_auth_log/drop_off_rates_helper.rb deleted file mode 100644 index d57daac0321..00000000000 --- a/app/services/db/doc_auth_log/drop_off_rates_helper.rb +++ /dev/null @@ -1,146 +0,0 @@ -module Db - module DocAuthLog - module DropOffRatesHelper - STEPS = %w[ - welcome - agreement - capture_document - cap_doc_submit - ssn - verify_info - verify_submit - phone - encrypt - personal_key - verified - ].freeze - - private - - attr_reader :start, :finish, :issuer, :results - - def drop_off_rates(title:, issuer: nil, start: nil, finish: nil) - @title = title - @issuer = issuer - @start = ActiveRecord::Base.connection.quote(start) if start - @finish = ActiveRecord::Base.connection.quote(finish) if finish - generate_report - end - - def select_count_from_profiles_where_verified_and_active - <<~SQL - select count(*) - from profiles - where verified_at is not null and active=true - SQL - end - - def select_counts_from_doc_auth_logs - <<~SQL - select count(welcome_view_at) as welcome, count(agreement_view_at) as agreement, count(upload_view_at) as upload_option, - count(COALESCE(back_image_view_at,mobile_back_image_view_at,capture_mobile_back_image_view_at,present_cac_view_at,document_capture_view_at)) as capture_document, - count(COALESCE(case when document_capture_submit_count>0 then 1 else null end, - case when back_image_submit_count>0 then 1 else null end, - case when capture_mobile_back_image_submit_count>0 then 1 else null end, - case when mobile_back_image_submit_count>0 then 1 else null end)) as cap_doc_submit, - count(COALESCE(ssn_view_at,enter_info_view_at)) as ssn, - count(verify_view_at) as verify_info, - count(COALESCE(case when verify_submit_count>0 then 1 else null end)) as verify_submit, - count(verify_phone_view_at) as phone, - count(encrypt_view_at) as encrypt, - count(verified_view_at) as personal_key - from doc_auth_logs - SQL - end - - def images_submitted - predicates = [ - 'back_image_submit_count>0', - 'mobile_back_image_submit_count>0', - 'capture_mobile_back_image_submit_count>0', - ].join(' or ') - - "(#{predicates})" - end - - def images_or_piv_cac_submitted - "(#{images_submitted} OR #{piv_cac_submitted})" - end - - def piv_cac_submitted - predicates = [ - 'present_cac_submit_count>0', - ].join(' or ') - - "(#{predicates})" - end - - def oldest_ial2_date - '01/01/2019' - end - - def initialize_results - @results = [] - @results << @title + (issuer ? ", issuer: #{issuer}" : '') + "\n" - @results << "#{start} <= date user starts doc auth < #{finish}\n\n" if start || finish - end - - def generate_report - initialize_results - rates = drop_offs_in_range - verified_profiles = verified_profiles_in_range - rates['verified'] = verified_profiles[0]['count'] - results << format("%20s %6s %3s %3s\n", 'step', 'users', '%users', 'dropoff') - print_report(rates) - end - - def verified_profiles_in_range - ActiveRecord::Base.connection.execute(verified_user_counts_query) - end - - def drop_offs_in_range - rates = ActiveRecord::Base.connection.execute(drop_offs_query) - rates[0] - end - - def verified_profiles_count_for_issuer - query = <<~SQL - select count(*) from identities where service_provider = '#{issuer}' and - user_id in (select user_id from profiles) - SQL - ActiveRecord::Base.connection.execute(query) - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - where #{start} <= welcome_view_at and welcome_view_at < #{finish} and issuer='#{issuer}' - SQL - end - - def print_report(rates) - STEPS.each_with_index do |step, index| - percent_left = calc_percent_left(rates, step, STEPS[0]) - dropoff = calc_dropoff(rates, STEPS[index + 1], step) - results << format("%20s %6d %5d%% %6d%%\n", step, rates[step], percent_left, dropoff) - end - results << "\n\n" - results.join - end - - def calc_percent_left(rec, step, total) - return 100 unless step && total - total = rec[total] - return 100 if total.zero? - ((rec[step] / total.to_f) * 100.0).round - end - - def calc_dropoff(rec, step, total) - return 0 unless step && total - total = rec[total] - return 0 if total.zero? - ((1 - (rec[step] / total.to_f)) * 100.0).round - end - end - end -end diff --git a/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_all_time.rb b/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_all_time.rb deleted file mode 100644 index 98e544abbcb..00000000000 --- a/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_all_time.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Db - module DocAuthLog - class OverallDropOffRatesAllSpsAllTime - include DropOffRatesHelper - - def call(title) - drop_off_rates(title: title) - end - - private - - def verified_user_counts_query - <<~SQL - #{select_count_from_profiles_where_verified_and_active} - and user_id in (select user_id from doc_auth_logs where #{images_or_piv_cac_submitted}) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - where #{images_or_piv_cac_submitted} - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_in_range.rb b/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_in_range.rb deleted file mode 100644 index 7ecdefd4cf6..00000000000 --- a/app/services/db/doc_auth_log/overall_drop_off_rates_all_sps_in_range.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Db - module DocAuthLog - class OverallDropOffRatesAllSpsInRange - include DropOffRatesHelper - - def call(title, start, finish) - drop_off_rates(title: title, start: start, finish: finish) - end - - private - - def verified_user_counts_query - <<~SQL - #{select_count_from_profiles_where_verified_and_active} - and user_id in (select user_id from doc_auth_logs where #{start} <= welcome_view_at and welcome_view_at < #{finish} and #{images_or_piv_cac_submitted}) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - where #{start} <= welcome_view_at and welcome_view_at < #{finish} and #{images_or_piv_cac_submitted} - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_all_time.rb b/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_all_time.rb deleted file mode 100644 index 119ed5f5d08..00000000000 --- a/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_all_time.rb +++ /dev/null @@ -1,29 +0,0 @@ -module Db - module DocAuthLog - class OverallDropOffRatesPerSpAllTime - include DropOffRatesHelper - - def call(title, issuer) - drop_off_rates(title: title, issuer: issuer) - end - - private - - def verified_user_counts_query - <<~SQL - select count(*) from identities - where service_provider='#{issuer}' and ial>=2 - and user_id in (select user_id from doc_auth_logs where #{images_or_piv_cac_submitted}) - and user_id in (select user_id from profiles) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - where issuer='#{issuer}' and #{images_or_piv_cac_submitted} - SQL - end - end - end -end diff --git a/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_in_range.rb b/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_in_range.rb deleted file mode 100644 index c549dc618b6..00000000000 --- a/app/services/db/doc_auth_log/overall_drop_off_rates_per_sp_in_range.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Db - module DocAuthLog - class OverallDropOffRatesPerSpInRange - include DropOffRatesHelper - - def call(title, issuer, start, finish) - drop_off_rates(title: title, issuer: issuer, start: start, finish: finish) - end - - private - - def verified_user_counts_query - <<~SQL - select count(*) from identities - where service_provider='#{issuer}' and ial>=2 - and user_id in (select user_id from doc_auth_logs where #{start} <= welcome_view_at and welcome_view_at < #{finish} and #{images_or_piv_cac_submitted}) - and user_id in (select user_id from profiles) - SQL - end - - def drop_offs_query - <<~SQL - #{select_counts_from_doc_auth_logs} - where issuer='#{issuer}' and #{start} <= welcome_view_at and welcome_view_at < #{finish} - and ((#{images_or_piv_cac_submitted}) OR (#{piv_cac_submitted})) - SQL - end - end - end -end diff --git a/app/services/db/identity/iaa_active_user_count.rb b/app/services/db/identity/iaa_active_user_count.rb deleted file mode 100644 index 1a573f984d2..00000000000 --- a/app/services/db/identity/iaa_active_user_count.rb +++ /dev/null @@ -1,85 +0,0 @@ -# generate incremental IAL2 active user counts by IAA (rolling up applications) -# incremental means a user can only be active for one month in the entire span of the IAA -# -# strategy: get active users in the current month in identities and -# subtract out those active in prior months from monthly_sp_auth_counts -# -# for each IAA: -# SELECT COUNT(*) as active_count FROM identities -# WHERE service_provider in (issuer1, issuer2, ...) -# AND last_ial2_authenticated_at >= first_of_the_month -# AND user_id IN (select user_id from profiles) -# AND user_id NOT IN ( -# SELECT DISTINCT user_id -# FROM monthly_sp_auth_counts -# WHERE issuer IN (issuer1, issuer2, ...) AND ial=2 -# AND year_month BETWEEN iaa_start_year_month AND last_month_year_month - -module Db - module Identity - class IaaActiveUserCount - def initialize(iaa, iaa_start_date, iaa_end_date) - @iaa = iaa - @iaa_start_date = iaa_start_date - @iaa_end_date = iaa_end_date - end - - def call(ial, today) - issuers = issuers_sql(iaa) - return unless issuers - sql = format(<<~SQL, sql_params(today, issuers, ial)) - SELECT COUNT(*) as active_count FROM identities - WHERE service_provider in %{issuers} - AND last_ial%{ial}_authenticated_at >= %{beginning_of_month} and - user_id IN (select user_id from profiles) - %{prior_months_sql} - SQL - ActiveRecord::Base.connection.execute(sql)[0]['active_count'] - end - - private - - attr_reader :iaa, :iaa_start_date, :iaa_end_date - - def from_monthly_sp_counts(issuers, ial, prior_months) - <<~SQL - AND user_id NOT IN ( - SELECT DISTINCT user_id FROM monthly_sp_auth_counts WHERE issuer IN #{issuers} and ial=#{ial} - #{prior_months} - ) - SQL - end - - def sql_params(today, issuers, ial) - { - beginning_of_month: ActiveRecord::Base.connection.quote(today.beginning_of_month), - ial: ial, - prior_months_sql: prior_months_sql(ial, today, issuers), - issuers: issuers, - } - end - - def issuers_sql(iaa) - sps = ServiceProvider.where(iaa: iaa) - return if sps.blank? - "(#{sps.map { |sp| "'#{sp.issuer}'" }.join(',')})" - end - - def prior_months_sql(ial, today, issuers) - last_month = today.last_month - return '' if last_month < iaa_start_date - <<~SQL - AND user_id NOT IN ( - SELECT DISTINCT user_id - FROM monthly_sp_auth_counts WHERE issuer IN #{issuers} and ial=#{ial} - AND year_month BETWEEN '#{year_month(iaa_start_date)}' AND '#{year_month(last_month)}' - ) - SQL - end - - def year_month(date) - date.strftime('%Y%m') - end - end - end -end diff --git a/app/services/db/identity/sp_active_user_counts_within_iaa_window.rb b/app/services/db/identity/sp_active_user_counts_within_iaa_window.rb deleted file mode 100644 index ec00be51f01..00000000000 --- a/app/services/db/identity/sp_active_user_counts_within_iaa_window.rb +++ /dev/null @@ -1,40 +0,0 @@ -module Db - module Identity - # Similar to SpActiveUserCounts, but it limits dates to within active IAA windows - class SpActiveUserCountsWithinIaaWindow - def self.call - sql = <<~SQL - SELECT - service_providers.issuer - , MAX(service_providers.app_id) AS app_id - , MAX(service_providers.iaa) AS iaa - , MIN(service_providers.iaa_start_date) AS iaa_start_date - , MAX(service_providers.iaa_end_date) AS iaa_end_date - , SUM( - CASE - WHEN identities.last_ial1_authenticated_at >= service_providers.iaa_start_date THEN 1 - ELSE 0 - END - ) AS total_ial1_active - , SUM( - CASE - WHEN identities.last_ial2_authenticated_at >= service_providers.iaa_start_date THEN 1 - ELSE 0 - END - ) AS total_ial2_active - FROM service_providers - INNER JOIN identities ON identities.service_provider = service_providers.issuer - WHERE - ( - identities.last_ial1_authenticated_at >= service_providers.iaa_start_date - OR identities.last_ial2_authenticated_at >= service_providers.iaa_start_date - ) - GROUP BY service_providers.issuer - ORDER BY service_providers.issuer ASC - SQL - - ActiveRecord::Base.connection.execute(sql) - end - end - end -end diff --git a/app/services/db/monthly_sp_auth_count/sp_month_total_auth_counts.rb b/app/services/db/monthly_sp_auth_count/sp_month_total_auth_counts.rb deleted file mode 100644 index 41a35714575..00000000000 --- a/app/services/db/monthly_sp_auth_count/sp_month_total_auth_counts.rb +++ /dev/null @@ -1,15 +0,0 @@ -module Db - module MonthlySpAuthCount - class SpMonthTotalAuthCounts - def self.call(today, issuer, ial) - sql = <<~SQL - SELECT SUM(auth_count) AS total - FROM monthly_sp_auth_counts - WHERE issuer = '#{issuer}' AND ial=#{ial} AND year_month='#{today.strftime('%Y%m')}' - SQL - results = ActiveRecord::Base.connection.execute(sql) - results.count.positive? ? results[0]['total'] || 0 : 0 - end - end - end -end diff --git a/app/services/db/monthly_sp_auth_count/unique_monthly_auth_counts.rb b/app/services/db/monthly_sp_auth_count/unique_monthly_auth_counts.rb deleted file mode 100644 index 4a42588dd2c..00000000000 --- a/app/services/db/monthly_sp_auth_count/unique_monthly_auth_counts.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Db - module MonthlySpAuthCount - class UniqueMonthlyAuthCounts - def self.call - sql = <<~SQL - SELECT monthly_sp_auth_counts.issuer,year_month,MAX(app_id) AS app_id, - COUNT(*) AS total - FROM monthly_sp_auth_counts,service_providers - WHERE monthly_sp_auth_counts.issuer = service_providers.issuer - GROUP BY monthly_sp_auth_counts.issuer, monthly_sp_auth_counts.ial, year_month - ORDER BY monthly_sp_auth_counts.issuer, monthly_sp_auth_counts.ial, year_month - SQL - ActiveRecord::Base.connection.execute(sql) - end - end - end -end diff --git a/app/services/db/monthly_sp_auth_count/unique_yearly_auth_counts.rb b/app/services/db/monthly_sp_auth_count/unique_yearly_auth_counts.rb deleted file mode 100644 index c3a4edc4a44..00000000000 --- a/app/services/db/monthly_sp_auth_count/unique_yearly_auth_counts.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Db - module MonthlySpAuthCount - class UniqueYearlyAuthCounts - def self.call - sql = <<~SQL - SELECT issuer, MAX(app_id) AS app_id, year, COUNT(*) AS total - FROM ( - SELECT monthly_sp_auth_counts.issuer, MAX(app_id) AS app_id, user_id, - monthly_sp_auth_counts.ial, LEFT(year_month, 4) AS year, COUNT(*) - FROM monthly_sp_auth_counts, service_providers - WHERE monthly_sp_auth_counts.issuer = service_providers.issuer - GROUP BY monthly_sp_auth_counts.issuer, monthly_sp_auth_counts.ial, - year, user_id) AS tbl - GROUP BY issuer, ial, year - SQL - ActiveRecord::Base.connection.execute(sql) - end - end - end -end diff --git a/app/services/db/sp_cost/sp_cost_summary.rb b/app/services/db/sp_cost/sp_cost_summary.rb deleted file mode 100644 index a5bd73b4799..00000000000 --- a/app/services/db/sp_cost/sp_cost_summary.rb +++ /dev/null @@ -1,21 +0,0 @@ -module Db - module SpCost - class SpCostSummary - def self.call(start, finish) - params = { - start: ActiveRecord::Base.connection.quote(start), - finish: ActiveRecord::Base.connection.quote(finish), - } - - sql = format(<<~SQL, params) - SELECT sp_costs.issuer,sp_costs.ial,cost_type,MAX(app_id) AS app_id,COUNT(*) - FROM sp_costs, service_providers - WHERE %{start} <= sp_costs.created_at and sp_costs.created_at <= %{finish} AND - sp_costs.issuer = service_providers.issuer - GROUP BY sp_costs.issuer,sp_costs.ial,cost_type ORDER BY issuer,ial,cost_type - SQL - ActiveRecord::Base.connection.execute(sql) - end - end - end -end diff --git a/app/services/db/sp_cost/total_sp_cost_summary.rb b/app/services/db/sp_cost/total_sp_cost_summary.rb deleted file mode 100644 index 92dd418596b..00000000000 --- a/app/services/db/sp_cost/total_sp_cost_summary.rb +++ /dev/null @@ -1,20 +0,0 @@ -module Db - module SpCost - class TotalSpCostSummary - def self.call(start, finish) - params = { - start: ActiveRecord::Base.connection.quote(start), - finish: ActiveRecord::Base.connection.quote(finish), - } - - sql = format(<<~SQL, params) - SELECT cost_type,COUNT(*) - FROM sp_costs - WHERE %{start} <= created_at and created_at <= %{finish} - GROUP BY cost_type ORDER BY cost_type - SQL - ActiveRecord::Base.connection.execute(sql) - end - end - end -end diff --git a/app/services/flow/flow_state_machine.rb b/app/services/flow/flow_state_machine.rb index cc0e06f7c14..ab86cadd60d 100644 --- a/app/services/flow/flow_state_machine.rb +++ b/app/services/flow/flow_state_machine.rb @@ -176,6 +176,9 @@ def analytics_properties step: current_step, step_count: current_flow_step_counts[current_step_name], analytics_id: @analytics_id, + irs_reproofing: effective_user&.decorate&.reproof_for_irs?( + service_provider: current_sp, + ).present?, } end diff --git a/app/services/funnel/registration/range_registered_count.rb b/app/services/funnel/registration/range_registered_count.rb deleted file mode 100644 index 8336235ecbf..00000000000 --- a/app/services/funnel/registration/range_registered_count.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Funnel - module Registration - class RangeRegisteredCount - def self.call(start, finish) - RegistrationLog.where('? < registered_at AND registered_at < ?', start, finish).count - end - end - end -end diff --git a/app/services/irs_attempts_api/envelope_encryptor.rb b/app/services/irs_attempts_api/envelope_encryptor.rb index d46790ac776..2395c22ec39 100644 --- a/app/services/irs_attempts_api/envelope_encryptor.rb +++ b/app/services/irs_attempts_api/envelope_encryptor.rb @@ -1,3 +1,5 @@ +require 'base16' + module IrsAttemptsApi class EnvelopeEncryptor Result = Struct.new(:filename, :iv, :encrypted_key, :encrypted_data, keyword_init: true) @@ -6,23 +8,24 @@ class EnvelopeEncryptor # provided so that only the owner of the private key may decrypt this data. def self.encrypt(data:, timestamp:, public_key:) compressed_data = Zlib.gzip(data) - cipher = OpenSSL::Cipher.new('aes-128-cbc') + cipher = OpenSSL::Cipher.new('aes-256-cbc') cipher.encrypt key = cipher.random_key iv = cipher.random_iv encrypted_data = cipher.update(compressed_data) + cipher.final - digest = Digest::SHA256.hexdigest(encrypted_data) + encoded_data = Base16.encode16(encrypted_data) + digest = Digest::SHA256.hexdigest(encoded_data) encrypted_key = public_key.public_encrypt(key) formatted_time = formatted_timestamp(timestamp) filename = - "FCI-#{IdentityConfig.store.irs_attempt_api_csp_id}_#{formatted_time}_#{digest}.json.gz" + "FCI-Logingov_#{formatted_time}_#{digest}.dat.gz.hex" Result.new( filename: filename, iv: iv, encrypted_key: encrypted_key, - encrypted_data: encrypted_data, + encrypted_data: encoded_data, ) end @@ -31,11 +34,11 @@ def self.formatted_timestamp(timestamp) end def self.decrypt(encrypted_data:, key:, iv:) - cipher = OpenSSL::Cipher.new('aes-128-cbc') + cipher = OpenSSL::Cipher.new('aes-256-cbc') cipher.decrypt cipher.key = key cipher.iv = iv - decrypted = cipher.update(encrypted_data) + cipher.final + decrypted = cipher.update(Base16.decode16(encrypted_data)) + cipher.final Zlib.gunzip(decrypted) end diff --git a/app/services/proofing/lexis_nexis/verification_error_parser.rb b/app/services/proofing/lexis_nexis/verification_error_parser.rb index 665c4f5cee9..25e40c09827 100644 --- a/app/services/proofing/lexis_nexis/verification_error_parser.rb +++ b/app/services/proofing/lexis_nexis/verification_error_parser.rb @@ -47,11 +47,10 @@ def parse_product_error_messages return {} if products.nil? products.each_with_object({}) do |product, error_messages| - if product['ProductType'] == 'InstantVerify' - next if product['ProductStatus'] == 'pass' - elsif product['ProductStatus'] == 'pass' - next - end + next if product['ProductStatus'] == 'pass' + + # don't log PhoneFinder reflected PII + product.delete('ParameterDetails') if product['ProductType'] == 'PhoneFinder' key = product.fetch('ExecutedStepName').to_sym error_messages[key] = product diff --git a/app/services/user_event_creator.rb b/app/services/user_event_creator.rb index 4a015861f68..c51e80f4973 100644 --- a/app/services/user_event_creator.rb +++ b/app/services/user_event_creator.rb @@ -8,6 +8,7 @@ def initialize(current_user:, request: nil) @current_user = current_user end + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_user_event(event_type, user = current_user, disavowal_token = nil) return unless user&.id existing_device = Device.find_by(user_id: user.id, cookie_uuid: cookies[:device]) @@ -25,10 +26,12 @@ def create_user_event(event_type, user = current_user, disavowal_token = nil) end # Create an event without a device or IP address + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_out_of_band_user_event(event_type) create_event_for_device(event_type: event_type, user: current_user, device: nil) end + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_user_event_with_disavowal(event_type, user = current_user, device = nil) disavowal_token = SecureRandom.urlsafe_base64(32) if device @@ -43,28 +46,32 @@ def create_user_event_with_disavowal(event_type, user = current_user, device = n private + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_event_for_existing_device(event_type:, user:, device:, disavowal_token:) device.update_last_used_ip(request.remote_ip) create_event_for_device( - event_type: event_type, user: user, device: device, - disavowal_token: disavowal_token + event_type: event_type, + user: user, + device: device, + disavowal_token: disavowal_token, ) end + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_event_for_new_device(event_type:, user:, disavowal_token:) user_has_multiple_devices = UserDecorator.new(user).devices? device = create_device_for_user(user) if user_has_multiple_devices && disavowal_token.nil? - event = create_user_event_with_disavowal( + event, disavowal_token = create_user_event_with_disavowal( event_type, user, device ) send_new_device_notification( user: user, device: device, - disavowal_token: event.disavowal_token, + disavowal_token: disavowal_token, ) - event + [event, disavowal_token] else create_event_for_device( device: device, @@ -97,17 +104,20 @@ def send_new_device_notification(user:, device:, disavowal_token:) UserAlerts::AlertUserAboutNewDevice.call(user, device, disavowal_token) end + # @return [Array(Event, String)] an (event, disavowal_token) tuple def create_event_for_device(event_type:, user:, device:, disavowal_token: nil) disavowal_token_fingerprint = if disavowal_token Pii::Fingerprinter.fingerprint(disavowal_token) end event = Event.create( - user_id: user.id, device_id: device&.id, ip: request&.remote_ip, event_type: event_type, - disavowal_token_fingerprint: disavowal_token_fingerprint + user_id: user.id, + device_id: device&.id, + ip: request&.remote_ip, + event_type: event_type, + disavowal_token_fingerprint: disavowal_token_fingerprint, ) - event.disavowal_token = disavowal_token - event + [event, disavowal_token] end def cookies diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb index abbc687ab36..c23be3588e6 100644 --- a/app/views/devise/passwords/edit.html.erb +++ b/app/views/devise/passwords/edit.html.erb @@ -14,11 +14,13 @@ <%= f.full_error :reset_password_token %> <%= render PasswordToggleComponent.new( form: f, - label: t('forms.passwords.edit.labels.password'), - required: true, + field_options: { + label: t('forms.passwords.edit.labels.password'), + required: true, + }, ) %> <%= render 'devise/shared/password_strength', forbidden_passwords: @forbidden_passwords %> - <%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'margin-bottom-4' %> + <%= f.submit t('forms.passwords.edit.buttons.submit'), class: 'display-block margin-y-5' %> <% end %> <%= render 'shared/password_accordion' %> diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 982f1da6439..58c30e4abef 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -35,8 +35,8 @@ ) %> <%= render PasswordToggleComponent.new( form: f, - required: true, - wrapper_html: { class: 'margin-top-6' }, + class: 'margin-bottom-4', + field_options: { required: true }, ) %> <%= f.input :request_id, as: :hidden, input_html: { value: @request_id } %>
diff --git a/app/views/devise/shared/_password_strength.html.erb b/app/views/devise/shared/_password_strength.html.erb index 596550ada73..1003611b6b1 100644 --- a/app/views/devise/shared/_password_strength.html.erb +++ b/app/views/devise/shared/_password_strength.html.erb @@ -1,5 +1,5 @@