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
13 changes: 4 additions & 9 deletions spec/features/openid_connect/vtr_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,10 @@
expect(page).to have_content(t('two_factor_authentication.two_factor_hspd12_choice_intro'))

# User must setup PIV/CAC before continuing
visit setup_piv_cac_path

nonce = piv_cac_nonce_from_form_action
visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
uuid: SecureRandom.uuid,
subject: 'SomeIgnoredSubject',
)
select_2fa_option('piv_cac')
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

click_agree_and_continue
click_on 'Continue'
Expand Down
13 changes: 4 additions & 9 deletions spec/features/saml/vtr_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,11 @@
expect(page).to have_content(t('two_factor_authentication.two_factor_hspd12_choice_intro'))

# User must setup PIV/CAC before continuing
visit setup_piv_cac_path
nonce = piv_cac_nonce_from_form_action
visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
uuid: SecureRandom.uuid,
subject: 'SomeIgnoredSubject',
)
select_2fa_option('piv_cac')
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

click_submit_default
click_agree_and_continue
click_submit_default
expect_successful_saml_redirect
Expand Down
24 changes: 6 additions & 18 deletions spec/features/two_factor_authentication/sign_in_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -405,36 +405,24 @@ def attempt_to_bypass_2fa
end

scenario 'user uses PIV/CAC as their second factor' do
stub_piv_cac_service

user = user_with_piv_cac
sign_in_before_2fa(user)
stub_piv_cac_service(uuid: user.piv_cac_configurations.first.x509_dn_uuid)

nonce = visit_login_two_factor_piv_cac_and_get_nonce
click_on t('forms.piv_cac_mfa.submit')
follow_piv_cac_redirect

visit_piv_cac_service(
login_two_factor_piv_cac_path,
uuid: user.piv_cac_configurations.first.x509_dn_uuid,
dn: 'C=US, O=U.S. Government, OU=DoD, OU=PKI, CN=DOE.JOHN.1234',
nonce: nonce,
)
expect(current_path).to eq account_path
end

scenario 'user uses incorrect PIV/CAC as their second factor' do
stub_piv_cac_service

user = user_with_piv_cac
sign_in_before_2fa(user)
stub_piv_cac_service(uuid: Random.uuid)

nonce = visit_login_two_factor_piv_cac_and_get_nonce
click_on t('forms.piv_cac_mfa.submit')
follow_piv_cac_redirect

visit_piv_cac_service(
login_two_factor_piv_cac_path,
uuid: user.piv_cac_configurations.first.x509_dn_uuid + 'X',
dn: 'C=US, O=U.S. Government, OU=DoD, OU=PKI, CN=DOE.JOHN.12345',
nonce: nonce,
)
expect(current_path).to eq login_two_factor_piv_cac_path
expect(page).to have_content(t('two_factor_authentication.invalid_piv_cac'))
end
Expand Down
52 changes: 18 additions & 34 deletions spec/features/users/piv_cac_management_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
allow(Identity::Hostdata).to receive(:env).and_return('test')
allow(Identity::Hostdata).to receive(:domain).and_return('example.com')

stub_piv_cac_service
stub_piv_cac_service(uuid:)

sign_in_and_2fa_user(user)
visit account_two_factor_authentication_path
Expand All @@ -18,14 +18,9 @@
expect(page.response_headers['Content-Security-Policy'].split(';').map(&:strip)).
to(include("form-action https://*.pivcac.test.example.com 'self'"))

nonce = piv_cac_nonce_from_form_action

visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
uuid: uuid,
subject: 'SomeIgnoredSubject',
)
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

expect(current_path).to eq account_path
visit account_two_factor_authentication_path
Expand Down Expand Up @@ -56,20 +51,15 @@
end

scenario 'disallows association of a piv/cac with the same name' do
stub_piv_cac_service
stub_piv_cac_service(uuid:)

sign_in_and_2fa_user(user)
visit account_two_factor_authentication_path
click_link t('account.index.piv_cac_add'), href: setup_piv_cac_url

nonce = piv_cac_nonce_from_form_action

visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
uuid: uuid,
subject: 'SomeIgnoredSubject',
)
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

expect(current_path).to eq account_path

Expand All @@ -83,19 +73,16 @@
end

scenario 'displays error for piv/cac with no certificate and accepts more error info' do
stub_piv_cac_service
stub_piv_cac_service(error: 'certificate.none')

sign_in_and_2fa_user(user)
visit account_two_factor_authentication_path
click_link t('account.index.piv_cac_add'), href: setup_piv_cac_url

nonce = piv_cac_nonce_from_form_action
visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
error: 'certificate.none',
key_id: 'AB:CD:EF',
)
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

expect(current_path).to eq setup_piv_cac_error_path
expect(page).to have_link(t('instructions.mfa.piv_cac.try_again'), href: setup_piv_cac_url)
expect(page).to have_content(
Expand All @@ -107,19 +94,16 @@
end

scenario 'displays error for expires certificate piv/cac and accepts more error info' do
stub_piv_cac_service
stub_piv_cac_service(error: 'certificate.expired')

sign_in_and_2fa_user(user)
visit account_two_factor_authentication_path
click_link t('account.index.piv_cac_add'), href: setup_piv_cac_url

nonce = piv_cac_nonce_from_form_action
visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
error: 'certificate.expired',
key_id: 'AB:CD:EF',
)
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect

expect(current_path).to eq setup_piv_cac_error_path
expect(page).to have_link(
t('instructions.mfa.piv_cac.please_try_again'),
Expand Down
10 changes: 2 additions & 8 deletions spec/features/users/sign_in_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,8 @@
allow(FeatureManagement).to receive(:development_and_identity_pki_disabled?).and_return(false)

stub_piv_cac_service
nonce = get_piv_cac_nonce_from_link(find_link(t('forms.piv_cac_login.submit')))
visit_piv_cac_service(
current_url,
nonce: nonce,
dn: 'C=US, O=U.S. Government, OU=DoD, OU=PKI, CN=DOE.JOHN.1234',
uuid: SecureRandom.uuid,
subject: 'SomeIgnoredSubject',
)
click_on t('forms.piv_cac_login.submit')
follow_piv_cac_redirect

expect(page).to have_current_path(login_piv_cac_error_path(error: 'user.not_found'))
visit sign_up_email_path
Expand Down
71 changes: 11 additions & 60 deletions spec/support/features/session_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,21 +64,14 @@ def signin_with_piv(user = user_with_piv_cac)
end

def signin_with_piv_error(error)
user = user_with_piv_cac
visit new_user_session_path
click_on t('account.login.piv_cac')

allow(FeatureManagement).to receive(:development_and_identity_pki_disabled?).and_return(false)

stub_piv_cac_service
nonce = get_piv_cac_nonce_from_link(find_link(t('forms.piv_cac_login.submit')))
visit_piv_cac_service(
current_url,
nonce: nonce,
uuid: user.piv_cac_configurations.first.x509_dn_uuid,
subject: 'SomeIgnoredSubject',
error: error,
)
stub_piv_cac_service(error:)
click_on t('forms.piv_cac_login.submit')
follow_piv_cac_redirect
end

def signin_with_bad_piv
Expand All @@ -93,14 +86,9 @@ def fill_in_piv_cac_credentials_and_submit(user,
piv_cac_configurations&.first&.x509_dn_uuid)
allow(FeatureManagement).to receive(:development_and_identity_pki_disabled?).and_return(false)

stub_piv_cac_service
nonce = get_piv_cac_nonce_from_link(find_link(t('forms.piv_cac_login.submit')))
visit_piv_cac_service(
current_url,
nonce: nonce,
uuid: uuid,
subject: 'SomeIgnoredSubject',
)
stub_piv_cac_service(uuid:)
click_on t('forms.piv_cac_login.submit')
follow_piv_cac_redirect
end

def fill_in_bad_piv_cac_credentials_and_submit
Expand Down Expand Up @@ -537,16 +525,9 @@ def register_user_with_piv_cac(email = 'test@test.com')
def set_up_2fa_with_piv_cac
stub_piv_cac_service
select_2fa_option('piv_cac')

expect(page).to have_current_path setup_piv_cac_path

nonce = piv_cac_nonce_from_form_action
visit_piv_cac_service(
setup_piv_cac_url,
nonce: nonce,
uuid: SecureRandom.uuid,
subject: 'SomeIgnoredSubject',
)
fill_in t('instructions.mfa.piv_cac.step_1'), with: 'Card'
click_on t('forms.piv_cac_setup.submit')
follow_piv_cac_redirect
end

def skip_second_mfa_prompt
Expand All @@ -559,7 +540,7 @@ def sign_in_via_branded_page(user)
click_submit_default
end

def stub_piv_cac_service(error: nil)
def stub_piv_cac_service(error: nil, uuid: Random.uuid)
allow(IdentityConfig.store).to receive(:identity_pki_disabled).and_return(false)
allow(IdentityConfig.store).to receive(:piv_cac_service_url).
and_return('http://piv.example.com/')
Expand All @@ -582,7 +563,7 @@ def stub_piv_cac_service(error: nil)
query['redirect_uri'],
token: {
dn: 'C=US, O=U.S. Government, OU=DoD, OU=PKI, CN=DOE.JOHN.1234',
uuid: SecureRandom.uuid,
uuid:,
subject: 'SomeIgnoredSubject',
nonce: query['nonce'],
error:,
Expand All @@ -600,36 +581,6 @@ def follow_piv_cac_redirect
visit redirect_url
end

def visit_piv_cac_service(idp_url, token_data)
visit(idp_url + '?token=' + CGI.escape(token_data.to_json))
end

def visit_login_two_factor_piv_cac_and_get_nonce
visit login_two_factor_piv_cac_path
get_piv_cac_nonce_from_link(find_link(t('forms.piv_cac_mfa.submit')))
end

# This is a bit convoluted because we generate a nonce when we visit the
# link. The link provides a redirect to the piv/cac service with the nonce.
# This way, even if JavaScript fetches the link to grab the nonce, a new nonce
# is generated when the user clicks on the link.
def get_piv_cac_nonce_from_link(link)
go_back = current_path
visit link['href']
nonce = Rack::Utils.parse_nested_query(URI(current_url).query)['nonce']
visit go_back
nonce
end

def piv_cac_nonce_from_form_action
go_back = current_path
fill_in 'name', with: 'Card ' + SecureRandom.uuid
click_button t('forms.piv_cac_setup.submit')
nonce = Rack::Utils.parse_nested_query(URI(current_url).query)['nonce']
visit go_back
nonce
end

def link_identity(user, service_provider, ial = nil)
IdentityLinker.new(
user,
Expand Down