Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 0 deletions Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ 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.5-18f'
gem 'saml_idp', github: '18F/saml_idp', tag: '0.23.4-18f'
gem 'scrypt'
gem 'simple_form', '>= 5.0.2'
gem 'stringex', require: false
Expand Down
8 changes: 4 additions & 4 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: bdf8e1f93707e413ecbd0f48d803e18812e19f90
tag: 0.23.5-18f
revision: e5d876cf10ce9b39bba0cc523d06c4dda1af5124
tag: 0.23.4-18f
specs:
saml_idp (0.23.5.pre.18f)
saml_idp (0.23.4.pre.18f)
activesupport
builder
faraday
Expand Down Expand Up @@ -477,7 +477,7 @@ GEM
pg (1.5.9)
pg_query (5.1.0)
google-protobuf (>= 3.22.3)
phonelib (0.10.3)
phonelib (0.9.1)
pkcs11 (0.3.4)
premailer (1.27.0)
addressable
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,6 @@ 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: 0 additions & 7 deletions app/controllers/concerns/idv/verify_info_concern.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true

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

Expand Down Expand Up @@ -40,12 +39,6 @@ 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: 22 additions & 0 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,13 @@ 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 @@ -161,6 +168,21 @@ 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: 19 additions & 21 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_sign_in_failure_count_if_window_expired, only: [:create]
before_action :clear_session_bad_password_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,14 +37,12 @@ def new

def create
session[:sign_in_flow] = :sign_in
return process_rate_limited if session_sign_in_failure_count_max_exceeded?
return process_rate_limited if session_bad_password_count_max_exceeded?
return process_locked_out_user if current_user && user_locked_out?(current_user)
return process_rate_limited if rate_limited?

rate_limit_password_failure = true

return process_failed_captcha unless recaptcha_response.success? || log_captcha_failures_only?

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

private

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
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
return if locked_at.nil? || (locked_at + window) > Time.zone.now.to_i
[:max_sign_in_failures_at, :sign_in_failure_count].each { |x| session.delete(x) }
[:max_bad_passwords_at, :bad_password_count].each { |x| session.delete(x) }
end

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

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
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
end

def process_rate_limited
sign_out(:user)
warden.lock!

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

def locked_out_time_remaining
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
if session[:max_bad_passwords_at]
locked_at = session[:max_bad_passwords_at]
window = IdentityConfig.store.max_bad_passwords_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 @@ -190,7 +188,7 @@ def process_locked_out_user

def handle_invalid_authentication
rate_limiter&.increment!
increment_session_sign_in_failure_count
increment_session_bad_password_count
end

def handle_valid_authentication
Expand Down Expand Up @@ -225,7 +223,7 @@ def track_authentication_attempt
rate_limited: rate_limited?,
captcha_validation_performed: captcha_validation_performed?,
valid_captcha_result: recaptcha_response.success?,
sign_in_failure_count: session[:sign_in_failure_count].to_i,
bad_password_count: session[:bad_password_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: 0 additions & 1 deletion app/forms/gpo_verify_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ 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,6 +140,11 @@ 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 @@ -486,16 +491,6 @@ 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 @@ -788,7 +783,7 @@ function AcuantCapture(
errorMessage={ownErrorMessage ?? errorMessage}
isValuePending={hasStartedCropping}
onClick={withLoggedClick('placeholder')(startCaptureOrTriggerUpload)}
onDrop={withLoggedClick('placeholder', { isDrop: true })(startDragDropUpload)}
onDrop={withLoggedClick('placeholder', { isDrop: true })(noop)}
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.17"
"libphonenumber-js": "^1.11.4"
},
"sideEffects": [
"./index.ts"
Expand Down
39 changes: 11 additions & 28 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,
proofing_components: nil, # rubocop:disable Lint/UnusedMethodArgument
# 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:, proofing_components:)
if use_shadow_mode?(user:)
SocureShadowModeProofingJob.perform_later(
document_capture_session_result_id: document_capture_session&.result_id,
encrypted_arguments:,
Expand All @@ -86,22 +86,15 @@ def perform(
end
end

# @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
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
end

private
Expand Down Expand Up @@ -157,14 +150,4 @@ 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: 0 additions & 1 deletion app/models/document_capture_session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ 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