ruby-saml 1.9.0 → 1.12.0

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.
Files changed (157) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +30 -14
  3. data/README.md +108 -22
  4. data/changelog.md +38 -0
  5. data/lib/onelogin/ruby-saml/attributes.rb +24 -1
  6. data/lib/onelogin/ruby-saml/authrequest.rb +23 -6
  7. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +239 -171
  8. data/lib/onelogin/ruby-saml/logging.rb +3 -3
  9. data/lib/onelogin/ruby-saml/logoutrequest.rb +20 -5
  10. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -9
  11. data/lib/onelogin/ruby-saml/metadata.rb +11 -3
  12. data/lib/onelogin/ruby-saml/response.rb +67 -21
  13. data/lib/onelogin/ruby-saml/saml_message.rb +12 -2
  14. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  15. data/lib/onelogin/ruby-saml/settings.rb +73 -7
  16. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +20 -1
  17. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +38 -16
  18. data/lib/onelogin/ruby-saml/utils.rb +74 -1
  19. data/lib/onelogin/ruby-saml/version.rb +1 -1
  20. data/lib/xml_security.rb +34 -6
  21. data/ruby-saml.gemspec +15 -7
  22. metadata +36 -278
  23. data/test/certificates/certificate1 +0 -12
  24. data/test/certificates/certificate_without_head_foot +0 -1
  25. data/test/certificates/formatted_certificate +0 -14
  26. data/test/certificates/formatted_chained_certificate +0 -42
  27. data/test/certificates/formatted_private_key +0 -12
  28. data/test/certificates/formatted_rsa_private_key +0 -12
  29. data/test/certificates/invalid_certificate1 +0 -1
  30. data/test/certificates/invalid_certificate2 +0 -1
  31. data/test/certificates/invalid_certificate3 +0 -12
  32. data/test/certificates/invalid_chained_certificate1 +0 -1
  33. data/test/certificates/invalid_private_key1 +0 -1
  34. data/test/certificates/invalid_private_key2 +0 -1
  35. data/test/certificates/invalid_private_key3 +0 -10
  36. data/test/certificates/invalid_rsa_private_key1 +0 -1
  37. data/test/certificates/invalid_rsa_private_key2 +0 -1
  38. data/test/certificates/invalid_rsa_private_key3 +0 -10
  39. data/test/certificates/ruby-saml-2.crt +0 -15
  40. data/test/certificates/ruby-saml.crt +0 -14
  41. data/test/certificates/ruby-saml.key +0 -15
  42. data/test/idp_metadata_parser_test.rb +0 -579
  43. data/test/logging_test.rb +0 -62
  44. data/test/logout_requests/invalid_slo_request.xml +0 -6
  45. data/test/logout_requests/slo_request.xml +0 -4
  46. data/test/logout_requests/slo_request.xml.base64 +0 -1
  47. data/test/logout_requests/slo_request_deflated.xml.base64 +0 -1
  48. data/test/logout_requests/slo_request_with_name_id_format.xml +0 -4
  49. data/test/logout_requests/slo_request_with_session_index.xml +0 -5
  50. data/test/logout_responses/logoutresponse_fixtures.rb +0 -67
  51. data/test/logoutrequest_test.rb +0 -226
  52. data/test/logoutresponse_test.rb +0 -402
  53. data/test/metadata/idp_descriptor.xml +0 -26
  54. data/test/metadata/idp_descriptor_2.xml +0 -56
  55. data/test/metadata/idp_descriptor_3.xml +0 -14
  56. data/test/metadata/idp_descriptor_4.xml +0 -72
  57. data/test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml +0 -72
  58. data/test/metadata/idp_metadata_multi_certs.xml +0 -75
  59. data/test/metadata/idp_metadata_multi_signing_certs.xml +0 -52
  60. data/test/metadata/idp_metadata_same_sign_and_encrypt_cert.xml +0 -71
  61. data/test/metadata/idp_multiple_descriptors.xml +0 -53
  62. data/test/metadata/no_idp_descriptor.xml +0 -21
  63. data/test/metadata_test.rb +0 -331
  64. data/test/request_test.rb +0 -323
  65. data/test/response_test.rb +0 -1619
  66. data/test/responses/adfs_response_sha1.xml +0 -46
  67. data/test/responses/adfs_response_sha256.xml +0 -46
  68. data/test/responses/adfs_response_sha384.xml +0 -46
  69. data/test/responses/adfs_response_sha512.xml +0 -46
  70. data/test/responses/adfs_response_xmlns.xml +0 -45
  71. data/test/responses/attackxee.xml +0 -13
  72. data/test/responses/invalids/duplicated_attributes.xml.base64 +0 -1
  73. data/test/responses/invalids/empty_destination.xml.base64 +0 -1
  74. data/test/responses/invalids/empty_nameid.xml.base64 +0 -1
  75. data/test/responses/invalids/encrypted_new_attack.xml.base64 +0 -1
  76. data/test/responses/invalids/invalid_audience.xml.base64 +0 -1
  77. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  78. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  79. data/test/responses/invalids/invalid_signature_position.xml.base64 +0 -1
  80. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +0 -1
  81. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +0 -1
  82. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +0 -1
  83. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +0 -1
  84. data/test/responses/invalids/multiple_assertions.xml.base64 +0 -2
  85. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  86. data/test/responses/invalids/no_authnstatement.xml.base64 +0 -1
  87. data/test/responses/invalids/no_conditions.xml.base64 +0 -1
  88. data/test/responses/invalids/no_id.xml.base64 +0 -1
  89. data/test/responses/invalids/no_issuer_assertion.xml.base64 +0 -1
  90. data/test/responses/invalids/no_issuer_response.xml.base64 +0 -1
  91. data/test/responses/invalids/no_nameid.xml.base64 +0 -1
  92. data/test/responses/invalids/no_saml2.xml.base64 +0 -1
  93. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  94. data/test/responses/invalids/no_status.xml.base64 +0 -1
  95. data/test/responses/invalids/no_status_code.xml.base64 +0 -1
  96. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +0 -1
  97. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +0 -1
  98. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +0 -1
  99. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  100. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  101. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  102. data/test/responses/invalids/status_code_responder.xml.base64 +0 -1
  103. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +0 -1
  104. data/test/responses/invalids/wrong_spnamequalifier.xml.base64 +0 -1
  105. data/test/responses/no_signature_ns.xml +0 -48
  106. data/test/responses/open_saml_response.xml +0 -56
  107. data/test/responses/response_assertion_wrapped.xml.base64 +0 -93
  108. data/test/responses/response_audience_self_closed_tag.xml.base64 +0 -1
  109. data/test/responses/response_double_status_code.xml.base64 +0 -1
  110. data/test/responses/response_encrypted_attrs.xml.base64 +0 -1
  111. data/test/responses/response_encrypted_nameid.xml.base64 +0 -1
  112. data/test/responses/response_eval.xml +0 -7
  113. data/test/responses/response_no_cert_and_encrypted_attrs.xml +0 -29
  114. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  115. data/test/responses/response_node_text_attack2.xml.base64 +0 -1
  116. data/test/responses/response_node_text_attack3.xml.base64 +0 -1
  117. data/test/responses/response_unsigned_xml_base64 +0 -1
  118. data/test/responses/response_with_ampersands.xml +0 -139
  119. data/test/responses/response_with_ampersands.xml.base64 +0 -93
  120. data/test/responses/response_with_ds_namespace_at_the_root.xml.base64 +0 -1
  121. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  122. data/test/responses/response_with_multiple_attribute_values.xml +0 -67
  123. data/test/responses/response_with_retrieval_method.xml +0 -26
  124. data/test/responses/response_with_saml2_namespace.xml.base64 +0 -102
  125. data/test/responses/response_with_signed_assertion.xml.base64 +0 -66
  126. data/test/responses/response_with_signed_assertion_2.xml.base64 +0 -1
  127. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  128. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  129. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  130. data/test/responses/response_without_attributes.xml.base64 +0 -79
  131. data/test/responses/response_without_reference_uri.xml.base64 +0 -1
  132. data/test/responses/response_wrapped.xml.base64 +0 -150
  133. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +0 -1
  134. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  135. data/test/responses/signed_nameid_in_atts.xml +0 -47
  136. data/test/responses/signed_unqual_nameid_in_atts.xml +0 -47
  137. data/test/responses/simple_saml_php.xml +0 -71
  138. data/test/responses/starfield_response.xml.base64 +0 -1
  139. data/test/responses/test_sign.xml +0 -43
  140. data/test/responses/unsigned_encrypted_adfs.xml +0 -23
  141. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +0 -1
  142. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +0 -1
  143. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +0 -1
  144. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +0 -1
  145. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +0 -1
  146. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +0 -1
  147. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +0 -1
  148. data/test/responses/valid_response.xml.base64 +0 -1
  149. data/test/responses/valid_response_with_formatted_x509certificate.xml.base64 +0 -1
  150. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  151. data/test/saml_message_test.rb +0 -56
  152. data/test/settings_test.rb +0 -329
  153. data/test/slo_logoutrequest_test.rb +0 -448
  154. data/test/slo_logoutresponse_test.rb +0 -199
  155. data/test/test_helper.rb +0 -327
  156. data/test/utils_test.rb +0 -254
  157. data/test/xml_security_test.rb +0 -421
@@ -8,9 +8,9 @@ module OneLogin
8
8
 
9
9
  def self.logger
10
10
  @logger ||= begin
11
- (defined?(::Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
12
- DEFAULT_LOGGER
13
- end
11
+ (defined?(::Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
12
+ DEFAULT_LOGGER
13
+ end
14
14
  end
15
15
 
16
16
  def self.logger=(logger)
@@ -1,6 +1,7 @@
1
1
  require "onelogin/ruby-saml/logging"
2
2
  require "onelogin/ruby-saml/saml_message"
3
3
  require "onelogin/ruby-saml/utils"
4
+ require "onelogin/ruby-saml/setting_error"
4
5
 
5
6
  # Only supports SAML 2.0
6
7
  module OneLogin
@@ -20,6 +21,10 @@ module OneLogin
20
21
  @uuid = OneLogin::RubySaml::Utils.uuid
21
22
  end
22
23
 
24
+ def request_id
25
+ @uuid
26
+ end
27
+
23
28
  # Creates the Logout Request string.
24
29
  # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
25
30
  # @param params [Hash] Some extra parameters to be added in the GET for example the RelayState
@@ -33,6 +38,7 @@ module OneLogin
33
38
  params.each_pair do |key, value|
34
39
  request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
35
40
  end
41
+ raise SettingError.new "Invalid settings, idp_slo_target_url is not set!" if settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
36
42
  @logout_url = settings.idp_slo_target_url + request_params
37
43
  end
38
44
 
@@ -89,6 +95,11 @@ module OneLogin
89
95
  # @return [String] The SAMLRequest String.
90
96
  #
91
97
  def create_logout_request_xml_doc(settings)
98
+ document = create_xml_document(settings)
99
+ sign_document(document, settings)
100
+ end
101
+
102
+ def create_xml_document(settings)
92
103
  time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
93
104
 
94
105
  request_doc = XMLSecurity::Document.new
@@ -98,11 +109,11 @@ module OneLogin
98
109
  root.attributes['ID'] = uuid
99
110
  root.attributes['IssueInstant'] = time
100
111
  root.attributes['Version'] = "2.0"
101
- root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil?
112
+ root.attributes['Destination'] = settings.idp_slo_target_url unless settings.idp_slo_target_url.nil? or settings.idp_slo_target_url.empty?
102
113
 
103
- if settings.issuer
114
+ if settings.sp_entity_id
104
115
  issuer = root.add_element "saml:Issuer"
105
- issuer.text = settings.issuer
116
+ issuer.text = settings.sp_entity_id
106
117
  end
107
118
 
108
119
  nameid = root.add_element "saml:NameID"
@@ -122,14 +133,18 @@ module OneLogin
122
133
  sessionindex.text = settings.sessionindex
123
134
  end
124
135
 
136
+ request_doc
137
+ end
138
+
139
+ def sign_document(document, settings)
125
140
  # embed signature
126
141
  if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
127
142
  private_key = settings.get_sp_key
128
143
  cert = settings.get_sp_cert
129
- request_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
144
+ document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
130
145
  end
131
146
 
132
- request_doc
147
+ document
133
148
  end
134
149
  end
135
150
  end
@@ -47,15 +47,16 @@ module OneLogin
47
47
  @document = XMLSecurity::SignedDocument.new(@response)
48
48
  end
49
49
 
50
+ def response_id
51
+ id(document)
52
+ end
53
+
50
54
  # Checks if the Status has the "Success" code
51
55
  # @return [Boolean] True if the StatusCode is Sucess
52
56
  # @raise [ValidationError] if soft == false and validation fails
53
57
  #
54
58
  def success?
55
- unless status_code == "urn:oasis:names:tc:SAML:2.0:status:Success"
56
- return append_error("Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <#@status_code>")
57
- end
58
- true
59
+ return status_code == "urn:oasis:names:tc:SAML:2.0:status:Success"
59
60
  end
60
61
 
61
62
  # @return [String|nil] Gets the InResponseTo attribute from the Logout Response if exists.
@@ -65,7 +66,7 @@ module OneLogin
65
66
  node = REXML::XPath.first(
66
67
  document,
67
68
  "/p:LogoutResponse",
68
- { "p" => PROTOCOL, "a" => ASSERTION }
69
+ { "p" => PROTOCOL }
69
70
  )
70
71
  node.nil? ? nil : node.attributes['InResponseTo']
71
72
  end
@@ -88,7 +89,7 @@ module OneLogin
88
89
  #
89
90
  def status_code
90
91
  @status_code ||= begin
91
- node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => PROTOCOL, "a" => ASSERTION })
92
+ node = REXML::XPath.first(document, "/p:LogoutResponse/p:Status/p:StatusCode", { "p" => PROTOCOL })
92
93
  node.nil? ? nil : node.attributes["Value"]
93
94
  end
94
95
  end
@@ -98,7 +99,7 @@ module OneLogin
98
99
  node = REXML::XPath.first(
99
100
  document,
100
101
  "/p:LogoutResponse/p:Status/p:StatusMessage",
101
- { "p" => PROTOCOL, "a" => ASSERTION }
102
+ { "p" => PROTOCOL }
102
103
  )
103
104
  Utils.element_text(node)
104
105
  end
@@ -166,7 +167,7 @@ module OneLogin
166
167
 
167
168
  return append_error("No settings on logout response") if settings.nil?
168
169
 
169
- return append_error("No issuer in settings of the logout response") if settings.issuer.nil?
170
+ return append_error("No sp_entity_id in settings of the logout response") if settings.sp_entity_id.nil?
170
171
 
171
172
  if settings.idp_cert_fingerprint.nil? && settings.idp_cert.nil? && settings.idp_cert_multi.nil?
172
173
  return append_error("No fingerprint or certificate on settings of the logout response")
@@ -231,13 +232,19 @@ module OneLogin
231
232
  :raw_sig_alg => options[:raw_get_params]['SigAlg']
232
233
  )
233
234
 
235
+ expired = false
234
236
  if idp_certs.nil? || idp_certs[:signing].empty?
235
237
  valid = OneLogin::RubySaml::Utils.verify_signature(
236
- :cert => settings.get_idp_cert,
238
+ :cert => idp_cert,
237
239
  :sig_alg => options[:get_params]['SigAlg'],
238
240
  :signature => options[:get_params]['Signature'],
239
241
  :query_string => query_string
240
242
  )
243
+ if valid && settings.security[:check_idp_cert_expiration]
244
+ if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
245
+ expired = true
246
+ end
247
+ end
241
248
  else
242
249
  valid = false
243
250
  idp_certs[:signing].each do |signing_idp_cert|
@@ -248,11 +255,20 @@ module OneLogin
248
255
  :query_string => query_string
249
256
  )
250
257
  if valid
258
+ if settings.security[:check_idp_cert_expiration]
259
+ if OneLogin::RubySaml::Utils.is_cert_expired(signing_idp_cert)
260
+ expired = true
261
+ end
262
+ end
251
263
  break
252
264
  end
253
265
  end
254
266
  end
255
267
 
268
+ if expired
269
+ error_msg = "IdP x509 certificate expired"
270
+ return append_error(error_msg)
271
+ end
256
272
  unless valid
257
273
  error_msg = "Invalid Signature on Logout Response"
258
274
  return append_error(error_msg)
@@ -15,9 +15,11 @@ module OneLogin
15
15
  # @param settings [OneLogin::RubySaml::Settings|nil] Toolkit settings
16
16
  # @param pretty_print [Boolean] Pretty print or not the response
17
17
  # (No pretty print if you gonna validate the signature)
18
+ # @param valid_until [DateTime] Metadata's valid time
19
+ # @param cache_duration [Integer] Duration of the cache in seconds
18
20
  # @return [String] XML Metadata of the Service Provider
19
21
  #
20
- def generate(settings, pretty_print=false)
22
+ def generate(settings, pretty_print=false, valid_until=nil, cache_duration=nil)
21
23
  meta_doc = XMLSecurity::Document.new
22
24
  namespaces = {
23
25
  "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
@@ -57,8 +59,14 @@ module OneLogin
57
59
  end
58
60
 
59
61
  root.attributes["ID"] = OneLogin::RubySaml::Utils.uuid
60
- if settings.issuer
61
- root.attributes["entityID"] = settings.issuer
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"
62
70
  end
63
71
  if settings.single_logout_service_url
64
72
  sp_sso.add_element "md:SingleLogoutService", {
@@ -34,7 +34,7 @@ module OneLogin
34
34
  # This is not a whitelist to allow people extending OneLogin::RubySaml:Response
35
35
  # and pass custom options
36
36
  AVAILABLE_OPTIONS = [
37
- :allowed_clock_drift, :check_duplicated_attributes, :matches_request_id, :settings, :skip_authnstatement, :skip_conditions,
37
+ :allowed_clock_drift, :check_duplicated_attributes, :matches_request_id, :settings, :skip_audience, :skip_authnstatement, :skip_conditions,
38
38
  :skip_destination, :skip_recipient_check, :skip_subject_confirmation
39
39
  ]
40
40
  # TODO: Update the comment on initialize to describe every option
@@ -47,6 +47,8 @@ module OneLogin
47
47
  # or :matches_request_id that will validate that the response matches the ID of the request,
48
48
  # or skip the subject confirmation validation with the :skip_subject_confirmation option
49
49
  # or skip the recipient validation of the subject confirmation element with :skip_recipient_check option
50
+ # or skip the audience validation with :skip_audience option
51
+ #
50
52
  def initialize(response, options = {})
51
53
  raise ArgumentError.new("Response cannot be nil") if response.nil?
52
54
 
@@ -341,6 +343,28 @@ module OneLogin
341
343
  return options[:allowed_clock_drift].to_f
342
344
  end
343
345
 
346
+ # Checks if the SAML Response contains or not an EncryptedAssertion element
347
+ # @return [Boolean] True if the SAML Response contains an EncryptedAssertion element
348
+ #
349
+ def assertion_encrypted?
350
+ ! REXML::XPath.first(
351
+ document,
352
+ "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
353
+ { "p" => PROTOCOL, "a" => ASSERTION }
354
+ ).nil?
355
+ end
356
+
357
+ def response_id
358
+ id(document)
359
+ end
360
+
361
+ def assertion_id
362
+ @assertion_id ||= begin
363
+ node = xpath_first_from_signed_assertion("")
364
+ node.nil? ? nil : node.attributes['ID']
365
+ end
366
+ end
367
+
344
368
  private
345
369
 
346
370
  # Validates the SAML Response (calls several validation methods)
@@ -435,7 +459,7 @@ module OneLogin
435
459
  # @return [Boolean] True if the SAML Response contains an ID, otherwise returns False
436
460
  #
437
461
  def validate_id
438
- unless id(document)
462
+ unless response_id
439
463
  return append_error("Missing ID attribute on SAML Response")
440
464
  end
441
465
 
@@ -584,16 +608,18 @@ module OneLogin
584
608
  end
585
609
 
586
610
  # Validates the Audience, (If the Audience match the Service Provider EntityID)
611
+ # If the response was initialized with the :skip_audience option, this validation is skipped,
587
612
  # If fails, the error is added to the errors array
588
613
  # @return [Boolean] True if there is an Audience Element that match the Service Provider EntityID, otherwise False if soft=True
589
614
  # @raise [ValidationError] if soft == false and validation fails
590
615
  #
591
616
  def validate_audience
592
- return true if audiences.empty? || settings.issuer.nil? || settings.issuer.empty?
617
+ return true if options[:skip_audience]
618
+ return true if audiences.empty? || settings.sp_entity_id.nil? || settings.sp_entity_id.empty?
593
619
 
594
- unless audiences.include? settings.issuer
620
+ unless audiences.include? settings.sp_entity_id
595
621
  s = audiences.count > 1 ? 's' : '';
596
- error_msg = "Invalid Audience#{s}. The audience#{s} #{audiences.join(',')}, did not match the expected audience #{settings.issuer}"
622
+ error_msg = "Invalid Audience#{s}. The audience#{s} #{audiences.join(',')}, did not match the expected audience #{settings.sp_entity_id}"
597
623
  return append_error(error_msg)
598
624
  end
599
625
 
@@ -781,8 +807,8 @@ module OneLogin
781
807
  return append_error("An empty NameID value found")
782
808
  end
783
809
 
784
- unless settings.issuer.nil? || settings.issuer.empty? || name_id_spnamequalifier.nil? || name_id_spnamequalifier.empty?
785
- if name_id_spnamequalifier != settings.issuer
810
+ unless settings.sp_entity_id.nil? || settings.sp_entity_id.empty? || name_id_spnamequalifier.nil? || name_id_spnamequalifier.empty?
811
+ if name_id_spnamequalifier != settings.sp_entity_id
786
812
  return append_error("The SPNameQualifier value mistmatch the SP entityID value.")
787
813
  end
788
814
  end
@@ -821,28 +847,59 @@ module OneLogin
821
847
  end
822
848
 
823
849
  if sig_elements.size != 1
850
+ if sig_elements.size == 0
851
+ append_error("Signed element id ##{doc.signed_element_id} is not found")
852
+ else
853
+ append_error("Signed element id ##{doc.signed_element_id} is found more than once")
854
+ end
824
855
  return append_error(error_msg)
825
856
  end
826
857
 
858
+ old_errors = @errors.clone
859
+
827
860
  idp_certs = settings.get_idp_cert_multi
828
861
  if idp_certs.nil? || idp_certs[:signing].empty?
829
862
  opts = {}
830
863
  opts[:fingerprint_alg] = settings.idp_cert_fingerprint_algorithm
831
- opts[:cert] = settings.get_idp_cert
864
+ idp_cert = settings.get_idp_cert
832
865
  fingerprint = settings.get_fingerprint
866
+ opts[:cert] = idp_cert
833
867
 
834
- unless fingerprint && doc.validate_document(fingerprint, @soft, opts)
868
+ if fingerprint && doc.validate_document(fingerprint, @soft, opts)
869
+ if settings.security[:check_idp_cert_expiration]
870
+ if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
871
+ error_msg = "IdP x509 certificate expired"
872
+ return append_error(error_msg)
873
+ end
874
+ end
875
+ else
835
876
  return append_error(error_msg)
836
877
  end
837
878
  else
838
879
  valid = false
880
+ expired = false
839
881
  idp_certs[:signing].each do |idp_cert|
840
- valid = doc.validate_document_with_cert(idp_cert)
882
+ valid = doc.validate_document_with_cert(idp_cert, true)
841
883
  if valid
884
+ if settings.security[:check_idp_cert_expiration]
885
+ if OneLogin::RubySaml::Utils.is_cert_expired(idp_cert)
886
+ expired = true
887
+ end
888
+ end
889
+
890
+ # At least one certificate is valid, restore the old accumulated errors
891
+ @errors = old_errors
842
892
  break
843
893
  end
894
+
895
+ end
896
+ if expired
897
+ error_msg = "IdP x509 certificate expired"
898
+ return append_error(error_msg)
844
899
  end
845
900
  unless valid
901
+ # Remove duplicated errors
902
+ @errors = @errors.uniq
846
903
  return append_error(error_msg)
847
904
  end
848
905
  end
@@ -943,17 +1000,6 @@ module OneLogin
943
1000
  XMLSecurity::SignedDocument.new(response_node.to_s)
944
1001
  end
945
1002
 
946
- # Checks if the SAML Response contains or not an EncryptedAssertion element
947
- # @return [Boolean] True if the SAML Response contains an EncryptedAssertion element
948
- #
949
- def assertion_encrypted?
950
- ! REXML::XPath.first(
951
- document,
952
- "(/p:Response/EncryptedAssertion/)|(/p:Response/a:EncryptedAssertion/)",
953
- { "p" => PROTOCOL, "a" => ASSERTION }
954
- ).nil?
955
- end
956
-
957
1003
  # Decrypts an EncryptedAssertion element
958
1004
  # @param encrypted_assertion_node [REXML::Element] The EncryptedAssertion element
959
1005
  # @return [REXML::Document] The decrypted EncryptedAssertion element
@@ -22,6 +22,8 @@ module OneLogin
22
22
  BASE64_FORMAT = %r(\A([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?\Z)
23
23
  @@mutex = Mutex.new
24
24
 
25
+ MAX_BYTE_SIZE = 250000
26
+
25
27
  # @return [Nokogiri::XML::Schema] Gets the schema object of the SAML 2.0 Protocol schema
26
28
  #
27
29
  def self.schema
@@ -89,6 +91,10 @@ module OneLogin
89
91
  def decode_raw_saml(saml)
90
92
  return saml unless base64_encoded?(saml)
91
93
 
94
+ if saml.bytesize > MAX_BYTE_SIZE
95
+ raise ValidationError.new("Encoded SAML Message exceeds " + MAX_BYTE_SIZE.to_s + " bytes, so was rejected")
96
+ end
97
+
92
98
  decoded = decode(saml)
93
99
  begin
94
100
  inflate(decoded)
@@ -105,7 +111,7 @@ module OneLogin
105
111
  def encode_raw_saml(saml, settings)
106
112
  saml = deflate(saml) if settings.compress_request
107
113
 
108
- CGI.escape(Base64.encode64(saml))
114
+ CGI.escape(encode(saml))
109
115
  end
110
116
 
111
117
  # Base 64 decode method
@@ -121,7 +127,11 @@ module OneLogin
121
127
  # @return [String] The encoded string
122
128
  #
123
129
  def encode(string)
124
- Base64.encode64(string).gsub(/\n/, "")
130
+ if Base64.respond_to?('strict_encode64')
131
+ Base64.strict_encode64(string)
132
+ else
133
+ Base64.encode64(string).gsub(/\n/, "")
134
+ end
125
135
  end
126
136
 
127
137
  # Check if a string is base64 encoded
@@ -0,0 +1,6 @@
1
+ module OneLogin
2
+ module RubySaml
3
+ class SettingError < StandardError
4
+ end
5
+ end
6
+ end
@@ -1,6 +1,7 @@
1
1
  require "xml_security"
2
2
  require "onelogin/ruby-saml/attribute_service"
3
3
  require "onelogin/ruby-saml/utils"
4
+ require "onelogin/ruby-saml/validation_error"
4
5
 
5
6
  # Only supports SAML 2.0
6
7
  module OneLogin
@@ -30,21 +31,24 @@ module OneLogin
30
31
 
31
32
  # IdP Data
32
33
  attr_accessor :idp_entity_id
33
- attr_accessor :idp_sso_target_url
34
- attr_accessor :idp_slo_target_url
34
+
35
+ attr_accessor :idp_sso_service_url
36
+ attr_accessor :idp_slo_service_url
37
+ attr_accessor :idp_slo_response_service_url
35
38
  attr_accessor :idp_cert
36
39
  attr_accessor :idp_cert_fingerprint
37
40
  attr_accessor :idp_cert_fingerprint_algorithm
38
41
  attr_accessor :idp_cert_multi
39
42
  attr_accessor :idp_attribute_names
40
43
  attr_accessor :idp_name_qualifier
44
+ attr_accessor :valid_until
41
45
  # SP Data
42
- attr_accessor :issuer
43
46
  attr_accessor :assertion_consumer_service_url
44
47
  attr_accessor :assertion_consumer_service_binding
45
48
  attr_accessor :sp_name_qualifier
46
49
  attr_accessor :name_identifier_format
47
50
  attr_accessor :name_identifier_value
51
+ attr_accessor :name_identifier_value_requested
48
52
  attr_accessor :sessionindex
49
53
  attr_accessor :compress_request
50
54
  attr_accessor :compress_response
@@ -66,6 +70,58 @@ module OneLogin
66
70
  # Compability
67
71
  attr_accessor :assertion_consumer_logout_service_url
68
72
  attr_accessor :assertion_consumer_logout_service_binding
73
+ attr_accessor :issuer
74
+ attr_accessor :idp_sso_target_url
75
+ attr_accessor :idp_slo_target_url
76
+
77
+ # @return [String] IdP Single Sign On Service URL
78
+ #
79
+ def idp_sso_service_url
80
+ val = nil
81
+ if @idp_sso_service_url.nil?
82
+ if @idp_sso_target_url
83
+ val = @idp_sso_target_url
84
+ end
85
+ else
86
+ val = @idp_sso_service_url
87
+ end
88
+ val
89
+ end
90
+
91
+ # @return [String] IdP Single Logout Service URL
92
+ #
93
+ def idp_slo_service_url
94
+ val = nil
95
+ if @idp_slo_service_url.nil?
96
+ if @idp_slo_target_url
97
+ val = @idp_slo_target_url
98
+ end
99
+ else
100
+ val = @idp_slo_service_url
101
+ end
102
+ val
103
+ end
104
+
105
+ # @return [String] SP Entity ID
106
+ #
107
+ def sp_entity_id
108
+ val = nil
109
+ if @sp_entity_id.nil?
110
+ if @issuer
111
+ val = @issuer
112
+ end
113
+ else
114
+ val = @sp_entity_id
115
+ end
116
+ val
117
+ end
118
+
119
+ # Setter for SP Entity ID.
120
+ # @param val [String].
121
+ #
122
+ def sp_entity_id=(val)
123
+ @sp_entity_id = val
124
+ end
69
125
 
70
126
  # @return [String] Single Logout Service URL.
71
127
  #
@@ -165,7 +221,15 @@ module OneLogin
165
221
  return nil if certificate.nil? || certificate.empty?
166
222
 
167
223
  formatted_cert = OneLogin::RubySaml::Utils.format_cert(certificate)
168
- OpenSSL::X509::Certificate.new(formatted_cert)
224
+ cert = OpenSSL::X509::Certificate.new(formatted_cert)
225
+
226
+ if security[:check_sp_cert_expiration]
227
+ if OneLogin::RubySaml::Utils.is_cert_expired(cert)
228
+ raise OneLogin::RubySaml::ValidationError.new("The SP certificate expired.")
229
+ end
230
+ end
231
+
232
+ cert
169
233
  end
170
234
 
171
235
  # @return [OpenSSL::X509::Certificate|nil] Build the New SP certificate from the settings (previously format it)
@@ -195,6 +259,7 @@ module OneLogin
195
259
  :compress_request => true,
196
260
  :compress_response => true,
197
261
  :soft => true,
262
+ :double_quote_xml_attribute_values => false,
198
263
  :security => {
199
264
  :authn_requests_signed => false,
200
265
  :logout_requests_signed => false,
@@ -205,9 +270,10 @@ module OneLogin
205
270
  :metadata_signed => false,
206
271
  :embed_sign => false,
207
272
  :digest_method => XMLSecurity::Document::SHA1,
208
- :signature_method => XMLSecurity::Document::RSA_SHA1
209
- }.freeze,
210
- :double_quote_xml_attribute_values => false,
273
+ :signature_method => XMLSecurity::Document::RSA_SHA1,
274
+ :check_idp_cert_expiration => false,
275
+ :check_sp_cert_expiration => false
276
+ }.freeze
211
277
  }.freeze
212
278
  end
213
279
  end