Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
91f10b1
LG-15396 Log the initiating SP on verify-by-mail code entry (#11713)
jmhooper Jan 7, 2025
a46faf6
LG-15187: Update Socure shadow mode A/B test logic (part 2/2) (#11694)
matthinz Jan 7, 2025
7b06970
Drop proofing_components table (#11711)
matthinz Jan 7, 2025
0b303ab
LG-15261 | Update AAMVA state maintenance windows (#11705)
n1zyy Jan 8, 2025
5061c4d
Bump phonelib from 0.9.1 to 0.10.3 (#11720)
dependabot[bot] Jan 8, 2025
9495fe9
Bump libphonenumber-js from 1.11.4 to 1.11.17 (#11721)
dependabot[bot] Jan 8, 2025
e2b9fe6
changelog: Internal, CI, Pin Alpine image to 3.20 (#11723)
dskgsa Jan 8, 2025
94fb705
LG-15051: Remove fallback to LexisNexis (#11718)
matthinz Jan 8, 2025
ae56c01
Add script to build narratives from event logs (#11691)
matthinz Jan 8, 2025
a125f46
Remove outdated package.json version resolutions (#11722)
aduth Jan 8, 2025
ec96a27
LG-15261 | Trivial tweaks to AAMVA maintenance window file (#11725)
n1zyy Jan 8, 2025
79cc2e9
changelog: User-Facing Improvements, Select email, update content to …
mdiarra3 Jan 9, 2025
d574623
Yamada/lg 15068 content updates on ready to verify view and email for…
gina-yamada Jan 10, 2025
171fa56
Revert "Update newrelic_rpm gem (#11699)" (#11730)
Jan 10, 2025
ed9bdcf
Don't require openssl@1.1 in Brewfile (#11731)
n1zyy Jan 10, 2025
b4ade25
Add Document Encryption Service for Doc Escrow (#11714)
Sgtpluck Jan 10, 2025
a314cdb
changelog: User-Facing Improvements, document capture upload, disable…
AShukla-GSA Jan 10, 2025
c32366f
Fix analytics event documentation formatting (#11727)
zachmargolis Jan 11, 2025
355be33
Account for a space in the height returned by TrueID (#11737)
jmhooper Jan 13, 2025
57fe1eb
LG-15453: Fix issue formating heights for AAMVA DLDV (#11736)
matthinz Jan 13, 2025
23bcca6
LG 15241 Bug fix for recaptcha failure not incrementing rate limit fo…
kevinsmaster5 Jan 13, 2025
87eec6f
changelog: Bug Fixes, SAML Gem, Validates signature algorithm correct…
Sgtpluck Jan 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ brew 'postgresql@14'
brew 'redis'
brew 'node@22'
brew 'yarn'
brew 'openssl@1.1'
brew 'jq'
cask 'chromedriver'
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ gem 'rqrcode'
gem 'ruby-progressbar'
gem 'ruby-saml'
gem 'safe_target_blank', '>= 1.0.2'
gem 'saml_idp', github: '18F/saml_idp', tag: '0.23.4-18f'
gem 'saml_idp', github: '18F/saml_idp', tag: '0.23.5-18f'
gem 'scrypt'
gem 'simple_form', '>= 5.0.2'
gem 'stringex', require: false
Expand Down
10 changes: 5 additions & 5 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ GIT

GIT
remote: https://github.com/18F/saml_idp.git
revision: e5d876cf10ce9b39bba0cc523d06c4dda1af5124
tag: 0.23.4-18f
revision: bdf8e1f93707e413ecbd0f48d803e18812e19f90
tag: 0.23.5-18f
specs:
saml_idp (0.23.4.pre.18f)
saml_idp (0.23.5.pre.18f)
activesupport
builder
faraday
Expand Down Expand Up @@ -458,7 +458,7 @@ GEM
net-smtp (0.5.0)
net-protocol
net-ssh (6.1.0)
newrelic_rpm (9.16.1)
newrelic_rpm (9.7.0)
nio4r (2.7.4)
nokogiri (1.16.8)
mini_portile2 (~> 2.8.2)
Expand All @@ -477,7 +477,7 @@ GEM
pg (1.5.9)
pg_query (5.1.0)
google-protobuf (>= 3.22.3)
phonelib (0.9.1)
phonelib (0.10.3)
pkcs11 (0.3.4)
premailer (1.27.0)
addressable
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ public/api/_analytics-events.json: .yardoc .yardoc/objects/root.dat

.yardoc .yardoc/objects/root.dat: app/services/analytics_events.rb
bundle exec yard doc \
--no-progress \
--fail-on-warning \
--type-tag identity.idp.previous_event_name:"Previous Event Name" \
--no-output \
Expand Down
7 changes: 7 additions & 0 deletions app/controllers/concerns/idv/verify_info_concern.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

module Idv
# @attr idv_session [Idv::Session]
module VerifyInfoConcern
extend ActiveSupport::Concern

Expand Down Expand Up @@ -39,6 +40,12 @@ def shared_update
threatmetrix_session_id: idv_session.threatmetrix_session_id,
request_ip: request.remote_ip,
ipp_enrollment_in_progress: ipp_enrollment_in_progress?,
proofing_components: ProofingComponents.new(
user: current_user,
idv_session:,
session:,
user_session:,
),
)

return true
Expand Down
22 changes: 0 additions & 22 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,6 @@ def capture_analytics

if result.success? && saml_request.signed?
analytics_payload[:cert_error_details] = saml_request.cert_errors

# analytics to determine if turning on SHA256 validation will break
# existing partners
if certs_different?
analytics_payload[:certs_different] = true
analytics_payload[:sha256_matching_cert] = sha256_alg_matching_cert_serial
end
end

analytics.saml_auth(**analytics_payload)
Expand All @@ -168,21 +161,6 @@ def matching_cert_serial
nil
end

def sha256_alg_matching_cert
# if sha256_alg_matching_cert is nil, fallback to the "first" cert
saml_request.sha256_validation_matching_cert ||
saml_request_service_provider&.ssl_certs&.first
rescue SamlIdp::XMLSecurity::SignedDocument::ValidationError
end

def sha256_alg_matching_cert_serial
sha256_alg_matching_cert&.serial&.to_s
end

def certs_different?
encryption_cert != sha256_alg_matching_cert
end

def log_external_saml_auth_request
return unless external_saml_request?

Expand Down
40 changes: 21 additions & 19 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class SessionsController < Devise::SessionsController
before_action :store_sp_metadata_in_session, only: [:new]
before_action :check_user_needs_redirect, only: [:new]
before_action :apply_secure_headers_override, only: [:new, :create]
before_action :clear_session_bad_password_count_if_window_expired, only: [:create]
before_action :clear_session_sign_in_failure_count_if_window_expired, only: [:create]
before_action :set_analytics_user_from_params, only: :create
before_action :allow_csp_recaptcha_src, if: :recaptcha_enabled?

Expand All @@ -37,12 +37,14 @@ def new

def create
session[:sign_in_flow] = :sign_in
return process_rate_limited if session_bad_password_count_max_exceeded?
return process_rate_limited if session_sign_in_failure_count_max_exceeded?
return process_locked_out_user if current_user && user_locked_out?(current_user)
return process_rate_limited if rate_limited?
return process_failed_captcha unless recaptcha_response.success? || log_captcha_failures_only?

rate_limit_password_failure = true

return process_failed_captcha unless recaptcha_response.success? || log_captcha_failures_only?

self.resource = warden.authenticate!(auth_options)
handle_valid_authentication
ensure
Expand All @@ -65,38 +67,38 @@ def analytics_user

private

def clear_session_bad_password_count_if_window_expired
locked_at = session[:max_bad_passwords_at]
window = IdentityConfig.store.max_bad_passwords_window_in_seconds
def clear_session_sign_in_failure_count_if_window_expired
locked_at = session[:max_sign_in_failures_at]
window = IdentityConfig.store.max_sign_in_failures_window_in_seconds
return if locked_at.nil? || (locked_at + window) > Time.zone.now.to_i
[:max_bad_passwords_at, :bad_password_count].each { |x| session.delete(x) }
[:max_sign_in_failures_at, :sign_in_failure_count].each { |x| session.delete(x) }
end

def session_bad_password_count_max_exceeded?
session[:bad_password_count].to_i >= IdentityConfig.store.max_bad_passwords
def session_sign_in_failure_count_max_exceeded?
session[:sign_in_failure_count].to_i >= IdentityConfig.store.max_sign_in_failures
end

def increment_session_bad_password_count
session[:bad_password_count] = session[:bad_password_count].to_i + 1
return unless session_bad_password_count_max_exceeded?
session[:max_bad_passwords_at] ||= Time.zone.now.to_i
def increment_session_sign_in_failure_count
session[:sign_in_failure_count] = session[:sign_in_failure_count].to_i + 1
return unless session_sign_in_failure_count_max_exceeded?
session[:max_sign_in_failures_at] ||= Time.zone.now.to_i
end

def process_rate_limited
sign_out(:user)
warden.lock!

flash[:error] = t(
'errors.sign_in.bad_password_limit',
'errors.sign_in.sign_in_failure_limit',
time_left: locked_out_time_remaining,
)
redirect_to root_url
end

def locked_out_time_remaining
if session[:max_bad_passwords_at]
locked_at = session[:max_bad_passwords_at]
window = IdentityConfig.store.max_bad_passwords_window_in_seconds.seconds
if session[:max_sign_in_failures_at]
locked_at = session[:max_sign_in_failures_at]
window = IdentityConfig.store.max_sign_in_failures_window_in_seconds.seconds
time_lockout_expires = Time.zone.at(locked_at) + window
else
time_lockout_expires = rate_limiter&.expires_at || Time.zone.now
Expand Down Expand Up @@ -188,7 +190,7 @@ def process_locked_out_user

def handle_invalid_authentication
rate_limiter&.increment!
increment_session_bad_password_count
increment_session_sign_in_failure_count
end

def handle_valid_authentication
Expand Down Expand Up @@ -223,7 +225,7 @@ def track_authentication_attempt
rate_limited: rate_limited?,
captcha_validation_performed: captcha_validation_performed?,
valid_captcha_result: recaptcha_response.success?,
bad_password_count: session[:bad_password_count].to_i,
sign_in_failure_count: session[:sign_in_failure_count].to_i,
sp_request_url_present: sp_session[:request_url].present?,
remember_device: remember_device_cookie.present?,
new_device: success ? new_device? : nil,
Expand Down
1 change: 1 addition & 0 deletions app/forms/gpo_verify_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def submit(is_enhanced_ipp)
pii_like_keypaths: [[:errors, :otp], [:error_details, :otp]],
pending_in_person_enrollment: !!pending_profile&.in_person_enrollment&.pending?,
fraud_check_failed: fraud_check_failed,
initiating_service_provider: pending_profile&.initiating_service_provider_issuer,
},
)
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,6 @@ interface AcuantCaptureProps {
*/
const NBSP_UNICODE = '\u00A0';

/**
* A noop function.
*/
const noop = () => {};

/**
* Returns true if the given Acuant capture failure was caused by the user declining access to the
* camera, or false otherwise.
Expand Down Expand Up @@ -491,6 +486,16 @@ function AcuantCapture(
}
}

/**
* Responds to a drag and drop file upload by either preventing the default action
* or allowing the file to be uploaded
*/
function startDragDropUpload(event) {
if (!allowUpload) {
event.preventDefault();
}
}

/**
* Responds to a click by starting capture if supported in the environment, or triggering the
* default file picker prompt. The click event may originate from the file input itself, or
Expand Down Expand Up @@ -783,7 +788,7 @@ function AcuantCapture(
errorMessage={ownErrorMessage ?? errorMessage}
isValuePending={hasStartedCropping}
onClick={withLoggedClick('placeholder')(startCaptureOrTriggerUpload)}
onDrop={withLoggedClick('placeholder', { isDrop: true })(noop)}
onDrop={withLoggedClick('placeholder', { isDrop: true })(startDragDropUpload)}
onChange={onUpload}
onError={() => setOwnErrorMessage(null)}
/>
Expand Down
2 changes: 1 addition & 1 deletion app/javascript/packages/phone-input/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"version": "1.0.0",
"dependencies": {
"intl-tel-input": "^24.5.0",
"libphonenumber-js": "^1.11.4"
"libphonenumber-js": "^1.11.17"
},
"sideEffects": [
"./index.ts"
Expand Down
39 changes: 28 additions & 11 deletions app/jobs/resolution_proofing_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def perform(
service_provider_issuer: nil,
threatmetrix_session_id: nil,
request_ip: nil,
proofing_components: nil, # rubocop:disable Lint/UnusedMethodArgument
proofing_components: nil,
# DEPRECATED ARGUMENTS
should_proof_state_id: false # rubocop:disable Lint/UnusedMethodArgument
)
Expand Down Expand Up @@ -75,7 +75,7 @@ def perform(
timing: timer.results,
)

if use_shadow_mode?(user:)
if use_shadow_mode?(user:, proofing_components:)
SocureShadowModeProofingJob.perform_later(
document_capture_session_result_id: document_capture_session&.result_id,
encrypted_arguments:,
Expand All @@ -86,15 +86,22 @@ def perform(
end
end

def use_shadow_mode?(user:)
IdentityConfig.store.idv_socure_shadow_mode_enabled &&
AbTests::SOCURE_IDV_SHADOW_MODE.bucket(
request: nil,
service_provider: nil,
session: nil,
user:,
user_session: nil,
) == :shadow_mode_enabled
# @param user [User]
# @param proofing_components [Hash,nil]
def use_shadow_mode?(user:, proofing_components:)
# Let idv_socure_shadow_mode_enabled setting control shadow mode globally
disabled_globally = !IdentityConfig.store.idv_socure_shadow_mode_enabled
return false if disabled_globally

# If the user went through Socure docv, they are already a Socure user and
# are thus eligible for shadow mode.
enabled_for_docv_users =
IdentityConfig.store.idv_socure_shadow_mode_enabled_for_docv_users
is_docv_user = proofing_components&.dig(:document_check) == Idp::Constants::Vendors::SOCURE
return true if enabled_for_docv_users && is_docv_user

# Otherwise fall back to A/B test
shadow_mode_ab_test_bucket(user:) == :socure_shadow_mode_for_non_docv_users
end

private
Expand Down Expand Up @@ -150,4 +157,14 @@ def logger_info_hash(hash)
def progressive_proofer
@progressive_proofer ||= Proofing::Resolution::ProgressiveProofer.new
end

def shadow_mode_ab_test_bucket(user:)
AbTests::SOCURE_IDV_SHADOW_MODE_FOR_NON_DOCV_USERS.bucket(
request: nil,
service_provider: nil,
session: nil,
user:,
user_session: nil,
)
end
end
1 change: 1 addition & 0 deletions app/models/document_capture_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def load_result
EncryptedRedisStructStorage.load(result_id, type: DocumentCaptureSessionResult)
end

# @param doc_auth_response [DocAuth::Response]
def store_result_from_response(doc_auth_response)
session_result = load_result || DocumentCaptureSessionResult.new(
id: generate_result_id,
Expand Down
Loading