Skip to content

Commit 5722ad0

Browse files
author
gene
committed
added SubjectConfirmation Recipient validation
1 parent a3bfd52 commit 5722ad0

File tree

2 files changed

+25
-10
lines changed

2 files changed

+25
-10
lines changed

lib/onelogin/ruby-saml/response.rb

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class Response < SamlMessage
3232

3333
# Constructs the SAML Response. A Response Object that is an extension of the SamlMessage class.
3434
# @param response [String] A UUEncoded SAML response from the IdP.
35-
# @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object
35+
# @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object
3636
# Or some options for the response validation process like skip the conditions validation
3737
# with the :skip_conditions, or allow a clock_drift when checking dates with :allowed_clock_drift
3838
# or :matches_request_id that will validate that the response matches the ID of the request,
@@ -189,7 +189,7 @@ def session_expires_at
189189

190190
# Checks if the Status has the "Success" code
191191
# @return [Boolean] True if the StatusCode is Sucess
192-
#
192+
#
193193
def success?
194194
status_code == "urn:oasis:names:tc:SAML:2.0:status:Success"
195195
end
@@ -376,15 +376,15 @@ def validate(collect_errors = false)
376376
#
377377
def validate_success_status
378378
return true if success?
379-
379+
380380
error_msg = 'The status code of the Response was not Success'
381381
status_error_msg = OneLogin::RubySaml::Utils.status_error_msg(error_msg, status_code, status_message)
382382
append_error(status_error_msg)
383383
end
384384

385385
# Validates the SAML Response against the specified schema.
386386
# @return [Boolean] True if the XML is valid, otherwise False if soft=True
387-
# @raise [ValidationError] if soft == false and validation fails
387+
# @raise [ValidationError] if soft == false and validation fails
388388
#
389389
def validate_structure
390390
structure_error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
@@ -417,7 +417,7 @@ def validate_response_state
417417
true
418418
end
419419

420-
# Validates that the SAML Response contains an ID
420+
# Validates that the SAML Response contains an ID
421421
# If fails, the error is added to the errors array.
422422
# @return [Boolean] True if the SAML Response contains an ID, otherwise returns False
423423
#
@@ -706,7 +706,7 @@ def validate_session_expiration(soft = true)
706706
end
707707

708708
# Validates if exists valid SubjectConfirmation (If the response was initialized with the :allowed_clock_drift option,
709-
# timimg validation are relaxed by the allowed_clock_drift value. If the response was initialized with the
709+
# timimg validation are relaxed by the allowed_clock_drift value. If the response was initialized with the
710710
# :skip_subject_confirmation option, this validation is skipped)
711711
# If fails, the error is added to the errors array
712712
# @return [Boolean] True if exists a valid SubjectConfirmation, otherwise False if soft=True
@@ -717,7 +717,7 @@ def validate_subject_confirmation
717717
valid_subject_confirmation = false
718718

719719
subject_confirmation_nodes = xpath_from_signed_assertion('/a:Subject/a:SubjectConfirmation')
720-
720+
721721
now = Time.now.utc
722722
subject_confirmation_nodes.each do |subject_confirmation|
723723
if subject_confirmation.attributes.include? "Method" and subject_confirmation.attributes['Method'] != 'urn:oasis:names:tc:SAML:2.0:cm:bearer'
@@ -735,8 +735,9 @@ def validate_subject_confirmation
735735
attrs = confirmation_data_node.attributes
736736
next if (attrs.include? "InResponseTo" and attrs['InResponseTo'] != in_response_to) ||
737737
(attrs.include? "NotOnOrAfter" and (parse_time(confirmation_data_node, "NotOnOrAfter") + allowed_clock_drift) <= now) ||
738-
(attrs.include? "NotBefore" and parse_time(confirmation_data_node, "NotBefore") > (now + allowed_clock_drift))
739-
738+
(attrs.include? "NotBefore" and parse_time(confirmation_data_node, "NotBefore") > (now + allowed_clock_drift)) ||
739+
(attrs.include? "Recipient" and settings.assertion_consumer_service_url != nil and attrs['Recipient'] != settings.assertion_consumer_service_url)
740+
740741
valid_subject_confirmation = true
741742
break
742743
end
@@ -808,7 +809,7 @@ def validate_signature
808809
opts[:cert] = settings.get_idp_cert
809810
fingerprint = settings.get_fingerprint
810811

811-
unless fingerprint && doc.validate_document(fingerprint, @soft, opts)
812+
unless fingerprint && doc.validate_document(fingerprint, @soft, opts)
812813
return append_error(error_msg)
813814
end
814815

test/response_test.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,20 @@ class RubySamlTest < Minitest::Test
676676
assert_includes response_invalid_subjectconfirmation_noa.errors, "A valid SubjectConfirmation was not found on this Response"
677677
end
678678

679+
it "return true when valid subject confirmation recipient" do
680+
response_valid_signed.settings = settings
681+
response_valid_signed.settings.assertion_consumer_service_url= 'recipient'
682+
assert response_valid_signed.send(:validate_subject_confirmation)
683+
assert_empty response_valid_signed.errors
684+
end
685+
686+
it "return false when valid subject confirmation recipient" do
687+
response_valid_signed.settings = settings
688+
response_valid_signed.settings.assertion_consumer_service_url = 'not-the-recipient'
689+
assert !response_valid_signed.send(:validate_subject_confirmation)
690+
assert_includes response_valid_signed.errors, "A valid SubjectConfirmation was not found on this Response"
691+
end
692+
679693
it "return true when the skip_subject_confirmation option is passed and the subject confirmation is valid" do
680694
opts = {}
681695
opts[:skip_subject_confirmation] = true

0 commit comments

Comments
 (0)