@@ -132,6 +132,7 @@ def sessionindex
132132 # attributes['name']
133133 #
134134 # @return [Attributes] OneLogin::RubySaml::Attributes enumerable collection.
135+ # @raise [ValidationError] if there are 2+ Attribute with the same Name
135136 #
136137 def attributes
137138 @attr_statements ||= begin
@@ -140,8 +141,19 @@ def attributes
140141 stmt_elements = xpath_from_signed_assertion ( '/a:AttributeStatement' )
141142 stmt_elements . each do |stmt_element |
142143 stmt_element . elements . each do |attr_element |
143- name = attr_element . attributes [ "Name" ]
144- values = attr_element . elements . collect { |e |
144+ if attr_element . name == "EncryptedAttribute"
145+ node = decrypt_attribute ( attr_element . dup )
146+ else
147+ node = attr_element
148+ end
149+
150+ name = node . attributes [ "Name" ]
151+
152+ if options [ :check_duplicated_attributes ] && attributes . include? ( name )
153+ raise ValidationError . new ( "Found an Attribute element with duplicated Name" )
154+ end
155+
156+ values = node . elements . collect { |e |
145157 if ( e . elements . nil? || e . elements . size == 0 )
146158 # SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
147159 # otherwise the value is to be regarded as empty.
@@ -300,7 +312,6 @@ def validate(collect_errors = false)
300312 :validate_id ,
301313 :validate_success_status ,
302314 :validate_num_assertion ,
303- :validate_no_encrypted_attributes ,
304315 :validate_no_duplicated_attributes ,
305316 :validate_signed_elements ,
306317 :validate_structure ,
@@ -432,37 +443,17 @@ def validate_num_assertion
432443 true
433444 end
434445
435- # Validates that there are not EncryptedAttribute (not supported)
436- # If fails, the error is added to the errors array
437- # @return [Boolean] True if there are no EncryptedAttribute elements, otherwise False if soft=True
438- # @raise [ValidationError] if soft == false and validation fails
439- #
440- def validate_no_encrypted_attributes
441- nodes = xpath_from_signed_assertion ( "/a:AttributeStatement/a:EncryptedAttribute" )
442- if nodes && nodes . length > 0
443- return append_error ( "There is an EncryptedAttribute in the Response and this SP not support them" )
444- end
445-
446- true
447- end
448-
449446 # Validates that there are not duplicated attributes
450447 # If fails, the error is added to the errors array
451448 # @return [Boolean] True if there are no duplicated attribute elements, otherwise False if soft=True
452449 # @raise [ValidationError] if soft == false and validation fails
453450 #
454451 def validate_no_duplicated_attributes
455452 if options [ :check_duplicated_attributes ]
456- processed_names = [ ]
457- stmt_elements = xpath_from_signed_assertion ( '/a:AttributeStatement' )
458- stmt_elements . each do |stmt_element |
459- stmt_element . elements . each do |attr_element |
460- name = attr_element . attributes [ "Name" ]
461- if attributes . include? ( name )
462- return append_error ( "Found an Attribute element with duplicated Name" )
463- end
464- processed_names . add ( name )
465- end
453+ begin
454+ attributes
455+ rescue ValidationError => e
456+ return append_error ( e . message )
466457 end
467458 end
468459
@@ -928,22 +919,39 @@ def decrypt_nameid(encryptedid_node)
928919 decrypt_element ( encryptedid_node , /(.*<\/ (\w +:)?NameID>)/m )
929920 end
930921
922+ # Decrypts an EncryptedID element
923+ # @param encryptedid_node [REXML::Element] The EncryptedID element
924+ # @return [REXML::Document] The decrypted EncrypedtID element
925+ #
926+ def decrypt_attribute ( encryptedattribute_node )
927+ decrypt_element ( encryptedattribute_node , /(.*<\/ (\w +:)?Attribute>)/m )
928+ end
929+
931930 # Decrypt an element
932931 # @param encryptedid_node [REXML::Element] The encrypted element
932+ # @param rgrex string Regex
933933 # @return [REXML::Document] The decrypted element
934934 #
935935 def decrypt_element ( encrypt_node , rgrex )
936936 if settings . nil? || !settings . get_sp_key
937937 raise ValidationError . new ( 'An ' + encrypt_node . name + ' found and no SP private key found on the settings to decrypt it' )
938938 end
939939
940+
941+ if encrypt_node . name == 'EncryptedAttribute'
942+ node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
943+ else
944+ node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">'
945+ end
946+
940947 elem_plaintext = OneLogin ::RubySaml ::Utils . decrypt_data ( encrypt_node , settings . get_sp_key )
941948 # If we get some problematic noise in the plaintext after decrypting.
942949 # This quick regexp parse will grab only the Element and discard the noise.
943950 elem_plaintext = elem_plaintext . match ( rgrex ) [ 0 ]
944- # To avoid namespace errors if saml namespace is not defined at assertion_plaintext
945- # create a parent node first with the saml namespace defined
946- elem_plaintext = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">' + elem_plaintext + '</node>'
951+
952+ # To avoid namespace errors if saml namespace is not defined
953+ # create a parent node first with the namespace defined
954+ elem_plaintext = node_header + elem_plaintext + '</node>'
947955 doc = REXML ::Document . new ( elem_plaintext )
948956 doc . root [ 0 ]
949957 end
0 commit comments