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
4 changes: 4 additions & 0 deletions .reek
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ TooManyMethods:
- Users::ConfirmationsController
- ApplicationController
- OpenidConnectAuthorizeForm
- OpenidConnect::AuthorizationController
- Idv::Session
- User
UncommunicativeMethodName:
Expand Down Expand Up @@ -103,6 +104,9 @@ UtilityFunction:
enabled: false
TooManyStatements:
enabled: false
UncommunicativeMethodName:
exclude:
- visit_idp_from_sp_with_loa1
UncommunicativeParameterName:
exclude:
- begin_sign_up_with_sp_and_loa
Expand Down
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ Metrics/ClassLength:
Exclude:
- spec/**/*
- app/controllers/application_controller.rb
- app/controllers/openid_connect/authorization_controller.rb
- app/controllers/users/confirmations_controller.rb
- app/controllers/devise/two_factor_authentication_controller.rb
- app/decorators/user_decorator.rb
- app/services/idv/session.rb

Metrics/LineLength:
Description: Limit lines to 100 characters.
Expand Down
3 changes: 2 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
class ApplicationController < ActionController::Base
include UserSessionContext
include VerifyProfileConcern

FLASH_KEYS = %w[alert error notice success warning].freeze

Expand Down Expand Up @@ -83,7 +84,7 @@ def after_sign_in_path_for(user)
end

def signed_in_path
user_fully_authenticated? ? account_path : user_two_factor_authentication_path
user_fully_authenticated? ? account_or_verify_profile_path : user_two_factor_authentication_path
end

def reauthn_param
Expand Down
25 changes: 17 additions & 8 deletions app/controllers/concerns/two_factor_authenticatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def reset_attempt_count_if_user_no_longer_locked_out
def handle_valid_otp
if authentication_context?
handle_valid_otp_for_authentication_context
elsif idv_or_confirmation_context?
elsif idv_or_confirmation_context? || profile_context?
handle_valid_otp_for_confirmation_context
end

Expand Down Expand Up @@ -132,7 +132,7 @@ def handle_valid_otp_for_authentication_context
end

def assign_phone
@updating_existing_number = old_phone.present?
@updating_existing_number = old_phone.present? && !profile_context?

if @updating_existing_number && confirmation_context?
phone_changed
Expand All @@ -157,18 +157,27 @@ def phone_confirmed
end

def update_phone_attributes
current_time = Time.zone.now

if idv_context?
Idv::Session.new(user_session, current_user).params['phone_confirmed_at'] = current_time
if idv_or_profile_context?
update_idv_state
else
UpdateUser.new(
user: current_user,
attributes: { phone: user_session[:unconfirmed_phone], phone_confirmed_at: current_time }
attributes: { phone: user_session[:unconfirmed_phone], phone_confirmed_at: Time.zone.now }
).call
end
end

def update_idv_state
now = Time.zone.now
if idv_context?
Idv::Session.new(user_session, current_user).params['phone_confirmed_at'] = now
elsif profile_context?
profile = current_user.decorate.pending_profile
profile.verified_at = now
profile.activate
end
end

def reset_otp_session_data
user_session.delete(:unconfirmed_phone)
user_session[:context] = 'authentication'
Expand Down Expand Up @@ -213,7 +222,7 @@ def direct_otp_code
end

def personal_key_unavailable?
idv_or_confirmation_context? || current_user.personal_key.blank?
idv_or_confirmation_context? || profile_context? || current_user.personal_key.blank?
end

def unconfirmed_phone?
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/concerns/user_session_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,12 @@ def idv_context?
def idv_or_confirmation_context?
confirmation_context? || idv_context?
end

def idv_or_profile_context?
idv_context? || profile_context?
end

def profile_context?
context == 'profile'
end
end
31 changes: 31 additions & 0 deletions app/controllers/concerns/verify_profile_concern.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module VerifyProfileConcern
private

def account_or_verify_profile_path
public_send "#{account_or_verify_profile_route}_path"
end

def account_or_verify_profile_url
public_send "#{account_or_verify_profile_route}_url"
end

def account_or_verify_profile_route
return 'account' if idv_context? || profile_context?
return 'account' unless current_user.decorate.pending_profile_requires_verification?
verify_profile_route
end

def verify_profile_route
decorated_user = current_user.decorate
if decorated_user.needs_profile_phone_verification?
flash[:notice] = t('account.index.verification.instructions')
return 'verify_profile_phone'
end
return 'verify_account' if decorated_user.needs_profile_usps_verification?
end

def profile_needs_verification?
return false if current_user.blank?
current_user.decorate.pending_profile_requires_verification?
end
end
13 changes: 11 additions & 2 deletions app/controllers/openid_connect/authorization_controller.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module OpenidConnect
class AuthorizationController < ApplicationController
include FullyAuthenticatable
include VerifyProfileConcern

before_action :build_authorize_form_from_params, only: [:index]
before_action :store_request, only: [:index]
Expand All @@ -11,10 +12,9 @@ class AuthorizationController < ApplicationController

def index
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
return redirect_to verify_url if identity_needs_verification?
return redirect_to_account_or_verify_profile_url if profile_or_identity_needs_verification?

track_index_action_analytics

return create if already_allowed?

render(@success ? :index : :error)
Expand Down Expand Up @@ -45,6 +45,15 @@ def destroy

private

def redirect_to_account_or_verify_profile_url
return redirect_to account_or_verify_profile_url if profile_needs_verification?
redirect_to verify_url if identity_needs_verification?
end

def profile_or_identity_needs_verification?
profile_needs_verification? || identity_needs_verification?
end

def track_index_action_analytics
@success = @authorize_form.valid?

Expand Down
16 changes: 10 additions & 6 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ class SamlIdpController < ApplicationController
include SamlIdpAuthConcern
include SamlIdpLogoutConcern
include FullyAuthenticatable
include VerifyProfileConcern

skip_before_action :verify_authenticity_token
skip_before_action :handle_two_factor_authentication, only: :logout

def auth
return confirm_two_factor_authenticated(request_id) unless user_fully_authenticated?
process_fully_authenticated_user do |needs_idv|
return store_location_and_redirect_to_verify_url if needs_idv
process_fully_authenticated_user do |needs_idv, needs_profile_finish|
return store_location_and_redirect_to(verify_url) if needs_idv && !needs_profile_finish
return store_location_and_redirect_to(account_or_verify_profile_url) if needs_profile_finish
end
delete_branded_experience
render_template_for(saml_response, saml_request.response_url, 'SAMLResponse')
Expand All @@ -40,14 +42,16 @@ def process_fully_authenticated_user
link_identity_from_session_data

needs_idv = identity_needs_verification?
analytics.track_event(Analytics::SAML_AUTH, @result.to_h.merge(idv: needs_idv))
needs_profile_finish = profile_needs_verification?
analytics_payload = @result.to_h.merge(idv: needs_idv, finish_profile: needs_profile_finish)
analytics.track_event(Analytics::SAML_AUTH, analytics_payload)

yield needs_idv
yield needs_idv, needs_profile_finish
end

def store_location_and_redirect_to_verify_url
def store_location_and_redirect_to(url)
store_location_for(:user, request.original_url)
redirect_to verify_url
redirect_to url
end

def render_template_for(message, action_url, type)
Expand Down
12 changes: 8 additions & 4 deletions app/controllers/users/verify_account_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Users
class VerifyAccountController < ApplicationController
before_action :confirm_two_factor_authenticated
before_action :confirm_verification_needed

def index
Expand All @@ -10,7 +11,7 @@ def create
@verify_account_form = build_verify_account_form
if @verify_account_form.submit
flash[:success] = t('account.index.verification.success')
redirect_to account_path
redirect_to after_sign_in_path_for(current_user)
else
render :index
end
Expand All @@ -31,12 +32,15 @@ def params_otp
end

def confirm_verification_needed
current_user.active_profile.blank? && current_user.decorate.pending_profile.present?
return if current_user.decorate.pending_profile_requires_verification?
redirect_to account_url
end

def decrypted_pii
cacher = Pii::Cacher.new(current_user, user_session)
cacher.fetch
@_decrypted_pii ||= begin
cacher = Pii::Cacher.new(current_user, user_session)
cacher.fetch
end
end
end
end
44 changes: 44 additions & 0 deletions app/controllers/users/verify_profile_phone_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Users
class VerifyProfilePhoneController < ApplicationController
include PhoneConfirmation

before_action :confirm_two_factor_authenticated
before_action :confirm_phone_verification_needed

def index
prompt_to_confirm_phone(phone: profile_phone, context: 'profile')
end

private

def confirm_phone_verification_needed
return if unverified_phone?
redirect_to account_url
end

def pending_profile_requires_verification?
current_user.decorate.pending_profile_requires_verification?
end

def unverified_phone?
pending_profile_requires_verification? &&
pending_profile.phone_confirmed? &&
current_user.phone != profile_phone
end

def profile_phone
@_profile_phone ||= decrypted_pii.phone.to_s
end

def pending_profile
@_pending_profile ||= current_user.decorate.pending_profile
end

def decrypted_pii
@_decrypted_pii ||= begin
cacher = Pii::Cacher.new(current_user, user_session)
cacher.fetch
end
end
end
end
1 change: 1 addition & 0 deletions app/controllers/verify/sessions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class SessionsController < ApplicationController
delegate :attempts_exceeded?, to: :step, prefix: true

def new
user_session[:context] = 'idv'
@view_model = view_model
analytics.track_event(Analytics::IDV_BASIC_INFO_VISIT)
end
Expand Down
35 changes: 27 additions & 8 deletions app/decorators/user_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,43 @@ def masked_two_factor_phone_number
masked_number(user.phone)
end

def identity_verified?
user.active_profile.present?
def active_identity_for(service_provider)
user.active_identities.find_by(service_provider: service_provider.issuer)
end

def identity_not_verified?
!identity_verified?
def active_or_pending_profile
user.active_profile || pending_profile
end

def active_identity_for(service_provider)
user.active_identities.find_by(service_provider: service_provider.issuer)
def pending_profile_requires_verification?
return false if pending_profile.blank?
return true if identity_not_verified?
return false if active_profile_newer_than_pending_profile?
true
end

def pending_profile
user.profiles.verification_pending.order(created_at: :desc).first
end

def active_or_pending_profile
user.active_profile || pending_profile
def identity_not_verified?
!identity_verified?
end

def identity_verified?
user.active_profile.present?
end

def active_profile_newer_than_pending_profile?
user.active_profile.activated_at >= pending_profile.created_at
end

def needs_profile_phone_verification?
pending_profile_requires_verification? && pending_profile.phone_confirmed?
end

def needs_profile_usps_verification?
pending_profile_requires_verification? && !pending_profile.phone_confirmed?
end

# This user's most recently activated profile that has also been deactivated
Expand Down
1 change: 1 addition & 0 deletions app/forms/verify_account_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def reset_sensitive_fields
end

def activate_profile
pending_profile.verified_at = Time.zone.now
pending_profile.activate
end
end
4 changes: 3 additions & 1 deletion app/services/idv/profile_maker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ module Idv
class ProfileMaker
attr_reader :pii_attributes, :profile

def initialize(applicant:, user:, normalized_applicant:)
def initialize(applicant:, user:, normalized_applicant:, vendor:, phone_confirmed:)
@profile = Profile.new(user: user, deactivation_reason: :verification_pending)
@pii_attributes = pii_from_applicant(applicant, normalized_applicant)
profile.encrypt_pii(user.user_access_key, pii_attributes)
profile.vendor = vendor
profile.phone_confirmed = phone_confirmed
profile.save!
end

Expand Down
Loading