Skip to content
Closed
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
4 changes: 2 additions & 2 deletions app/components/alert_component.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# frozen_string_literal: true

class AlertComponent < BaseComponent
VALID_TYPES = %i[info success warning error emergency other].freeze
VALID_TYPES = [nil, :info, :success, :warning, :error, :emergency].freeze

attr_reader :type, :message, :tag_options, :text_tag

def initialize(type: :info, text_tag: 'p', message: nil, **tag_options)
def initialize(type: nil, text_tag: 'p', message: nil, **tag_options)
if !VALID_TYPES.include?(type)
raise ArgumentError, "`type` #{type} is invalid, expected one of #{VALID_TYPES}"
end
Expand Down
4 changes: 2 additions & 2 deletions app/controllers/users/webauthn_setup_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def confirm
user_session: user_session,
device_name: DeviceName.from_user_agent(request.user_agent),
)
result = form.submit(request.protocol, confirm_params)
result = form.submit(confirm_params)
@platform_authenticator = form.platform_authenticator?
@presenter = WebauthnSetupPresenter.new(
current_user: current_user,
Expand Down Expand Up @@ -161,7 +161,7 @@ def confirm_params
:name,
:platform_authenticator,
:transports,
)
).merge(protocol: request.protocol)
end
end
end
50 changes: 27 additions & 23 deletions app/forms/webauthn_setup_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class WebauthnSetupForm
:name,
presence: { message: proc { |object| object.send(:generic_error_message) } }
validate :name_is_unique
validate :validate_attestation_response

attr_reader :attestation_response

Expand All @@ -22,12 +23,13 @@ def initialize(user:, user_session:, device_name:)
@name = nil
@platform_authenticator = false
@authenticator_data_flags = nil
@protocol = nil
@device_name = device_name
end

def submit(protocol, params)
def submit(params)
consume_parameters(params)
success = valid? && valid_attestation_response?(protocol)
success = valid?
if success
create_webauthn_configuration
event = PushNotification::RecoveryInformationChangedEvent.new(user: user)
Expand Down Expand Up @@ -59,7 +61,7 @@ def generic_error_message

private

attr_reader :success, :transports, :invalid_transports
attr_reader :success, :transports, :invalid_transports, :protocol
attr_accessor :user, :challenge, :attestation_object, :client_data_json,
:name, :platform_authenticator, :authenticator_data_flags, :device_name

Expand All @@ -74,6 +76,7 @@ def consume_parameters(params)
@transports, @invalid_transports = params[:transports]&.split(',')&.partition do |transport|
WebauthnConfiguration::VALID_TRANSPORTS.include?(transport)
end
@protocol = params[:protocol]
end

def name_is_unique
Expand All @@ -94,32 +97,22 @@ def name_is_unique
end
end

def validate_attestation_response
return if valid_attestation_response?(protocol)
errors.add(:attestation_object, :invalid, message: general_error_message)
end

def valid_attestation_response?(protocol)
original_origin = "#{protocol}#{self.class.domain_name}"
@attestation_response = ::WebAuthn::AuthenticatorAttestationResponse.new(
attestation_object: Base64.decode64(@attestation_object),
client_data_json: Base64.decode64(@client_data_json),
)
safe_response("#{protocol}#{self.class.domain_name}")
end

def safe_response(original_origin)
response = @attestation_response.valid?(@challenge.pack('c*'), original_origin)
add_attestation_error unless response
response
rescue StandardError
add_attestation_error
false
end

def add_attestation_error
if @platform_authenticator
errors.add :name, I18n.t('errors.webauthn_platform_setup.general_error'),
type: :attestation_error
else
errors.add :name, I18n.t(
'errors.webauthn_setup.general_error_html',
link_html: I18n.t('errors.webauthn_setup.additional_methods_link'),
), type: :attestation_error
begin
attestation_response.valid?(@challenge.pack('c*'), original_origin)
rescue StandardError
false
end
end

Expand Down Expand Up @@ -151,6 +144,17 @@ def create_webauthn_configuration
)
end

def general_error_message
if platform_authenticator
I18n.t('errors.webauthn_platform_setup.general_error')
else
I18n.t(
'errors.webauthn_setup.general_error_html',
link_html: I18n.t('errors.webauthn_setup.additional_methods_link'),
)
end
end

def mfa_user
@mfa_user ||= MfaContext.new(user)
end
Expand Down
2 changes: 1 addition & 1 deletion app/jobs/reports/drop_off_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def perform(report_date)
self.report_date = report_date

subject = "Drop Off Report - #{report_date.to_date}"
JSON.parse(configs).each do |config|
configs.each do |config|
reports = [report_maker(config['issuers']).as_emailable_reports]
config['emails'].each do |email|
ReportMailer.tables_report(
Expand Down
11 changes: 3 additions & 8 deletions app/jobs/risc_delivery_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def perform(
'Content-Type' => 'application/secevent+jwt',
) do |req|
req.options.context = {
service_name: inline? ? 'risc_http_push_direct' : 'risc_http_push_async',
service_name: 'risc_http_push_async',
}
end
end
Expand All @@ -58,7 +58,7 @@ def perform(
user:,
)
rescue *NETWORK_ERRORS => err
raise err if self.executions < 2 && !inline?
raise err if self.executions < 2

track_event(
error: err.message,
Expand All @@ -68,7 +68,7 @@ def perform(
user:,
)
rescue RedisRateLimiter::LimitError => err
raise err if self.executions < 10 && !inline?
raise err if self.executions < 10

track_event(
error: err.message,
Expand Down Expand Up @@ -102,18 +102,13 @@ def faraday
end
end

def inline?
queue_adapter.is_a?(ActiveJob::QueueAdapters::InlineAdapter)
end

def track_event(event_type:, issuer:, success:, user:, error: nil, status: nil)
analytics(user).risc_security_event_pushed(
client_id: issuer,
error:,
event_type:,
status:,
success:,
transport: inline? ? 'direct' : 'async',
)
end

Expand Down
3 changes: 0 additions & 3 deletions app/services/analytics_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4919,14 +4919,12 @@ def return_to_sp_failure_to_proof(redirect_url:, flow: nil, step: nil, location:
# @param [String] client_id
# @param [String] event_type
# @param [Boolean] success
# @param ['async'|'direct'] transport
# @param [Integer] status
# @param [String] error
def risc_security_event_pushed(
client_id:,
event_type:,
success:,
transport:,
status: nil,
error: nil,
**extra
Expand All @@ -4938,7 +4936,6 @@ def risc_security_event_pushed(
event_type:,
status:,
success:,
transport:,
**extra,
)
end
Expand Down
10 changes: 2 additions & 8 deletions app/services/push_notification/http_push.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,12 @@ def url_options
def deliver_one(service_provider)
deliver_local(service_provider) if IdentityConfig.store.risc_notifications_local_enabled

job_arguments = {
RiscDeliveryJob.perform_later(
push_notification_url: service_provider.push_notification_url,
jwt: jwt(service_provider),
event_type: event.event_type,
issuer: service_provider.issuer,
}

if IdentityConfig.store.risc_notifications_active_job_enabled
RiscDeliveryJob.perform_later(**job_arguments)
else
RiscDeliveryJob.perform_now(**job_arguments)
end
)
end

def deliver_local(service_provider)
Expand Down
2 changes: 1 addition & 1 deletion app/views/accounts/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

<h3 class="margin-top-3"><%= t('i18n.language') %></h3>
<div class="grid-row padding-1 border border-primary-light">
<div class="grid-col-8">
<div class="grid-col-8" lang="<%= @presenter.user.email_language || I18n.default_locale %>">
<%= @presenter.user.email_language_preference_description %>
</div>
<div class="grid-col-4 text-right">
Expand Down
6 changes: 5 additions & 1 deletion app/views/idv/by_mail/request_letter/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@
) %>
</p>
<% else %>
<%= render AlertComponent.new(message: t('idv.messages.gpo.info_alert'), class: 'margin-bottom-4') %>
<%= render AlertComponent.new(
type: :info,
message: t('idv.messages.gpo.info_alert'),
class: 'margin-bottom-4',
) %>
<%= render PageHeadingComponent.new.with_content(@presenter.title) %>
<p>
<%= t('idv.messages.gpo.timeframe_html') %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/idv/in_person/ready_to_verify/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
) %>
</div>

<%= render AlertComponent.new(class: 'margin-y-4', text_tag: :div) do %>
<%= render AlertComponent.new(type: :info, class: 'margin-y-4', text_tag: :div) do %>
<p class="margin-bottom-1 margin-top-0 h3"><strong><%= t('in_person_proofing.body.barcode.deadline', deadline: @presenter.formatted_due_date) %></strong></p>
<p><%= t('in_person_proofing.body.barcode.deadline_restart') %></p>
<% end %>
Expand Down
2 changes: 1 addition & 1 deletion app/views/shared/_sp_alert.html.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<% alert = decorated_sp_session.sp_alert(section) %>
<% if alert %>
<%= render AlertComponent.new(text_tag: 'div', class: 'margin-bottom-4') do %>
<%= render AlertComponent.new(type: :info, text_tag: 'div', class: 'margin-bottom-4') do %>
<%= raw sanitize(alert, tags: %w[a b strong em br p ol ul li], attributes: %w[href target]) %>
<% end %>
<% end %>
1 change: 0 additions & 1 deletion config/application.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ reset_password_email_max_attempts: 20
reset_password_email_window_in_minutes: 60
reset_password_on_auth_fraud_event: true
risc_notifications_local_enabled: false
risc_notifications_active_job_enabled: false
risc_notifications_rate_limit_interval: 60
risc_notifications_rate_limit_max_requests: 1_000
risc_notifications_rate_limit_overrides: '{"https://example.com/push":{"interval":120,"max_requests":10000}}'
Expand Down
2 changes: 1 addition & 1 deletion lib/identity_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def self.store
Identity::Hostdata.config
end

# identity-hostdata transforms these configs to the described type
# rubocop:disable Metrics/BlockLength
BUILDER = proc do |config|
# ______________________________________
Expand Down Expand Up @@ -334,7 +335,6 @@ def self.store
config.add(:reset_password_email_max_attempts, type: :integer)
config.add(:reset_password_email_window_in_minutes, type: :integer)
config.add(:reset_password_on_auth_fraud_event, type: :boolean)
config.add(:risc_notifications_active_job_enabled, type: :boolean)
config.add(:risc_notifications_local_enabled, type: :boolean)
config.add(:risc_notifications_rate_limit_interval, type: :integer)
config.add(:risc_notifications_rate_limit_max_requests, type: :integer)
Expand Down
4 changes: 2 additions & 2 deletions spec/components/alert_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@
expect(rendered).to have_content('locals')
end

it 'defaults to type "info"' do
it 'renders without modifier classes by default' do
rendered = render_inline AlertComponent.new(message: 'FYI')

expect(rendered).to have_selector('.usa-alert.usa-alert--info')
expect(rendered).to have_selector('.usa-alert:not([class*=usa-alert--])')
end

it 'accepts alert type param' do
Expand Down
10 changes: 3 additions & 7 deletions spec/components/previews/alert_component_preview.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,14 @@ def emergency
render(AlertComponent.new(message: 'An emergency message', type: :emergency))
end

def other
render(AlertComponent.new(message: 'An other message', type: :other))
end

def with_custom_text_tag
render(AlertComponent.new(type: :success, message: 'A custom message', text_tag: 'div'))
end
# @!endgroup

# @param message text
# @param type select [info, success, warning, error, emergency, other]
def workbench(message: 'An important message', type: :info)
render(AlertComponent.new(message:, type:))
# @param type select [~, info, success, warning, error, emergency]
def workbench(message: 'An important message', type: nil)
render(AlertComponent.new(message:, type: type&.to_sym))
end
end
6 changes: 4 additions & 2 deletions spec/controllers/users/webauthn_setup_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,10 @@
'Multi-Factor Authentication Setup',
{
enabled_mfa_methods_count: 0,
errors: { name: [I18n.t('errors.webauthn_platform_setup.general_error')] },
error_details: { name: { attestation_error: true } },
errors: {
attestation_object: [I18n.t('errors.webauthn_platform_setup.general_error')],
},
error_details: { attestation_object: { invalid: true } },
in_account_creation_flow: false,
mfa_method_counts: {},
multi_factor_auth_method: 'webauthn_platform',
Expand Down
Loading