@@ -132,6 +132,7 @@ def sessionindex
132
132
# attributes['name']
133
133
#
134
134
# @return [Attributes] OneLogin::RubySaml::Attributes enumerable collection.
135
+ # @raise [ValidationError] if there are 2+ Attribute with the same Name
135
136
#
136
137
def attributes
137
138
@attr_statements ||= begin
@@ -140,8 +141,19 @@ def attributes
140
141
stmt_elements = xpath_from_signed_assertion ( '/a:AttributeStatement' )
141
142
stmt_elements . each do |stmt_element |
142
143
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 |
145
157
if ( e . elements . nil? || e . elements . size == 0 )
146
158
# SAMLCore requires that nil AttributeValues MUST contain xsi:nil XML attribute set to "true" or "1"
147
159
# otherwise the value is to be regarded as empty.
@@ -300,7 +312,6 @@ def validate(collect_errors = false)
300
312
:validate_id ,
301
313
:validate_success_status ,
302
314
:validate_num_assertion ,
303
- :validate_no_encrypted_attributes ,
304
315
:validate_no_duplicated_attributes ,
305
316
:validate_signed_elements ,
306
317
:validate_structure ,
@@ -432,37 +443,17 @@ def validate_num_assertion
432
443
true
433
444
end
434
445
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
-
449
446
# Validates that there are not duplicated attributes
450
447
# If fails, the error is added to the errors array
451
448
# @return [Boolean] True if there are no duplicated attribute elements, otherwise False if soft=True
452
449
# @raise [ValidationError] if soft == false and validation fails
453
450
#
454
451
def validate_no_duplicated_attributes
455
452
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 )
466
457
end
467
458
end
468
459
@@ -928,22 +919,39 @@ def decrypt_nameid(encryptedid_node)
928
919
decrypt_element ( encryptedid_node , /(.*<\/ (\w +:)?NameID>)/m )
929
920
end
930
921
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
+
931
930
# Decrypt an element
932
931
# @param encryptedid_node [REXML::Element] The encrypted element
932
+ # @param rgrex string Regex
933
933
# @return [REXML::Document] The decrypted element
934
934
#
935
935
def decrypt_element ( encrypt_node , rgrex )
936
936
if settings . nil? || !settings . get_sp_key
937
937
raise ValidationError . new ( 'An ' + encrypt_node . name + ' found and no SP private key found on the settings to decrypt it' )
938
938
end
939
939
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
+
940
947
elem_plaintext = OneLogin ::RubySaml ::Utils . decrypt_data ( encrypt_node , settings . get_sp_key )
941
948
# If we get some problematic noise in the plaintext after decrypting.
942
949
# This quick regexp parse will grab only the Element and discard the noise.
943
950
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>'
947
955
doc = REXML ::Document . new ( elem_plaintext )
948
956
doc . root [ 0 ]
949
957
end
0 commit comments