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
8 changes: 5 additions & 3 deletions app/controllers/concerns/saml_idp_auth_concern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,10 @@ def default_ial_context
end
end

def requested_aal_authn_context
saml_request.requested_aal_authn_context || default_aal_context
def response_authn_context
saml_request.requested_vtr_authn_context ||
saml_request.requested_aal_authn_context ||
default_aal_context
end

def requested_ial_authn_context
Expand Down Expand Up @@ -186,7 +188,7 @@ def saml_response
encode_response(
current_user,
name_id_format: name_id_format,
authn_context_classref: requested_aal_authn_context,
authn_context_classref: response_authn_context,
reference_id: active_identity.session_uuid,
encryption: encryption_opts,
signature: saml_response_signature_options,
Expand Down
1 change: 1 addition & 0 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ def log_external_saml_auth_request
analytics.saml_auth_request(
requested_ial: requested_ial,
requested_aal_authn_context: saml_request&.requested_aal_authn_context,
requested_vtr_authn_context: saml_request&.requested_vtr_authn_context,
force_authn: saml_request&.force_authn?,
final_auth_request: sp_session[:final_auth_request],
service_provider: saml_request&.issuer,
Expand Down
44 changes: 30 additions & 14 deletions app/services/attribute_asserter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,14 @@ def build
add_email(attrs) if bundle.include? :email
add_all_emails(attrs) if bundle.include? :all_emails
add_bundle(attrs) if should_add_proofed_attributes?
add_verified_at(attrs) if bundle.include?(:verified_at) && ial_context.ial2_service_provider?
add_aal(attrs)
add_ial(attrs) if authn_request.requested_ial_authn_context || !service_provider.ial.nil?
add_verified_at(attrs) if bundle.include?(:verified_at) && ial2_service_provider?
if authn_request.requested_vtr_authn_context.present?
add_vot(attrs)
else
add_aal(attrs)
add_ial(attrs) if authn_request.requested_ial_authn_context || !service_provider.ial.nil?
end

add_x509(attrs) if bundle.include?(:x509_presented) && x509_data
user.asserted_attributes = attrs
end
Expand All @@ -53,20 +58,22 @@ def build

def should_add_proofed_attributes?
return false if !user.active_profile.present?
ial_context.ial2_or_greater? || ial_max_requested?
resolved_authn_context_result.identity_proofing_or_ialmax?
end

def ial_max_requested?
ial_acr_value = FederatedProtocols::Saml.new(authn_request).ial
Vot::LegacyComponentValues.by_name[ial_acr_value]&.requirements&.include?(:ialmax)
def ial2_service_provider?
service_provider.ial.to_i >= ::Idp::Constants::IAL2
end

def ial_context
@ial_context ||= IalContext.new(
ial: authn_context,
service_provider: service_provider,
user: user,
)
def resolved_authn_context_result
@resolved_authn_context_result ||= begin
saml = FederatedProtocols::Saml.new(authn_request)
AuthnContextResolver.new(
service_provider: service_provider,
vtr: saml.vtr,
acr_values: saml.acr_values,
).resolve
end
end

def default_attrs
Expand Down Expand Up @@ -125,6 +132,11 @@ def add_verified_at(attrs)
attrs[:verified_at] = { getter: verified_at_getter_function }
end

def add_vot(attrs)
context = resolved_authn_context_result.component_values.map(&:name).join('.')
attrs[:vot] = { getter: vot_getter_function(context) }
end

def add_aal(attrs)
requested_context = authn_request.requested_aal_authn_context
requested_aal_level = Saml::Idp::Constants::AUTHN_CONTEXT_CLASSREF_TO_AAL[requested_context]
Expand All @@ -145,7 +157,7 @@ def add_ial(attrs)
end

def ialmax_requested_and_fullfilable?
ial_max_requested? && user.active_profile.present?
resolved_authn_context_result.ialmax? && user.active_profile.present?
end

def sp_ial
Expand All @@ -169,6 +181,10 @@ def verified_at_getter_function
->(principal) { principal.active_profile&.verified_at&.iso8601 }
end

def vot_getter_function(vot_authn_context)
->(_principal) { vot_authn_context }
end

def aal_getter_function(aal_authn_context)
->(_principal) { aal_authn_context }
end
Expand Down
44 changes: 43 additions & 1 deletion spec/features/saml/vtr_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
expect_successful_saml_redirect

xmldoc = SamlResponseDoc.new('feature', 'response_assertion')
email = xmldoc.attribute_node_for('email').children.map(&:text).join
expect(xmldoc.assertion_statement_node.content).to eq('C1')
expect(xmldoc.attribute_node_for('vot').content).to eq('C1')
expect(xmldoc.attribute_node_for('ial')).to be_nil
expect(xmldoc.attribute_node_for('aal')).to be_nil

email = xmldoc.attribute_node_for('email').content
expect(user.email_addresses.first.email).to eq(email)
end

Expand Down Expand Up @@ -149,6 +153,44 @@
expect_successful_saml_redirect
end

scenario 'sign in with VTR request for idv includes proofed attributes' do
pii = {
first_name: 'Jonathan',
ssn: '900-66-6666',
}
user = create(:user, :fully_registered)
create(:profile, :active, user: user, pii: pii)

visit_saml_authn_request_url(
overrides: {
issuer: sp1_issuer,
authn_context: [
'C1.C2.P1',
"#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}first_name",
"#{Saml::Idp::Constants::REQUESTED_ATTRIBUTES_CLASSREF}ssn",
],
},
)
sign_in_live_with_2fa(user)
click_submit_default
click_agree_and_continue
click_submit_default

expect_successful_saml_redirect

xmldoc = SamlResponseDoc.new('feature', 'response_assertion')
expect(xmldoc.assertion_statement_node.content).to eq('C1.C2.P1')
expect(xmldoc.attribute_node_for('vot').content).to eq('C1.C2.P1')
expect(xmldoc.attribute_node_for('ial')).to be_nil
expect(xmldoc.attribute_node_for('aal')).to be_nil

first_name = xmldoc.attribute_node_for('first_name').content
ssn = xmldoc.attribute_node_for('ssn').content

expect(first_name).to eq(pii[:first_name])
expect(ssn).to eq(pii[:ssn])
end

scenario 'sign in with VTR request for idv with biometric requires idv with biometric', :js do
allow(IdentityConfig.store).to receive(:doc_auth_selfie_capture_enabled).and_return(true)

Expand Down
79 changes: 79 additions & 0 deletions spec/services/attribute_asserter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@
)
CGI.unescape ial1_authn_request_url.split('SAMLRequest').last
end
let(:raw_vtr_no_proofing_authn_request) do
vtr_proofing_authn_request = saml_authn_request_url(
overrides: {
issuer: sp1_issuer,
authn_context: 'C1.C2',
},
)
CGI.unescape vtr_proofing_authn_request.split('SAMLRequest').last
end
let(:raw_ial2_authn_request) do
ial2_authnrequest = saml_authn_request_url(
overrides: {
Expand All @@ -59,6 +68,15 @@
)
CGI.unescape ial2_authnrequest.split('SAMLRequest').last
end
let(:raw_vtr_proofing_authn_request) do
vtr_proofing_authn_request = saml_authn_request_url(
overrides: {
issuer: sp1_issuer,
authn_context: 'C1.C2.P1',
},
)
CGI.unescape vtr_proofing_authn_request.split('SAMLRequest').last
end
let(:raw_ial1_aal3_authn_request) do
ial1_aal3_authnrequest = saml_authn_request_url(
overrides: {
Expand Down Expand Up @@ -95,6 +113,12 @@
let(:ial2_authn_request) do
SamlIdp::Request.from_deflated_request(raw_ial2_authn_request)
end
let(:vtr_proofing_authn_request) do
SamlIdp::Request.from_deflated_request(raw_vtr_proofing_authn_request)
end
let(:vtr_no_proofing_authn_request) do
SamlIdp::Request.from_deflated_request(raw_vtr_no_proofing_authn_request)
end
let(:ial1_aal3_authn_request) do
SamlIdp::Request.from_deflated_request(raw_ial1_aal3_authn_request)
end
Expand Down Expand Up @@ -295,6 +319,34 @@
end
end

context 'verified user and proofing VTR request' do
let(:subject) do
described_class.new(
user: user,
name_id_format: name_id_format,
service_provider: service_provider,
authn_request: vtr_proofing_authn_request,
decrypted_pii: decrypted_pii,
user_session: user_session,
)
end

before do
user.identities << identity
allow(service_provider.metadata).to receive(:[]).with(:attribute_bundle).
and_return(%w[email first_name last_name])
subject.build
end

it 'includes the correct bundle attributes' do
expect(user.asserted_attributes.keys).to eq(
[:uuid, :email, :first_name, :last_name, :verified_at, :vot],
)
expect(user.asserted_attributes[:first_name][:getter].call(user)).to eq 'Jåné'
expect(user.asserted_attributes[:vot][:getter].call(user)).to eq 'C1.C2.P1'
end
end

context 'verified user and IAL1 request' do
let(:subject) do
described_class.new(
Expand Down Expand Up @@ -454,6 +506,33 @@
end
end
end

context 'request made with a VTR param' do
let(:subject) do
described_class.new(
user: user,
name_id_format: name_id_format,
service_provider: service_provider,
authn_request: vtr_no_proofing_authn_request,
decrypted_pii: decrypted_pii,
user_session: user_session,
)
end

before do
user.identities << identity
allow(service_provider.metadata).to receive(:[]).with(:attribute_bundle).
and_return(%w[email])
subject.build
end

it 'includes the correct bundle attributes' do
expect(user.asserted_attributes.keys).to eq(
[:uuid, :email, :vot],
)
expect(user.asserted_attributes[:vot][:getter].call(user)).to eq 'C1.C2'
end
end
end

context 'verified user and IAL1 AAL3 request' do
Expand Down