ruby-saml 0.9.4 → 1.0.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/LICENSE +1 -1
  4. data/README.md +71 -15
  5. data/changelog.md +15 -6
  6. data/lib/onelogin/ruby-saml.rb +1 -0
  7. data/lib/onelogin/ruby-saml/attribute_service.rb +25 -2
  8. data/lib/onelogin/ruby-saml/attributes.rb +42 -23
  9. data/lib/onelogin/ruby-saml/authrequest.rb +33 -8
  10. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +65 -10
  12. data/lib/onelogin/ruby-saml/logging.rb +14 -10
  13. data/lib/onelogin/ruby-saml/logoutrequest.rb +39 -14
  14. data/lib/onelogin/ruby-saml/logoutresponse.rb +166 -39
  15. data/lib/onelogin/ruby-saml/metadata.rb +40 -23
  16. data/lib/onelogin/ruby-saml/response.rb +562 -88
  17. data/lib/onelogin/ruby-saml/saml_message.rb +80 -14
  18. data/lib/onelogin/ruby-saml/settings.rb +62 -23
  19. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +210 -20
  20. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +44 -13
  21. data/lib/onelogin/ruby-saml/utils.rb +163 -40
  22. data/lib/onelogin/ruby-saml/version.rb +1 -1
  23. data/lib/schemas/saml-schema-metadata-2.0.xsd +0 -2
  24. data/lib/xml_security.rb +87 -29
  25. data/ruby-saml.gemspec +1 -0
  26. data/test/certificates/{r1_certificate2_base64 → certificate_without_head_foot} +0 -0
  27. data/test/certificates/formatted_certificate +14 -0
  28. data/test/certificates/formatted_private_key +12 -0
  29. data/test/certificates/formatted_rsa_private_key +12 -0
  30. data/test/certificates/invalid_certificate1 +1 -0
  31. data/test/certificates/invalid_certificate2 +1 -0
  32. data/test/certificates/invalid_certificate3 +12 -0
  33. data/test/certificates/invalid_private_key1 +1 -0
  34. data/test/certificates/invalid_private_key2 +1 -0
  35. data/test/certificates/invalid_private_key3 +10 -0
  36. data/test/certificates/invalid_rsa_private_key1 +1 -0
  37. data/test/certificates/invalid_rsa_private_key2 +1 -0
  38. data/test/certificates/invalid_rsa_private_key3 +10 -0
  39. data/test/idp_metadata_parser_test.rb +41 -4
  40. data/test/logging_test.rb +62 -0
  41. data/test/logout_requests/invalid_slo_request.xml +6 -0
  42. data/test/{responses → logout_requests}/slo_request.xml +0 -0
  43. data/test/logout_requests/slo_request.xml.base64 +1 -0
  44. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  45. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  46. data/test/{responses → logout_responses}/logoutresponse_fixtures.rb +6 -6
  47. data/test/logoutrequest_test.rb +79 -52
  48. data/test/logoutresponse_test.rb +206 -59
  49. data/test/metadata_test.rb +77 -7
  50. data/test/request_test.rb +80 -65
  51. data/test/response_test.rb +862 -189
  52. data/test/responses/attackxee.xml +13 -0
  53. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  54. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  55. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  56. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  57. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  58. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  59. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  60. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  61. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  62. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  63. data/test/responses/invalids/no_id.xml.base64 +1 -0
  64. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  65. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  66. data/test/responses/invalids/no_status.xml.base64 +1 -0
  67. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  68. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  69. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  70. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  71. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  72. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  73. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  74. data/test/responses/{response4.xml.base64 → response_assertion_wrapped.xml.base64} +0 -0
  75. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  76. data/test/responses/response_unsigned_xml_base64 +1 -0
  77. data/test/responses/{response5.xml.base64 → response_with_saml2_namespace.xml.base64} +0 -0
  78. data/test/responses/{response3.xml.base64 → response_with_signed_assertion.xml.base64} +0 -0
  79. data/test/responses/{r1_response6.xml.base64 → response_with_signed_assertion_2.xml.base64} +0 -0
  80. data/test/responses/{response1.xml.base64 → response_with_undefined_recipient.xml.base64} +0 -0
  81. data/test/responses/{response2.xml.base64 → response_without_attributes.xml.base64} +0 -0
  82. data/test/responses/{wrapped_response_2.xml.base64 → response_wrapped.xml.base64} +0 -0
  83. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  84. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  85. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  86. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  87. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  88. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  89. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  90. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  91. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  92. data/test/responses/valid_response.xml.base64 +1 -0
  93. data/test/saml_message_test.rb +56 -0
  94. data/test/settings_test.rb +138 -1
  95. data/test/slo_logoutrequest_test.rb +239 -28
  96. data/test/slo_logoutresponse_test.rb +93 -71
  97. data/test/test_helper.rb +138 -31
  98. data/test/utils_test.rb +129 -25
  99. data/test/xml_security_test.rb +140 -71
  100. metadata +142 -25
  101. data/test/responses/response_node_text_attack.xml.base64 +0 -1
@@ -1,110 +1,257 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
2
 
3
3
  require 'onelogin/ruby-saml/logoutresponse'
4
- require 'responses/logoutresponse_fixtures'
4
+ require 'logout_responses/logoutresponse_fixtures'
5
5
 
6
6
  class RubySamlTest < Minitest::Test
7
7
 
8
8
  describe "Logoutresponse" do
9
+
10
+ let(:valid_logout_response_without_settings) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document) }
11
+ let(:valid_logout_response) { OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings) }
12
+
9
13
  describe "#new" do
10
14
  it "raise an exception when response is initialized with nil" do
11
15
  assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
12
16
  end
13
17
  it "default to empty settings" do
14
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response)
15
- assert_nil logoutresponse.settings
18
+ assert_nil valid_logout_response_without_settings.settings
16
19
  end
17
20
  it "accept constructor-injected settings" do
18
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
19
- refute_nil logoutresponse.settings
21
+ refute_nil valid_logout_response.settings
20
22
  end
21
23
  it "accept constructor-injected options" do
22
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
24
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} )
23
25
  assert !logoutresponse.options.empty?
24
26
  end
25
27
  it "support base64 encoded responses" do
26
- expected_response = valid_response
27
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings)
28
-
29
- assert_equal expected_response, logoutresponse.response
28
+ generated_logout_response = valid_logout_response_document
29
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(generated_logout_response), settings)
30
+ assert_equal generated_logout_response, logoutresponse.response
30
31
  end
31
32
  end
32
33
 
34
+ describe "#validate_structure" do
35
+ it "invalidates when the logout response has an invalid xml" do
36
+ settings.soft = true
37
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
38
+ assert !logoutresponse.send(:validate_structure)
39
+ assert_includes logoutresponse.errors, "Invalid SAML Logout Response. Not match the saml-schema-protocol-2.0.xsd"
40
+ end
41
+
42
+ it "raise when the logout response has an invalid xml" do
43
+ settings.soft = false
44
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_logout_response_document, settings)
45
+ assert_raises OneLogin::RubySaml::ValidationError do
46
+ logoutresponse.send(:validate_structure)
47
+ end
48
+ end
49
+ end
50
+
33
51
  describe "#validate" do
34
- it "validate the response" do
35
- in_relation_to_request_id = random_id
52
+ describe "when soft=true" do
53
+ before do
54
+ settings.soft = true
55
+ end
36
56
 
37
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
57
+ it "validate the logout response" do
58
+ in_relation_to_request_id = random_id
59
+ opts = { :matches_request_id => in_relation_to_request_id}
38
60
 
39
- assert logoutresponse.validate
61
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
40
62
 
41
- assert_equal settings.issuer, logoutresponse.issuer
42
- assert_equal in_relation_to_request_id, logoutresponse.in_response_to
63
+ assert logoutresponse.validate
43
64
 
44
- assert logoutresponse.success?
45
- end
65
+ assert_equal settings.issuer, logoutresponse.issuer
66
+ assert_equal in_relation_to_request_id, logoutresponse.in_response_to
46
67
 
47
- it "invalidate responses with wrong id when given option :matches_uuid" do
68
+ assert logoutresponse.success?
69
+ assert_empty logoutresponse.errors
70
+ end
48
71
 
49
- expected_request_id = "_some_other_expected_uuid"
50
- opts = { :matches_request_id => expected_request_id}
72
+ it "validate the logout response extended" do
73
+ in_relation_to_request_id = random_id
74
+ settings.idp_entity_id = 'http://app.muda.no'
75
+ opts = { :matches_request_id => in_relation_to_request_id}
51
76
 
52
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
77
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
78
+ assert logoutresponse.validate
79
+ assert_equal in_relation_to_request_id, logoutresponse.in_response_to
80
+ assert logoutresponse.success?
81
+ assert_empty logoutresponse.errors
82
+ end
53
83
 
54
- assert !logoutresponse.validate
55
- refute_equal expected_request_id, logoutresponse.in_response_to
56
- end
84
+ it "invalidate logout response when initiated with blank" do
85
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
86
+ assert !logoutresponse.validate
87
+ assert_includes logoutresponse.errors, "Blank logout response"
88
+ end
57
89
 
58
- it "invalidate responses with wrong request status" do
59
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
90
+ it "invalidate logout response when initiated with no idp cert or fingerprint" do
91
+ settings.idp_cert_fingerprint = nil
92
+ settings.idp_cert = nil
93
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
94
+ assert !logoutresponse.validate
95
+ assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
96
+ end
60
97
 
61
- assert !logoutresponse.validate
62
- assert !logoutresponse.success?
63
- end
64
- end
98
+ it "invalidate logout response with wrong id when given option :matches_request_id" do
99
+ expected_request_id = "_some_other_expected_uuid"
100
+ opts = { :matches_request_id => expected_request_id}
65
101
 
66
- describe "#validate!" do
67
- it "validates good responses" do
68
- in_relation_to_request_id = random_id
102
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
69
103
 
70
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
104
+ assert !logoutresponse.validate
105
+ refute_equal expected_request_id, logoutresponse.in_response_to
106
+ assert_includes logoutresponse.errors, "Response does not match the request ID, expected: <#{expected_request_id}>, but was: <#{logoutresponse.in_response_to}>"
107
+ end
71
108
 
72
- logoutresponse.validate!
73
- end
109
+ it "invalidate logout response with wrong request status" do
110
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
74
111
 
75
- it "raises validation error when matching for wrong request id" do
112
+ assert !logoutresponse.success?
113
+ assert !logoutresponse.validate
114
+ assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
115
+ assert_includes logoutresponse.errors, "The status code of the Logout Response was not Success, was Requester"
116
+ end
76
117
 
77
- expected_request_id = "_some_other_expected_id"
78
- opts = { :matches_request_id => expected_request_id}
118
+ it "invalidate logout response when in lack of issuer setting" do
119
+ bad_settings = settings
120
+ bad_settings.issuer = nil
121
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, bad_settings)
122
+ assert !logoutresponse.validate
123
+ assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
124
+ end
79
125
 
80
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
126
+ it "invalidate logout response with wrong issuer" do
127
+ in_relation_to_request_id = random_id
128
+ settings.idp_entity_id = 'http://invalid.issuer.example.com/'
129
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
130
+ assert !logoutresponse.validate
131
+ assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
132
+ end
81
133
 
82
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
83
134
  end
84
135
 
85
- it "raise validation error for wrong request status" do
86
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
136
+ describe "when soft=false" do
137
+ before do
138
+ settings.soft = false
139
+ end
87
140
 
88
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
89
- end
141
+ it "validates good logout response" do
142
+ in_relation_to_request_id = random_id
90
143
 
91
- it "raise validation error when in bad state" do
92
- # no settings
93
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response)
94
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
95
- end
144
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
145
+ assert logoutresponse.validate
146
+ assert_empty logoutresponse.errors
147
+ end
148
+
149
+ it "raises validation error when response initiated with blank" do
150
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new("", settings)
96
151
 
97
- it "raise validation error when in lack of issuer setting" do
98
- bad_settings = settings
99
- bad_settings.issuer = nil
100
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings)
101
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
152
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
153
+ assert_includes logoutresponse.errors, "Blank logout response"
154
+ end
155
+
156
+ it "raises validation error when initiated with no idp cert or fingerprint" do
157
+ settings.idp_cert_fingerprint = nil
158
+ settings.idp_cert = nil
159
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings)
160
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
161
+ assert_includes logoutresponse.errors, "No fingerprint or certificate on settings of the logout response"
162
+ end
163
+
164
+ it "raises validation error when matching for wrong request id" do
165
+
166
+ expected_request_id = "_some_other_expected_id"
167
+ opts = { :matches_request_id => expected_request_id}
168
+
169
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
170
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
171
+ assert_includes logoutresponse.errors, "Response does not match the request ID, expected: <#{expected_request_id}>, but was: <#{logoutresponse.in_response_to}>"
172
+ end
173
+
174
+ it "raise validation error for wrong request status" do
175
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
176
+
177
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
178
+ assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
179
+ end
180
+
181
+ it "raise validation error when in bad state" do
182
+ # no settings
183
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
184
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
185
+ assert_includes logoutresponse.errors, "Bad status code. Expected <urn:oasis:names:tc:SAML:2.0:status:Success>, but was: <urn:oasis:names:tc:SAML:2.0:status:Requester>"
186
+ end
187
+
188
+ it "raise validation error when in lack of issuer setting" do
189
+ settings.issuer = nil
190
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
191
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
192
+ assert_includes logoutresponse.errors, "No issuer in settings of the logout response"
193
+ end
194
+
195
+ it "raise validation error when logout response with wrong issuer" do
196
+ in_relation_to_request_id = random_id
197
+ settings.idp_entity_id = 'http://invalid.issuer.example.com/'
198
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings)
199
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate }
200
+ assert_includes logoutresponse.errors, "Doesn't match the issuer, expected: <#{logoutresponse.settings.idp_entity_id}>, but was: <http://app.muda.no>"
201
+ end
102
202
  end
103
203
 
104
- it "raise error for invalid xml" do
105
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
204
+ describe "#validate_signature" do
205
+ let (:params) { OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, random_id, "Custom Logout Message", :RelayState => 'http://example.com') }
206
+
207
+ before do
208
+ settings.soft = true
209
+ settings.idp_slo_target_url = "http://example.com?field=value"
210
+ settings.security[:logout_responses_signed] = true
211
+ settings.security[:embed_sign] = false
212
+ settings.certificate = ruby_saml_cert_text
213
+ settings.private_key = ruby_saml_key_text
214
+ settings.idp_cert = ruby_saml_cert_text
215
+ end
216
+
217
+ it "return true when valid RSA_SHA1 Signature" do
218
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
219
+ params['RelayState'] = params[:RelayState]
220
+ options = {}
221
+ options[:get_params] = params
222
+ logoutresponse_sign_test = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
223
+ assert logoutresponse_sign_test.send(:validate_signature)
224
+ end
225
+
226
+ it "return true when valid RSA_SHA256 Signature" do
227
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
228
+ params['RelayState'] = params[:RelayState]
229
+ options = {}
230
+ options[:get_params] = params
231
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
232
+ assert logoutresponse.send(:validate_signature)
233
+ end
234
+
235
+ it "return false when invalid RSA_SHA1 Signature" do
236
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
237
+ params['RelayState'] = 'http://invalid.example.com'
238
+ options = {}
239
+ options[:get_params] = params
240
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
241
+ assert !logoutresponse.send(:validate_signature)
242
+ end
243
+
244
+ it "raise when invalid RSA_SHA1 Signature" do
245
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
246
+ settings.soft = false
247
+ params['RelayState'] = 'http://invalid.example.com'
248
+ options = {}
249
+ options[:get_params] = params
250
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(params['SAMLResponse'], settings, options)
106
251
 
107
- assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
252
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
253
+ assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
254
+ end
108
255
  end
109
256
  end
110
257
  end
@@ -33,10 +33,31 @@ class MetadataTest < Minitest::Test
33
33
 
34
34
  assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
35
35
  assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
36
+
37
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
36
38
  end
37
39
 
38
40
  it "generates Service Provider Metadata" do
39
- # assert correct xml declaration
41
+ settings.single_logout_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
42
+ settings.single_logout_service_url = "https://foo.example/saml/sls"
43
+ xml_metadata = OneLogin::RubySaml::Metadata.new.generate(settings, false)
44
+
45
+ start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
46
+ assert_equal xml_metadata[0..start.length-1],start
47
+
48
+ doc_metadata = REXML::Document.new(xml_metadata)
49
+ sls = REXML::XPath.first(doc_metadata, "//md:SingleLogoutService")
50
+
51
+ assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect", sls.attribute("Binding").value
52
+ assert_equal "https://foo.example/saml/sls", sls.attribute("Location").value
53
+ assert_equal "https://foo.example/saml/sls", sls.attribute("ResponseLocation").value
54
+ assert_nil sls.attribute("isDefault")
55
+ assert_nil sls.attribute("index")
56
+
57
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
58
+ end
59
+
60
+ it "generates Service Provider Metadata with single logout service" do
40
61
  start = "<?xml version='1.0' encoding='UTF-8'?><md:EntityDescriptor"
41
62
  assert_equal xml_text[0..start.length-1], start
42
63
 
@@ -50,27 +71,50 @@ class MetadataTest < Minitest::Test
50
71
 
51
72
  assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
52
73
  assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
74
+
75
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
53
76
  end
54
77
 
55
78
  describe "when auth requests are signed" do
56
- let(:cert_node) do
57
- REXML::XPath.first(
79
+ let(:key_descriptors) do
80
+ REXML::XPath.match(
81
+ xml_doc,
82
+ "//md:KeyDescriptor",
83
+ "md" => "urn:oasis:names:tc:SAML:2.0:metadata"
84
+ )
85
+ end
86
+ let(:cert_nodes) do
87
+ REXML::XPath.match(
58
88
  xml_doc,
59
89
  "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate",
60
90
  "md" => "urn:oasis:names:tc:SAML:2.0:metadata",
61
91
  "ds" => "http://www.w3.org/2000/09/xmldsig#"
62
92
  )
63
93
  end
64
- let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(cert_node.text)) }
94
+ let(:cert) { OpenSSL::X509::Certificate.new(Base64.decode64(cert_nodes[0].text)) }
65
95
 
66
96
  before do
67
- settings.security[:authn_requests_signed] = true
68
97
  settings.certificate = ruby_saml_cert_text
69
98
  end
70
99
 
71
- it "generates Service Provider Metadata with X509Certificate" do
100
+ it "generates Service Provider Metadata with AuthnRequestsSigned" do
101
+ settings.security[:authn_requests_signed] = true
72
102
  assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value
73
103
  assert_equal ruby_saml_cert.to_der, cert.to_der
104
+
105
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
106
+ end
107
+
108
+ it "generates Service Provider Metadata with X509Certificate for sign and encrypt" do
109
+ assert_equal 2, key_descriptors.length
110
+ assert_equal "signing", key_descriptors[0].attribute("use").value
111
+ assert_equal "encryption", key_descriptors[1].attribute("use").value
112
+
113
+ assert_equal 2, cert_nodes.length
114
+ assert_equal ruby_saml_cert.to_der, cert.to_der
115
+ assert_equal cert_nodes[0].text, cert_nodes[1].text
116
+
117
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
74
118
  end
75
119
  end
76
120
 
@@ -93,7 +137,29 @@ class MetadataTest < Minitest::Test
93
137
  assert_equal "Name", req_attr.attribute("Name").value
94
138
  assert_equal "Name Format", req_attr.attribute("NameFormat").value
95
139
  assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value
96
- assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//md:AttributeValue").text.strip
140
+ assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//saml:AttributeValue").text.strip
141
+
142
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
143
+ end
144
+
145
+ describe "#service_name" do
146
+ before do
147
+ settings.attribute_consuming_service.service_name("Test2 Service")
148
+ end
149
+
150
+ it "change service name" do
151
+ assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test2 Service"
152
+ end
153
+ end
154
+
155
+ describe "#service_index" do
156
+ before do
157
+ settings.attribute_consuming_service.service_index(2)
158
+ end
159
+
160
+ it "change service index" do
161
+ assert_equal "2", attr_svc.attribute("index").value
162
+ end
97
163
  end
98
164
  end
99
165
 
@@ -110,6 +176,8 @@ class MetadataTest < Minitest::Test
110
176
  assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], xml_text
111
177
  signed_metadata = XMLSecurity::SignedDocument.new(xml_text)
112
178
  assert signed_metadata.validate_document(ruby_saml_cert_fingerprint, false)
179
+
180
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
113
181
  end
114
182
 
115
183
  describe "when digest and signature methods are specified" do
@@ -126,6 +194,8 @@ class MetadataTest < Minitest::Test
126
194
  signed_metadata_2 = XMLSecurity::SignedDocument.new(xml_text)
127
195
 
128
196
  assert signed_metadata_2.validate_document(ruby_saml_cert_fingerprint, false)
197
+
198
+ assert validate_xml!(xml_text, "saml-schema-metadata-2.0.xsd")
129
199
  end
130
200
  end
131
201
  end