Skip to content

Commit

Permalink
Merge pull request #11748 from 18F/stages/rc-2025-01-14
Browse files Browse the repository at this point in the history
Deploy RC 443 to Production
  • Loading branch information
jmhooper authored Jan 14, 2025
2 parents 6ece32f + 87eec6f commit 4e1fbd4
Show file tree
Hide file tree
Showing 63 changed files with 2,392 additions and 396 deletions.
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 '[email protected]'
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
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: 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 @@ -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

0 comments on commit 4e1fbd4

Please sign in to comment.