Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
54dc79f
LG-13914 Implementation: EIPP Barcode page visual tag and REAL ID upd…
gina-yamada Jul 31, 2024
03763c8
LG-13517 update Acuant SDK and selfie camera commands for better 508 …
jmax-gsa Aug 1, 2024
9365c40
get prod dockerfile working properly (#10790)
timothy-spencer Aug 1, 2024
fd81274
LG-13689: Rate-limit sign-in attempts based on IP+user ID (#10982)
aduth Aug 1, 2024
b3314bd
Update saml_idp version (#11017)
Sgtpluck Aug 2, 2024
c1c9f1d
Update knapsack report (#10997)
Aug 2, 2024
9106726
Update rexml to 3.3.4 to resolve security advisory (#11020)
aduth Aug 2, 2024
e3306d7
Use printf to format newlines correctly in bash (#11022)
zachmargolis Aug 2, 2024
f3240e4
change cert bundle to the new global bundle (#11023)
timothy-spencer Aug 2, 2024
8b736a2
Support biometric ACR values in SAML (#11013)
vrajmohan Aug 2, 2024
fd343eb
Remove OIDC form-action CSP assertions from account creation specs (#…
aduth Aug 2, 2024
c82be24
LG-13150: remove deprecated address route (#11016)
KeithNava Aug 5, 2024
72832aa
LG-13691: Standardize PIV/CAC terminology (#10969)
jmdembe Aug 5, 2024
2d0332b
Limit analytics CSP revisions to necessary entries (#11021)
aduth Aug 5, 2024
dfbe4ad
changelog: Internal, In-person proofing, make sponser_id on in_person…
WilliamBirdsall Aug 5, 2024
f65b43d
LG-13896: State ID expiration validation (#10995)
amirbey Aug 5, 2024
66b1b4e
Fix spacing on PIV/CAC login screen (#11032)
aduth Aug 6, 2024
6939515
LG-13871: Update Account Deletion granted CTA (#11033)
mdiarra3 Aug 6, 2024
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
6 changes: 4 additions & 2 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ build-idp-image:
--build-arg "ARG_CI_COMMIT_SHA=${CI_COMMIT_SHA}"
--build-arg "LARGE_FILES_TOKEN=${LARGE_FILES_TOKEN}"
--build-arg "LARGE_FILES_USER=${LARGE_FILES_USER}"
--build-arg "SERVICE_PROVIDERS_KEY=${SERVICE_PROVIDERS_KEY}"


check_changelog:
stage: test
Expand Down Expand Up @@ -832,7 +834,7 @@ pinpoint_check_scheduled:
--channel "#login-appdev" \
--webhook "${SLACK_WEBHOOK}" \
--raise \
--text "Pinpoint supported countries check in GitLab failed.\nBuild Results: ${CI_JOB_URL}.\nCheck results locally with 'make lint_country_dialing_codes'"
--text "$(printf "Pinpoint supported countries check in GitLab failed.\nBuild Results: ${CI_JOB_URL}.\nCheck results locally with 'make lint_country_dialing_codes'")""
fi
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
Expand All @@ -856,7 +858,7 @@ audit_packages_scheduled:
--channel "#login-appdev" \
--webhook "${SLACK_WEBHOOK}" \
--raise \
--text "Dependencies audit in GitLab failed.\nBuild Results: ${CI_JOB_URL}\nCheck results locally with 'make audit'"
--text "$(printf "Dependencies audit in GitLab failed.\nBuild Results: ${CI_JOB_URL}\nCheck results locally with 'make audit'")"
fi
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,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.21.4-18f'
gem 'saml_idp', github: '18F/saml_idp', tag: '0.21.6-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 @@ -35,10 +35,10 @@ GIT

GIT
remote: https://github.com/18F/saml_idp.git
revision: 5e9999ef8e9260cda74cfea0a637f754994e0f9d
tag: 0.21.4-18f
revision: 512ad9b1609884781034773f6c4ccd53e7036a82
tag: 0.21.6-18f
specs:
saml_idp (0.21.4.pre.18f)
saml_idp (0.21.6.pre.18f)
activesupport
builder
faraday
Expand Down Expand Up @@ -578,7 +578,7 @@ GEM
actionpack (>= 5.0)
railties (>= 5.0)
retries (0.0.5)
rexml (3.3.2)
rexml (3.3.4)
strscan
rotp (6.3.0)
rouge (4.2.0)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions app/assets/stylesheets/email.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
@use 'variables/app' as *;
@use 'variables/email' as *;

@forward 'usa-tag';

.gray {
&:active,
&:hover,
Expand Down Expand Up @@ -73,10 +75,6 @@ h6 {
width: 50%;
}

.half {
width: 50%;
}

.footer {
background: $secondary-color;

Expand Down
2 changes: 1 addition & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def resolved_authn_context_result
service_provider: service_provider,
vtr: sp_session[:vtr],
acr_values: sp_session[:acr_values],
).resolve
).result
end
end

Expand Down
51 changes: 36 additions & 15 deletions app/controllers/users/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ def new

def create
session[:sign_in_flow] = :sign_in
return process_locked_out_session if session_bad_password_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?
return process_failed_captcha if !valid_captcha_result?

rate_limit_password_failure = true
self.resource = warden.authenticate!(auth_options)
handle_valid_authentication
ensure
increment_session_bad_password_count if rate_limit_password_failure && !current_user
handle_invalid_authentication if rate_limit_password_failure && !current_user
track_authentication_attempt(auth_params[:email])
end

Expand Down Expand Up @@ -71,7 +72,7 @@ def increment_session_bad_password_count
session[:max_bad_passwords_at] ||= Time.zone.now.to_i
end

def process_locked_out_session
def process_rate_limited
sign_out(:user)
warden.lock!

Expand All @@ -83,9 +84,14 @@ def process_locked_out_session
end

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

distance_of_time_in_words(Time.zone.now, time_lockout_expires, true)
end

Expand Down Expand Up @@ -147,7 +153,13 @@ def process_locked_out_user
render_full_width('two_factor_authentication/_locked', locals: { presenter: presenter })
end

def handle_invalid_authentication
rate_limiter&.increment!
increment_session_bad_password_count
end

def handle_valid_authentication
rate_limiter&.reset!
sign_in(resource_name, resource)
cache_profiles(auth_params[:password])
set_new_device_session(nil)
Expand All @@ -171,6 +183,7 @@ def track_authentication_attempt(email)
success: success,
user_id: user.uuid,
user_locked_out: user_locked_out?(user),
rate_limited: rate_limited?,
valid_captcha_result: valid_captcha_result?,
bad_password_count: session[:bad_password_count].to_i,
sp_request_url_present: sp_session[:request_url].present?,
Expand All @@ -183,6 +196,20 @@ def user_locked_out?(user)
user.locked_out?
end

def rate_limited?
!!rate_limiter&.limited?
end

def rate_limiter
return @rate_limiter if defined?(@rate_limiter)
user = User.find_with_email(auth_params[:email])
return @rate_limiter = nil unless user
@rate_limiter = RateLimiter.new(
rate_limit_type: :sign_in_user_id_per_ip,
target: [user.id, request.ip].join('-'),
)
end

def store_sp_metadata_in_session
return if sp_session[:issuer] || request_id.blank?
StoreSpMetadataInSession.new(session: session, request_id: request_id).call
Expand Down Expand Up @@ -214,23 +241,17 @@ def pending_account_reset_request

def override_csp_for_google_analytics
return unless IdentityConfig.store.participate_in_dap
# See: https://github.com/digital-analytics-program/gov-wide-code#content-security-policy
policy = current_content_security_policy
policy.script_src(
*policy.script_src,
'dap.digitalgov.gov',
'www.google-analytics.com',
'*.googletagmanager.com',
'www.googletagmanager.com',
)
policy.connect_src(
*policy.connect_src,
'*.google-analytics.com',
'*.analytics.google.com',
'*.googletagmanager.com',
)
policy.img_src(
*policy.img_src,
'*.google-analytics.com',
'*.googletagmanager.com',
'www.google-analytics.com',
)
request.content_security_policy = policy
end
Expand Down
10 changes: 9 additions & 1 deletion app/forms/idv/doc_pii_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ class DocPiiForm
validates_presence_of :state_id_number, { message: proc {
I18n.t('doc_auth.errors.general.no_liveness')
} }
validate :state_id_expired?

attr_reader :first_name, :last_name, :dob, :address1, :state, :zipcode, :attention_with_barcode,
:jurisdiction, :state_id_number
:jurisdiction, :state_id_number, :state_id_expiration
alias_method :attention_with_barcode?, :attention_with_barcode

def initialize(pii:, attention_with_barcode: false)
Expand All @@ -33,6 +34,7 @@ def initialize(pii:, attention_with_barcode: false)
@zipcode = pii[:zipcode]
@jurisdiction = pii[:state_id_jurisdiction]
@state_id_number = pii[:state_id_number]
@state_id_expiration = pii[:state_id_expiration]
@attention_with_barcode = attention_with_barcode
end

Expand Down Expand Up @@ -106,6 +108,12 @@ def dob_valid?
end
end

def state_id_expired?
if state_id_expiration && DateParser.parse_legacy(state_id_expiration).past?
errors.add(:state_id_expiration, generic_error, type: :state_id_expiration)
end
end

def zipcode_valid?
return if zipcode.is_a?(String) && zipcode.present?

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ interface FaceDetectionStates {
TOO_MANY_FACES: string;
FACE_TOO_SMALL: string;
FACE_CLOSE_TO_BORDER: string;
CLOSE_TEXT: string;
RETAKE_TEXT: string;
INTRO_TEXT: string;
SUBMIT_ALT: string;
CAPTURE_ALT: string;
}

function AcuantSelfieCamera({
Expand Down Expand Up @@ -145,6 +150,11 @@ function AcuantSelfieCamera({
TOO_MANY_FACES: t('doc_auth.info.selfie_capture_status.too_many_faces'),
FACE_TOO_SMALL: t('doc_auth.info.selfie_capture_status.face_too_small'),
FACE_CLOSE_TO_BORDER: t('doc_auth.info.selfie_capture_status.face_close_to_border'),
CLOSE_TEXT: t('doc_auth.info.selfie_capture.action.close'),
RETAKE_TEXT: t('doc_auth.info.selfie_capture.action.retake'),
INTRO_TEXT: t('doc_auth.info.selfie_capture.intro'),
SUBMIT_ALT: t('doc_auth.info.selfie_capture.action.submit'),
CAPTURE_ALT: t('doc_auth.info.selfie_capture.action.capture'),
};
const cleanupSelfieCamera = () => {
window.AcuantPassiveLiveness.end();
Expand Down
25 changes: 8 additions & 17 deletions app/presenters/openid_connect_user_info_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def user_info
info.merge!(x509_attributes) if scoper.x509_scopes_requested?
info[:verified_at] = verified_at if scoper.verified_at_requested?
if identity.vtr.nil?
info[:ial] = asserted_ial_value
info[:ial] = authn_context_resolver.asserted_ial_acr
info[:aal] = identity.requested_aal_value
else
info[:vot] = vot_values
Expand All @@ -40,26 +40,13 @@ def url_options

private

def asserted_ial_value
return Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF unless active_profile.present?

if resolved_authn_context_result.biometric_comparison?
Saml::Idp::Constants::IAL2_BIO_REQUIRED_AUTHN_CONTEXT_CLASSREF
elsif resolved_authn_context_result.identity_proofing? ||
resolved_authn_context_result.ialmax?
Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF
else
Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF
end
end

def vot_values
AuthnContextResolver.new(
user: identity.user,
vtr: JSON.parse(identity.vtr),
service_provider: identity&.service_provider_record,
acr_values: nil,
).resolve.expanded_component_values
).result.expanded_component_values
end

def uuid_from_sp_identity(identity)
Expand Down Expand Up @@ -151,12 +138,16 @@ def identity_proofing_requested_for_verified_user?
end

def resolved_authn_context_result
@resolved_authn_context_result ||= AuthnContextResolver.new(
authn_context_resolver.result
end

def authn_context_resolver
@authn_context_resolver ||= AuthnContextResolver.new(
user: identity.user,
service_provider: identity&.service_provider_record,
vtr: identity.vtr.presence && JSON.parse(identity.vtr),
acr_values: identity.acr_values,
).resolve
)
end

def ial2_session?
Expand Down
2 changes: 1 addition & 1 deletion app/presenters/piv_cac_authentication_login_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ def heading
end

def info
t('instructions.mfa.piv_cac.sign_in_html', app_name: APP_NAME)
t('instructions.mfa.piv_cac.sign_in', app_name: APP_NAME)
end
end
2 changes: 1 addition & 1 deletion app/services/analytics.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def resolved_authn_context_result
service_provider:,
vtr: session[:sp][:vtr],
acr_values: session[:sp][:acr_values],
).resolve
).result
rescue Vot::Parser::ParseException
return
end
Expand Down
3 changes: 3 additions & 0 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,7 @@ def edit_password_visit(required_password_change: false, **extra)
# @param [Boolean] success
# @param [String] user_id
# @param [Boolean] user_locked_out if the user is currently locked out of their second factor
# @param [Boolean] rate_limited Whether the user has exceeded user IP rate limiting
# @param [Boolean] valid_captcha_result Whether user passed the reCAPTCHA check
# @param [String] bad_password_count represents number of prior login failures
# @param [Boolean] sp_request_url_present if was an SP request URL in the session
Expand All @@ -421,6 +422,7 @@ def email_and_password_auth(
success:,
user_id:,
user_locked_out:,
rate_limited:,
valid_captcha_result:,
bad_password_count:,
sp_request_url_present:,
Expand All @@ -433,6 +435,7 @@ def email_and_password_auth(
success:,
user_id:,
user_locked_out:,
rate_limited:,
valid_captcha_result:,
bad_password_count:,
sp_request_url_present:,
Expand Down
Loading