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
10 changes: 9 additions & 1 deletion app/controllers/sign_up/completions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def completions_presenter
current_sp: current_sp,
decrypted_pii: pii,
requested_attributes: decorated_session.requested_attributes.map(&:to_sym),
ial2_requested: sp_session[:ial2] || sp_session[:ialmax],
ial2_requested: ial2_requested?,
completion_context: needs_completion_screen_reason,
)
end
Expand All @@ -56,6 +56,14 @@ def ial2?
sp_session[:ial2]
end

def ial_max?
sp_session[:ialmax]
end

def ial2_requested?
!!(ial2? || (ial_max? && current_user.identity_verified?))
end

def return_to_account
track_completion_event('account-page')
redirect_to account_url
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/users/piv_cac_login_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ def next_step
end

def ial_context
@ial_context ||= IalContext.new(ial: sp_session_ial, service_provider: current_sp)
@ial_context ||= IalContext.new(
ial: sp_session_ial,
service_provider: current_sp,
user: piv_cac_login_form.user,
)
end

def process_invalid_submission
Expand Down
10 changes: 9 additions & 1 deletion app/services/id_token_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,22 @@ def timestamp_claims
def acr
ial = identity.ial
case ial
when Idp::Constants::IAL_MAX then Saml::Idp::Constants::IALMAX_AUTHN_CONTEXT_CLASSREF
when Idp::Constants::IAL_MAX then determine_ial_max_acr
when Idp::Constants::IAL1 then Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF
when Idp::Constants::IAL2 then Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF
else
raise "Unknown ial #{ial}"
end
end

def determine_ial_max_acr
if identity.user.identity_verified?
Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF
else
Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF
end
end

def expires
now.to_i + ttl
end
Expand Down
63 changes: 59 additions & 4 deletions spec/controllers/sign_up/completions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@
end

context 'IAL1' do
it 'tracks page visit' do
user = create(:user)
let(:user) { create(:user) }
before do
stub_sign_in(user)
subject.session[:sp] = {
issuer: current_sp.issuer,
Expand All @@ -32,7 +32,9 @@
request_url: 'http://localhost:3000',
}
get :show
end

it 'tracks page visit' do
expect(@analytics).to have_received(:track_event).with(
'User registration: agency handoff visited',
ial2: false,
Expand All @@ -44,6 +46,10 @@
sp_session_requested_attributes: [:email],
)
end

it 'creates a presenter object that is not requesting ial2' do
expect(assigns(:presenter).ial2_requested?).to eq false
end
end

context 'IAL2' do
Expand All @@ -61,11 +67,10 @@
request_url: 'http://localhost:3000',
}
allow(controller).to receive(:user_session).and_return('decrypted_pii' => pii.to_json)
get :show
end

it 'tracks page visit' do
get :show

expect(@analytics).to have_received(:track_event).with(
'User registration: agency handoff visited',
ial2: true,
Expand All @@ -77,6 +82,56 @@
sp_session_requested_attributes: [:email],
)
end

it 'creates a presenter object that is requesting ial2' do
expect(assigns(:presenter).ial2_requested?).to eq true
end
end

context 'IALMax' do
let(:user) do
create(:user, profiles: [create(:profile, :verified, :active)])
end
let(:pii) { { ssn: '123456789' } }

before do
stub_sign_in(user)
subject.session[:sp] = {
issuer: current_sp.issuer,
ial2: false,
ialmax: true,
requested_attributes: [:email],
request_url: 'http://localhost:3000',
}
allow(controller).to receive(:user_session).and_return('decrypted_pii' => pii.to_json)
get :show
end

it 'tracks page visit' do
expect(@analytics).to have_received(:track_event).with(
'User registration: agency handoff visited',
ial2: false,
ialmax: true,
service_provider_name: subject.decorated_session.sp_name,
page_occurence: '',
needs_completion_screen_reason: :new_sp,
sp_request_requested_attributes: nil,
sp_session_requested_attributes: [:email],
)
end

context 'verified user' do
it 'creates a presenter object that is requesting ial2' do
expect(assigns(:presenter).ial2_requested?).to eq true
end
end

context 'unverified user' do
let(:user) { create(:user) }
it 'creates a presenter object that is requesting ial2' do
expect(assigns(:presenter).ial2_requested?).to eq false
end
end
end
end

Expand Down
220 changes: 220 additions & 0 deletions spec/controllers/users/piv_cac_login_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
require 'rails_helper'

describe Users::PivCacLoginController do
describe 'GET new' do
before do
stub_analytics
allow(@analytics).to receive(:track_event)
end

context 'without a token' do
before { get :new }

it 'tracks the piv_cac setup' do
expect(@analytics).to have_received(:track_event).with(
'User Registration: piv cac setup visited',
)
end

it 'redirects to root url' do
expect(response).to render_template(:new)
end
end

context 'with a token' do
let(:token) { 'TEST:abcdefg' }

context 'an invalid token' do
before { get :new, params: { token: token } }
it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
'PIV/CAC Login',
{
errors: {},
key_id: nil,
success: false,
},
)
end

it 'redirects to the error url' do
expect(response).to redirect_to(login_piv_cac_error_url(error: 'token.bad'))
end
end

context 'with a valid token' do
let(:service_provider) { create(:service_provider) }
let(:sp_session) { { ial: 1, issuer: service_provider.issuer } }
let(:nonce) { SecureRandom.base64(20) }
let(:data) do
{
nonce: nonce,
uuid: '1234',
subject: 'subject',
issuer: 'issuer',
}.with_indifferent_access
end

before do
subject.piv_session[:piv_cac_nonce] = nonce
subject.session[:sp] = sp_session

allow(PivCacService).to receive(:decode_token).with(token) { data }
get :new, params: { token: token }
end

context 'without a valid user' do
before do
# valid_token? is being called twice, once to determine if it's a valid submission
# and once to set the session variable in process_invalid_submission
# good opportunity for a refactor
expect(PivCacService).to have_received(:decode_token).with(token) { data }.twice
end

it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
'PIV/CAC Login',
{
errors: {
type: 'user.not_found',
},
key_id: nil,
success: false,
},
)
end

it 'sets the session variable' do
expect(subject.session[:needs_to_setup_piv_cac_after_sign_in]).to be true
end

it 'redirects to the error url' do
expect(response).to redirect_to(login_piv_cac_error_url(error: 'user.not_found'))
end
end

context 'with a valid user' do
let(:user) { build(:user) }
let(:piv_cac_config) { create(:piv_cac_configuration, user: user) }
let(:data) do
{
nonce: nonce,
uuid: piv_cac_config.x509_dn_uuid,
subject: 'subject',
issuer: 'issuer',
}.with_indifferent_access
end

before do
expect(PivCacService).to have_received(:decode_token).with(token) { data }
sign_in user
end

it 'tracks the login attempt' do
expect(@analytics).to have_received(:track_event).with(
'PIV/CAC Login',
{
errors: {},
key_id: nil,
success: true,
},
)
end

it 'sets the session correctly' do
expect(controller.user_session[TwoFactorAuthenticatable::NEED_AUTHENTICATION]).
to eq false

expect(controller.user_session[:authn_at]).to_not be nil
expect(controller.user_session[:authn_at].class).to eq ActiveSupport::TimeWithZone
end

it 'tracks the user_marked_authed event' do
expect(@analytics).to have_received(:track_event).with(
'User marked authenticated',
{ authentication_type: :piv_cac },
)
end

it 'saves the piv_cac session information' do
session_info = {
subject: data[:subject],
issuer: data[:issuer],
presented: true,
}
expect(controller.user_session[:decrypted_x509]).to eq session_info.to_json
end

describe 'it handles the otp_context' do
it 'sets the otp user_session' do
expect(controller.user_session[:auth_method]).
to eq TwoFactorAuthenticatable::AuthMethod::PIV_CAC

expect(controller.user_session[:unconfirmed_phone]).to be nil
expect(controller.user_session[:context]).to eq 'authentication'
end

it 'tracks the user_marked_authed event' do
expect(@analytics).to have_received(:track_event).with(
'User marked authenticated',
{ authentication_type: :valid_2fa },
)
end

context 'ial1 user' do
it 'redirects to the after_sign_in_path_for' do
expect(response).to redirect_to(account_url)
end

context 'ial_max service level' do
let(:sp_session) do
{ ial: Idp::Constants::IAL_MAX, issuer: service_provider.issuer }
end

it 'redirects to the after_sign_in_path_for' do
expect(response).to redirect_to(account_url)
end
end
end

context 'ial2 user' do
let(:user) { create(:user, profiles: [create(:profile, :verified, :active)]) }

context 'ial1 service level' do
it 'redirects to the after_sign_in_path_for' do
expect(response).to redirect_to(account_url)
end
end

context 'ial2 service_level' do
let(:sp_session) { { ial: Idp::Constants::IAL2, issuer: service_provider.issuer } }

it 'redirects to the capture_password_url' do
expect(response).to redirect_to(capture_password_url)
end
end

context 'ial_max service_level' do
let(:sp_session) do
{ ial: Idp::Constants::IAL_MAX, issuer: service_provider.issuer }
end

it 'redirects to the capture_password_url' do
expect(response).to redirect_to(capture_password_url)
end
end
end
end
end
end
end
end

describe 'GET error' do
before { get :error, params: { error: 'token.bad' } }

it 'sends the error to the error presenter' do
expect(assigns(:presenter).error).to eq 'token.bad'
end
end
end
Loading