ruby-saml 1.12.4 → 1.18.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,7 +43,7 @@ module OneLogin
43
43
  end
44
44
 
45
45
  @options = options
46
- @response = decode_raw_saml(response)
46
+ @response = decode_raw_saml(response, settings)
47
47
  @document = XMLSecurity::SignedDocument.new(@response)
48
48
  end
49
49
 
@@ -213,7 +213,7 @@ module OneLogin
213
213
  return true unless options.has_key? :get_params
214
214
  return true unless options[:get_params].has_key? 'Signature'
215
215
 
216
- options[:raw_get_params] = OneLogin::RubySaml::Utils.prepare_raw_get_params(options[:raw_get_params], options[:get_params])
216
+ options[:raw_get_params] = OneLogin::RubySaml::Utils.prepare_raw_get_params(options[:raw_get_params], options[:get_params], settings.security[:lowercase_url_encoding])
217
217
 
218
218
  if options[:get_params]['SigAlg'].nil? && !options[:raw_get_params]['SigAlg'].nil?
219
219
  options[:get_params]['SigAlg'] = CGI.unescape(options[:raw_get_params]['SigAlg'])
@@ -21,53 +21,61 @@ module OneLogin
21
21
  #
22
22
  def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
23
23
  meta_doc = XMLSecurity::Document.new
24
+ add_xml_declaration(meta_doc)
25
+ root = add_root_element(meta_doc, settings, valid_until, cache_duration)
26
+ sp_sso = add_sp_sso_element(root, settings)
27
+ add_sp_certificates(sp_sso, settings)
28
+ add_sp_service_elements(sp_sso, settings)
29
+ add_extras(root, settings)
30
+ embed_signature(meta_doc, settings)
31
+ output_xml(meta_doc, pretty_print)
32
+ end
33
+
34
+ protected
35
+
36
+ def add_xml_declaration(meta_doc)
37
+ meta_doc << REXML::XMLDecl.new('1.0', 'UTF-8')
38
+ end
39
+
40
+ def add_root_element(meta_doc, settings, valid_until, cache_duration)
24
41
  namespaces = {
25
42
  "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
26
43
  }
44
+
27
45
  if settings.attribute_consuming_service.configured?
28
46
  namespaces["xmlns:saml"] = "urn:oasis:names:tc:SAML:2.0:assertion"
29
47
  end
30
- root = meta_doc.add_element "md:EntityDescriptor", namespaces
31
- sp_sso = root.add_element "md:SPSSODescriptor", {
48
+
49
+ root = meta_doc.add_element("md:EntityDescriptor", namespaces)
50
+ root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
51
+ root.attributes["entityID"] = settings.sp_entity_id if settings.sp_entity_id
52
+ root.attributes["validUntil"] = valid_until.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if valid_until
53
+ root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S" if cache_duration
54
+ root
55
+ end
56
+
57
+ def add_sp_sso_element(root, settings)
58
+ root.add_element "md:SPSSODescriptor", {
32
59
  "protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
33
60
  "AuthnRequestsSigned" => settings.security[:authn_requests_signed],
34
61
  "WantAssertionsSigned" => settings.security[:want_assertions_signed],
35
62
  }
63
+ end
36
64
 
37
- # Add KeyDescriptor if messages will be signed / encrypted
38
- # with SP certificate, and new SP certificate if any
39
- cert = settings.get_sp_cert
40
- cert_new = settings.get_sp_cert_new
41
-
42
- for sp_cert in [cert, cert_new]
43
- if sp_cert
44
- cert_text = Base64.encode64(sp_cert.to_der).gsub("\n", '')
45
- kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
46
- ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
47
- xd = ki.add_element "ds:X509Data"
48
- xc = xd.add_element "ds:X509Certificate"
49
- xc.text = cert_text
50
-
51
- if settings.security[:want_assertions_encrypted]
52
- kd2 = sp_sso.add_element "md:KeyDescriptor", { "use" => "encryption" }
53
- ki2 = kd2.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
54
- xd2 = ki2.add_element "ds:X509Data"
55
- xc2 = xd2.add_element "ds:X509Certificate"
56
- xc2.text = cert_text
57
- end
58
- end
59
- end
65
+ # Add KeyDescriptor elements for SP certificates.
66
+ def add_sp_certificates(sp_sso, settings)
67
+ certs = settings.get_sp_certs
60
68
 
61
- root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
62
- if settings.sp_entity_id
63
- root.attributes["entityID"] = settings.sp_entity_id
64
- end
65
- if valid_until
66
- root.attributes["validUntil"] = valid_until.strftime('%Y-%m-%dT%H:%M:%S%z')
67
- end
68
- if cache_duration
69
- root.attributes["cacheDuration"] = "PT" + cache_duration.to_s + "S"
69
+ certs[:signing].each { |cert, _| add_sp_cert_element(sp_sso, cert, :signing) }
70
+
71
+ if settings.security[:want_assertions_encrypted]
72
+ certs[:encryption].each { |cert, _| add_sp_cert_element(sp_sso, cert, :encryption) }
70
73
  end
74
+
75
+ sp_sso
76
+ end
77
+
78
+ def add_sp_service_elements(sp_sso, settings)
71
79
  if settings.single_logout_service_url
72
80
  sp_sso.add_element "md:SingleLogoutService", {
73
81
  "Binding" => settings.single_logout_service_binding,
@@ -75,10 +83,12 @@ module OneLogin
75
83
  "ResponseLocation" => settings.single_logout_service_url
76
84
  }
77
85
  end
86
+
78
87
  if settings.name_identifier_format
79
88
  nameid = sp_sso.add_element "md:NameIDFormat"
80
89
  nameid.text = settings.name_identifier_format
81
90
  end
91
+
82
92
  if settings.assertion_consumer_service_url
83
93
  sp_sso.add_element "md:AssertionConsumerService", {
84
94
  "Binding" => settings.assertion_consumer_service_binding,
@@ -117,15 +127,26 @@ module OneLogin
117
127
  # <md:RoleDescriptor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:query="urn:oasis:names:tc:SAML:metadata:ext:query" xsi:type="query:AttributeQueryDescriptorType" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
118
128
  # <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
119
129
 
120
- meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
130
+ sp_sso
131
+ end
121
132
 
122
- # embed signature
123
- if settings.security[:metadata_signed] && settings.private_key && settings.certificate
124
- private_key = settings.get_sp_key
125
- meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
126
- end
133
+ # can be overridden in subclass
134
+ def add_extras(root, _settings)
135
+ root
136
+ end
137
+
138
+ def embed_signature(meta_doc, settings)
139
+ return unless settings.security[:metadata_signed]
140
+
141
+ cert, private_key = settings.get_sp_signing_pair
142
+ return unless private_key && cert
143
+
144
+ meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
145
+ end
146
+
147
+ def output_xml(meta_doc, pretty_print)
148
+ ret = ''.dup
127
149
 
128
- ret = ""
129
150
  # pretty print the XML so IdP administrators can easily see what the SP supports
130
151
  if pretty_print
131
152
  meta_doc.write(ret, 1)
@@ -133,7 +154,19 @@ module OneLogin
133
154
  ret = meta_doc.to_s
134
155
  end
135
156
 
136
- return ret
157
+ ret
158
+ end
159
+
160
+ private
161
+
162
+ def add_sp_cert_element(sp_sso, cert, use)
163
+ return unless cert
164
+ cert_text = Base64.encode64(cert.to_der).gsub("\n", '')
165
+ kd = sp_sso.add_element "md:KeyDescriptor", { "use" => use.to_s }
166
+ ki = kd.add_element "ds:KeyInfo", { "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#" }
167
+ xd = ki.add_element "ds:X509Data"
168
+ xc = xd.add_element "ds:X509Certificate"
169
+ xc.text = cert_text
137
170
  end
138
171
  end
139
172
  end
@@ -17,6 +17,10 @@ module OneLogin
17
17
  PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
18
18
  DSIG = "http://www.w3.org/2000/09/xmldsig#"
19
19
  XENC = "http://www.w3.org/2001/04/xmlenc#"
20
+ SAML_NAMESPACES = {
21
+ "p" => PROTOCOL,
22
+ "a" => ASSERTION
23
+ }.freeze
20
24
 
21
25
  # TODO: Settings should probably be initialized too... WDYT?
22
26
 
@@ -63,7 +67,7 @@ module OneLogin
63
67
  end
64
68
  end
65
69
 
66
- @response = decode_raw_saml(response)
70
+ @response = decode_raw_saml(response, settings)
67
71
  @document = XMLSecurity::SignedDocument.new(@response, @errors)
68
72
 
69
73
  if assertion_encrypted?
@@ -198,6 +202,27 @@ module OneLogin
198
202
  end
199
203
  end
200
204
 
205
+ # Gets the AuthnInstant from the AuthnStatement.
206
+ # Could be used to require re-authentication if a long time has passed
207
+ # since the last user authentication.
208
+ # @return [String] AuthnInstant value
209
+ #
210
+ def authn_instant
211
+ @authn_instant ||= begin
212
+ node = xpath_first_from_signed_assertion('/a:AuthnStatement')
213
+ node.nil? ? nil : node.attributes['AuthnInstant']
214
+ end
215
+ end
216
+
217
+ # Gets the AuthnContextClassRef from the AuthnStatement
218
+ # Could be used to require re-authentication if the assertion
219
+ # did not met the requested authentication context class.
220
+ # @return [String] AuthnContextClassRef value
221
+ #
222
+ def authn_context_class_ref
223
+ @authn_context_class_ref ||= Utils.element_text(xpath_first_from_signed_assertion('/a:AuthnStatement/a:AuthnContext/a:AuthnContextClassRef'))
224
+ end
225
+
201
226
  # Checks if the Status has the "Success" code
202
227
  # @return [Boolean] True if the StatusCode is Sucess
203
228
  #
@@ -227,11 +252,10 @@ module OneLogin
227
252
  statuses = nodes.collect do |inner_node|
228
253
  inner_node.attributes["Value"]
229
254
  end
230
- extra_code = statuses.join(" | ")
231
- if extra_code
232
- code = "#{code} | #{extra_code}"
233
- end
255
+
256
+ code = [code, statuses].flatten.join(" | ")
234
257
  end
258
+
235
259
  code
236
260
  end
237
261
  end
@@ -283,7 +307,7 @@ module OneLogin
283
307
  issuer_response_nodes = REXML::XPath.match(
284
308
  document,
285
309
  "/p:Response/a:Issuer",
286
- { "p" => PROTOCOL, "a" => ASSERTION }
310
+ SAML_NAMESPACES
287
311
  )
288
312
 
289
313
  unless issuer_response_nodes.size == 1
@@ -338,9 +362,9 @@ module OneLogin
338
362
  end
339
363
 
340
364
  # returns the allowed clock drift on timing validation
341
- # @return [Integer]
365
+ # @return [Float]
342
366
  def allowed_clock_drift
343
- return options[:allowed_clock_drift].to_f
367
+ options[:allowed_clock_drift].to_f.abs + Float::EPSILON
344
368
  end
345
369
 
346
370
  # Checks if the SAML Response contains or not an EncryptedAssertion element
@@ -350,7 +374,7 @@ module OneLogin
350
374
  ! REXML::XPath.first(
351
375
  document,
352
376
  "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
353
- { "p" => PROTOCOL, "a" => ASSERTION }
377
+ SAML_NAMESPACES
354
378
  ).nil?
355
379
  end
356
380
 
@@ -377,14 +401,13 @@ module OneLogin
377
401
  return false unless validate_response_state
378
402
 
379
403
  validations = [
380
- :validate_response_state,
381
404
  :validate_version,
382
405
  :validate_id,
383
406
  :validate_success_status,
384
407
  :validate_num_assertion,
385
- :validate_no_duplicated_attributes,
386
408
  :validate_signed_elements,
387
409
  :validate_structure,
410
+ :validate_no_duplicated_attributes,
388
411
  :validate_in_response_to,
389
412
  :validate_one_conditions,
390
413
  :validate_conditions,
@@ -425,6 +448,7 @@ module OneLogin
425
448
  #
426
449
  def validate_structure
427
450
  structure_error_msg = "Invalid SAML Response. Not match the saml-schema-protocol-2.0.xsd"
451
+
428
452
  check_malformed_doc = check_malformed_doc_enabled?
429
453
  unless valid_saml?(document, soft, check_malformed_doc)
430
454
  return append_error(structure_error_msg)
@@ -616,7 +640,12 @@ module OneLogin
616
640
  #
617
641
  def validate_audience
618
642
  return true if options[:skip_audience]
619
- return true if audiences.empty? || settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
643
+ return true if settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
644
+
645
+ if audiences.empty?
646
+ return true unless settings.security[:strict_audience_validation]
647
+ return append_error("Invalid Audiences. The <AudienceRestriction> element contained only empty <Audience> elements. Expected audience #{settings.sp_entity_id}.")
648
+ end
620
649
 
621
650
  unless audiences.include? settings.sp_entity_id
622
651
  s = audiences.count > 1 ? 's' : '';
@@ -695,13 +724,13 @@ module OneLogin
695
724
 
696
725
  now = Time.now.utc
697
726
 
698
- if not_before && (now_with_drift = now + allowed_clock_drift) < not_before
699
- error_msg = "Current time is earlier than NotBefore condition (#{now_with_drift} < #{not_before})"
727
+ if not_before && now < (not_before - allowed_clock_drift)
728
+ error_msg = "Current time is earlier than NotBefore condition (#{now} < #{not_before}#{" - #{allowed_clock_drift.ceil}s" if allowed_clock_drift > 0})"
700
729
  return append_error(error_msg)
701
730
  end
702
731
 
703
- if not_on_or_after && now >= (not_on_or_after_with_drift = not_on_or_after + allowed_clock_drift)
704
- error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after_with_drift})"
732
+ if not_on_or_after && now >= (not_on_or_after + allowed_clock_drift)
733
+ error_msg = "Current time is on or after NotOnOrAfter condition (#{now} >= #{not_on_or_after}#{" + #{allowed_clock_drift.ceil}s" if allowed_clock_drift > 0})"
705
734
  return append_error(error_msg)
706
735
  end
707
736
 
@@ -739,11 +768,11 @@ module OneLogin
739
768
  # @return [Boolean] True if the SessionNotOnOrAfter of the AuthnStatement is valid, otherwise (when expired) False if soft=True
740
769
  # @raise [ValidationError] if soft == false and validation fails
741
770
  #
742
- def validate_session_expiration(soft = true)
771
+ def validate_session_expiration
743
772
  return true if session_expires_at.nil?
744
773
 
745
774
  now = Time.now.utc
746
- unless (session_expires_at + allowed_clock_drift) > now
775
+ unless now < (session_expires_at + allowed_clock_drift)
747
776
  error_msg = "The attributes have expired, based on the SessionNotOnOrAfter of the AuthnStatement of this Response"
748
777
  return append_error(error_msg)
749
778
  end
@@ -781,8 +810,8 @@ module OneLogin
781
810
 
782
811
  attrs = confirmation_data_node.attributes
783
812
  next if (attrs.include? "InResponseTo" and attrs['InResponseTo'] != in_response_to) ||
784
- (attrs.include? "NotOnOrAfter" and (parse_time(confirmation_data_node, "NotOnOrAfter") + allowed_clock_drift) <= now) ||
785
- (attrs.include? "NotBefore" and parse_time(confirmation_data_node, "NotBefore") > (now + allowed_clock_drift)) ||
813
+ (attrs.include? "NotBefore" and now < (parse_time(confirmation_data_node, "NotBefore") - allowed_clock_drift)) ||
814
+ (attrs.include? "NotOnOrAfter" and now >= (parse_time(confirmation_data_node, "NotOnOrAfter") + allowed_clock_drift)) ||
786
815
  (attrs.include? "Recipient" and !options[:skip_recipient_check] and settings and attrs['Recipient'] != settings.assertion_consumer_service_url)
787
816
 
788
817
  valid_subject_confirmation = true
@@ -810,7 +839,7 @@ module OneLogin
810
839
 
811
840
  unless settings.sp_entity_id.nil? || settings.sp_entity_id.empty? || name_id_spnamequalifier.nil? || name_id_spnamequalifier.empty?
812
841
  if name_id_spnamequalifier != settings.sp_entity_id
813
- return append_error("The SPNameQualifier value mistmatch the SP entityID value.")
842
+ return append_error("SPNameQualifier value does not match the SP entityID value.")
814
843
  end
815
844
  end
816
845
  end
@@ -818,6 +847,25 @@ module OneLogin
818
847
  true
819
848
  end
820
849
 
850
+ def doc_to_validate
851
+ # If the response contains the signature, and the assertion was encrypted, validate the original SAML Response
852
+ # otherwise, review if the decrypted assertion contains a signature
853
+ sig_elements = REXML::XPath.match(
854
+ document,
855
+ "/p:Response[@ID=$id]/ds:Signature",
856
+ { "p" => PROTOCOL, "ds" => DSIG },
857
+ { 'id' => document.signed_element_id }
858
+ )
859
+
860
+ use_original = sig_elements.size == 1 || decrypted_document.nil?
861
+ doc = use_original ? document : decrypted_document
862
+ if !doc.processed
863
+ doc.cache_referenced_xml(@soft, check_malformed_doc_enabled?)
864
+ end
865
+
866
+ return doc
867
+ end
868
+
821
869
  # Validates the Signature
822
870
  # @return [Boolean] True if not contains a Signature or if the Signature is valid, otherwise False if soft=True
823
871
  # @raise [ValidationError] if soft == false and validation fails
@@ -825,8 +873,8 @@ module OneLogin
825
873
  def validate_signature
826
874
  error_msg = "Invalid Signature on SAML Response"
827
875
 
828
- # If the response contains the signature, and the assertion was encrypted, validate the original SAML Response
829
- # otherwise, review if the decrypted assertion contains a signature
876
+ doc = doc_to_validate
877
+
830
878
  sig_elements = REXML::XPath.match(
831
879
  document,
832
880
  "/p:Response[@ID=$id]/ds:Signature",
@@ -834,15 +882,12 @@ module OneLogin
834
882
  { 'id' => document.signed_element_id }
835
883
  )
836
884
 
837
- use_original = sig_elements.size == 1 || decrypted_document.nil?
838
- doc = use_original ? document : decrypted_document
839
-
840
- # Check signature nodes
885
+ # Check signature node inside assertion
841
886
  if sig_elements.nil? || sig_elements.size == 0
842
887
  sig_elements = REXML::XPath.match(
843
888
  doc,
844
889
  "/p:Response/a:Assertion[@ID=$id]/ds:Signature",
845
- {"p" => PROTOCOL, "a" => ASSERTION, "ds"=>DSIG},
890
+ SAML_NAMESPACES.merge({"ds"=>DSIG}),
846
891
  { 'id' => doc.signed_element_id }
847
892
  )
848
893
  end
@@ -866,8 +911,6 @@ module OneLogin
866
911
  fingerprint = settings.get_fingerprint
867
912
  opts[:cert] = idp_cert
868
913
 
869
- check_malformed_doc = check_malformed_doc_enabled?
870
- opts[:check_malformed_doc] = check_malformed_doc
871
914
  if fingerprint && doc.validate_document(fingerprint, @soft, opts)
872
915
  if settings.security[:check_idp_cert_expiration]
873
916
  if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
@@ -882,7 +925,7 @@ module OneLogin
882
925
  valid = false
883
926
  expired = false
884
927
  idp_certs[:signing].each do |idp_cert|
885
- valid = doc.validate_document_with_cert(idp_cert, true, check_malformed_doc)
928
+ valid = doc.validate_document_with_cert(idp_cert, true)
886
929
  if valid
887
930
  if settings.security[:check_idp_cert_expiration]
888
931
  if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
@@ -915,31 +958,54 @@ module OneLogin
915
958
  begin
916
959
  encrypted_node = xpath_first_from_signed_assertion('/a:Subject/a:EncryptedID')
917
960
  if encrypted_node
918
- node = decrypt_nameid(encrypted_node)
961
+ decrypt_nameid(encrypted_node)
919
962
  else
920
- node = xpath_first_from_signed_assertion('/a:Subject/a:NameID')
963
+ xpath_first_from_signed_assertion('/a:Subject/a:NameID')
921
964
  end
922
965
  end
923
966
  end
924
967
 
968
+ def get_cached_signed_assertion
969
+ xml = doc_to_validate.referenced_xml
970
+ empty_doc = REXML::Document.new
971
+
972
+ return empty_doc if xml.nil? # when no signature/reference is found, return empty document
973
+
974
+ root = REXML::Document.new(xml).root
975
+
976
+ if root.attributes["ID"] != doc_to_validate.signed_element_id
977
+ return empty_doc
978
+ end
979
+
980
+ assertion = empty_doc
981
+ if root.name == "Response"
982
+ if REXML::XPath.first(root, "a:Assertion", {"a" => ASSERTION})
983
+ assertion = REXML::XPath.first(root, "a:Assertion", {"a" => ASSERTION})
984
+ elsif REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => ASSERTION})
985
+ assertion = decrypt_assertion(REXML::XPath.first(root, "a:EncryptedAssertion", {"a" => ASSERTION}))
986
+ end
987
+ elsif root.name == "Assertion"
988
+ assertion = root
989
+ end
990
+
991
+ assertion
992
+ end
993
+
994
+ def signed_assertion
995
+ @signed_assertion ||= get_cached_signed_assertion
996
+ end
997
+
925
998
  # Extracts the first appearance that matchs the subelt (pattern)
926
999
  # Search on any Assertion that is signed, or has a Response parent signed
927
1000
  # @param subelt [String] The XPath pattern
928
1001
  # @return [REXML::Element | nil] If any matches, return the Element
929
1002
  #
930
1003
  def xpath_first_from_signed_assertion(subelt=nil)
931
- doc = decrypted_document.nil? ? document : decrypted_document
1004
+ doc = signed_assertion
932
1005
  node = REXML::XPath.first(
933
1006
  doc,
934
- "/p:Response/a:Assertion[@ID=$id]#{subelt}",
935
- { "p" => PROTOCOL, "a" => ASSERTION },
936
- { 'id' => doc.signed_element_id }
937
- )
938
- node ||= REXML::XPath.first(
939
- doc,
940
- "/p:Response[@ID=$id]/a:Assertion#{subelt}",
941
- { "p" => PROTOCOL, "a" => ASSERTION },
942
- { 'id' => doc.signed_element_id }
1007
+ "./#{subelt}",
1008
+ SAML_NAMESPACES
943
1009
  )
944
1010
  node
945
1011
  end
@@ -950,26 +1016,20 @@ module OneLogin
950
1016
  # @return [Array of REXML::Element] Return all matches
951
1017
  #
952
1018
  def xpath_from_signed_assertion(subelt=nil)
953
- doc = decrypted_document.nil? ? document : decrypted_document
1019
+ doc = signed_assertion
954
1020
  node = REXML::XPath.match(
955
1021
  doc,
956
- "/p:Response/a:Assertion[@ID=$id]#{subelt}",
957
- { "p" => PROTOCOL, "a" => ASSERTION },
958
- { 'id' => doc.signed_element_id }
1022
+ "./#{subelt}",
1023
+ SAML_NAMESPACES
959
1024
  )
960
- node.concat( REXML::XPath.match(
961
- doc,
962
- "/p:Response[@ID=$id]/a:Assertion#{subelt}",
963
- { "p" => PROTOCOL, "a" => ASSERTION },
964
- { 'id' => doc.signed_element_id }
965
- ))
1025
+ node
966
1026
  end
967
1027
 
968
1028
  # Generates the decrypted_document
969
1029
  # @return [XMLSecurity::SignedDocument] The SAML Response with the assertion decrypted
970
1030
  #
971
1031
  def generate_decrypted_document
972
- if settings.nil? || !settings.get_sp_key
1032
+ if settings.nil? || settings.get_sp_decryption_keys.empty?
973
1033
  raise ValidationError.new('An EncryptedAssertion found and no SP private key found on the settings to decrypt it. Be sure you provided the :settings parameter at the initialize method')
974
1034
  end
975
1035
 
@@ -996,7 +1056,7 @@ module OneLogin
996
1056
  encrypted_assertion_node = REXML::XPath.first(
997
1057
  document_copy,
998
1058
  "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
999
- { "p" => PROTOCOL, "a" => ASSERTION }
1059
+ SAML_NAMESPACES
1000
1060
  )
1001
1061
  response_node.add(decrypt_assertion(encrypted_assertion_node))
1002
1062
  encrypted_assertion_node.remove
@@ -1012,42 +1072,42 @@ module OneLogin
1012
1072
  end
1013
1073
 
1014
1074
  # Decrypts an EncryptedID element
1015
- # @param encryptedid_node [REXML::Element] The EncryptedID element
1075
+ # @param encrypted_id_node [REXML::Element] The EncryptedID element
1016
1076
  # @return [REXML::Document] The decrypted EncrypedtID element
1017
1077
  #
1018
- def decrypt_nameid(encryptedid_node)
1019
- decrypt_element(encryptedid_node, /(.*<\/(\w+:)?NameID>)/m)
1078
+ def decrypt_nameid(encrypted_id_node)
1079
+ decrypt_element(encrypted_id_node, /(.*<\/(\w+:)?NameID>)/m)
1020
1080
  end
1021
1081
 
1022
- # Decrypts an EncryptedID element
1023
- # @param encryptedid_node [REXML::Element] The EncryptedID element
1024
- # @return [REXML::Document] The decrypted EncrypedtID element
1082
+ # Decrypts an EncryptedAttribute element
1083
+ # @param encrypted_attribute_node [REXML::Element] The EncryptedAttribute element
1084
+ # @return [REXML::Document] The decrypted EncryptedAttribute element
1025
1085
  #
1026
- def decrypt_attribute(encryptedattribute_node)
1027
- decrypt_element(encryptedattribute_node, /(.*<\/(\w+:)?Attribute>)/m)
1086
+ def decrypt_attribute(encrypted_attribute_node)
1087
+ decrypt_element(encrypted_attribute_node, /(.*<\/(\w+:)?Attribute>)/m)
1028
1088
  end
1029
1089
 
1030
1090
  # Decrypt an element
1031
- # @param encryptedid_node [REXML::Element] The encrypted element
1032
- # @param rgrex string Regex
1091
+ # @param encrypt_node [REXML::Element] The encrypted element
1092
+ # @param regexp [Regexp] The regular expression to extract the decrypted data
1033
1093
  # @return [REXML::Document] The decrypted element
1034
1094
  #
1035
- def decrypt_element(encrypt_node, rgrex)
1036
- if settings.nil? || !settings.get_sp_key
1095
+ def decrypt_element(encrypt_node, regexp)
1096
+ if settings.nil? || settings.get_sp_decryption_keys.empty?
1037
1097
  raise ValidationError.new('An ' + encrypt_node.name + ' found and no SP private key found on the settings to decrypt it')
1038
1098
  end
1039
1099
 
1040
-
1041
1100
  if encrypt_node.name == 'EncryptedAttribute'
1042
1101
  node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
1043
1102
  else
1044
1103
  node_header = '<node xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">'
1045
1104
  end
1046
1105
 
1047
- elem_plaintext = OneLogin::RubySaml::Utils.decrypt_data(encrypt_node, settings.get_sp_key)
1106
+ elem_plaintext = OneLogin::RubySaml::Utils.decrypt_multi(encrypt_node, settings.get_sp_decryption_keys)
1107
+
1048
1108
  # If we get some problematic noise in the plaintext after decrypting.
1049
1109
  # This quick regexp parse will grab only the Element and discard the noise.
1050
- elem_plaintext = elem_plaintext.match(rgrex)[0]
1110
+ elem_plaintext = elem_plaintext.match(regexp)[0]
1051
1111
 
1052
1112
  # To avoid namespace errors if saml namespace is not defined
1053
1113
  # create a parent node first with the namespace defined