diff --git a/app/forms/openid_connect_authorize_form.rb b/app/forms/openid_connect_authorize_form.rb index 6c423678a65..ec9d57761b3 100644 --- a/app/forms/openid_connect_authorize_form.rb +++ b/app/forms/openid_connect_authorize_form.rb @@ -61,10 +61,6 @@ def submit FormResponse.new(success: success, errors: errors.messages, extra: extra_analytics_attributes) end - def aal3_requested? - aal == Authorization::AAL3 - end - def verified_at_requested? scope.include?('profile:verified_at') end diff --git a/app/policies/aal3_policy.rb b/app/policies/aal3_policy.rb index 757ecb7ec90..b7e40fa001e 100644 --- a/app/policies/aal3_policy.rb +++ b/app/policies/aal3_policy.rb @@ -24,6 +24,8 @@ def aal3_configured_but_not_used? !aal3_used? end + private + def aal3_requested? return false unless @session diff --git a/spec/forms/openid_connect_authorize_form_spec.rb b/spec/forms/openid_connect_authorize_form_spec.rb index 91e8061bbed..0fb1c58b7db 100644 --- a/spec/forms/openid_connect_authorize_form_spec.rb +++ b/spec/forms/openid_connect_authorize_form_spec.rb @@ -381,49 +381,6 @@ end end - describe '#aal3_requested?' do - subject(:aal3_requested?) { form.aal3_requested? } - context 'with only ial1' do - let(:acr_values) { Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF } - it { expect(aal3_requested?).to eq(false) } - end - - context 'with only ial2' do - let(:acr_values) { Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF } - it { expect(aal3_requested?).to eq(false) } - end - - context 'with only aal3' do - let(:acr_values) { Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF } - it { expect(aal3_requested?).to eq(true) } - end - - context 'with ial1 and aal3' do - let(:acr_values) do - [ - Saml::Idp::Constants::IAL1_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, - ].join(' ') - end - it { expect(aal3_requested?).to eq(true) } - end - - context 'with ial2 and aal3' do - let(:acr_values) do - [ - Saml::Idp::Constants::IAL2_AUTHN_CONTEXT_CLASSREF, - Saml::Idp::Constants::AAL3_AUTHN_CONTEXT_CLASSREF, - ].join(' ') - end - it { expect(aal3_requested?).to eq(true) } - end - - context 'with a malformed ial' do - let(:acr_values) { 'foobarbaz' } - it { expect(aal3_requested?).to eq(false) } - end - end - describe '#client_id' do it 'returns the form client_id' do form = OpenidConnectAuthorizeForm.new(client_id: 'foobar') diff --git a/spec/policies/aal3_policy_spec.rb b/spec/policies/aal3_policy_spec.rb new file mode 100644 index 00000000000..6f281f4c0d9 --- /dev/null +++ b/spec/policies/aal3_policy_spec.rb @@ -0,0 +1,121 @@ +require 'rails_helper' + +describe AAL3Policy do + let(:service_provider) { create(:service_provider, aal: 1) } + let(:sp_session) { { issuer: service_provider.issuer, aal_level_requested: 1 } } + let(:session) { { auth_method: 'phone', sp: sp_session } } + let(:user) { create(:user) } + + describe '#aal3_required?' do + it 'is false if the SP did not request and does not require AAL3' do + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required?).to eq(false) + end + + it 'is true if the SP requested AAL3' do + sp_session[:aal_level_requested] = 3 + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required?).to eq(true) + end + + it 'is true if the SP is configured for AAL3 only' do + service_provider.update!(aal: 3) + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required?).to eq(true) + end + + it 'is false if there is no SP' do + session[:sp] = nil + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required?).to eq(false) + end + end + + describe '#aal3_used?' do + it 'returns true if the user used PIV/CAC for MFA' do + session[:auth_method] = 'piv_cac' + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_used?).to eq(true) + end + + it 'returns true if the user used webauthn for MFA' do + session[:auth_method] = 'webauthn' + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_used?).to eq(true) + end + + it 'returns false if the user has not used an AAL3 method for MFA' do + session[:auth_method] = 'phone' + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_used?).to eq(false) + end + end + + describe '#aal3_required_but_not_used?' do + it 'returns false if AAL3 is not required' do + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required_but_not_used?).to eq(false) + end + + it 'returns true if AAL3 is required and was not used to sign in' do + sp_session[:aal_level_requested] = 3 + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required_but_not_used?).to eq(true) + end + + it 'returns false if AAL3 is required and was used to sign in' do + sp_session[:aal_level_requested] = 3 + session[:auth_method] = 'webauthn' + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_required_but_not_used?).to eq(false) + end + end + + describe '#aal3_configured_but_not_used?' do + it 'returns false if AAL3 is configured and not required and not used' do + configure_aal3_for_user(user) + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_configured_but_not_used?).to eq(false) + end + + it 'returns false if AAL3 is not configured' do + sp_session[:aal_level_requested] = 3 + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_configured_but_not_used?).to eq(false) + end + + it 'returns true if AAL3 is configured and was not used to sign in' do + sp_session[:aal_level_requested] = 3 + configure_aal3_for_user(user) + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_configured_but_not_used?).to eq(true) + end + + it 'returns false if AAL3 is configured and was used to sign in' do + sp_session[:aal_level_requested] = 3 + configure_aal3_for_user(user) + session[:auth_method] = 'webauthn' + aal3_policy = AAL3Policy.new(session: session, user: user) + + expect(aal3_policy.aal3_configured_but_not_used?).to eq(false) + end + end + + def configure_aal3_for_user(user) + user.webauthn_configurations << create(:webauthn_configuration) + user.save! + end +end