ruby-saml 0.8.18 → 0.9

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +1 -6
  4. data/Gemfile +2 -12
  5. data/README.md +363 -35
  6. data/Rakefile +14 -0
  7. data/changelog.md +22 -9
  8. data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
  9. data/lib/onelogin/ruby-saml/attributes.rb +26 -64
  10. data/lib/onelogin/ruby-saml/authrequest.rb +47 -93
  11. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
  12. data/lib/onelogin/ruby-saml/logoutrequest.rb +36 -100
  13. data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -35
  14. data/lib/onelogin/ruby-saml/metadata.rb +46 -16
  15. data/lib/onelogin/ruby-saml/response.rb +63 -373
  16. data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
  17. data/lib/onelogin/ruby-saml/settings.rb +54 -122
  18. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +25 -71
  19. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +37 -102
  20. data/lib/onelogin/ruby-saml/utils.rb +32 -199
  21. data/lib/onelogin/ruby-saml/version.rb +1 -1
  22. data/lib/ruby-saml.rb +5 -2
  23. data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
  24. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  25. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  26. data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
  27. data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
  28. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  29. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  30. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  31. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  32. data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
  33. data/lib/schemas/xml.xsd +287 -0
  34. data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
  35. data/lib/xml_security.rb +83 -235
  36. data/ruby-saml.gemspec +1 -0
  37. data/test/idp_metadata_parser_test.rb +54 -0
  38. data/test/logoutrequest_test.rb +68 -155
  39. data/test/logoutresponse_test.rb +43 -32
  40. data/test/metadata_test.rb +87 -0
  41. data/test/request_test.rb +102 -99
  42. data/test/response_test.rb +181 -495
  43. data/test/responses/idp_descriptor.xml +3 -0
  44. data/test/responses/logoutresponse_fixtures.rb +7 -8
  45. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  46. data/test/responses/response_with_multiple_attribute_values.xml +1 -1
  47. data/test/responses/slo_request.xml +4 -0
  48. data/test/settings_test.rb +25 -112
  49. data/test/slo_logoutrequest_test.rb +40 -50
  50. data/test/slo_logoutresponse_test.rb +86 -185
  51. data/test/test_helper.rb +27 -102
  52. data/test/xml_security_test.rb +114 -337
  53. metadata +30 -81
  54. data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
  55. data/test/certificates/certificate.der +0 -0
  56. data/test/certificates/formatted_certificate +0 -14
  57. data/test/certificates/formatted_chained_certificate +0 -42
  58. data/test/certificates/formatted_private_key +0 -12
  59. data/test/certificates/formatted_rsa_private_key +0 -12
  60. data/test/certificates/invalid_certificate1 +0 -1
  61. data/test/certificates/invalid_certificate2 +0 -1
  62. data/test/certificates/invalid_certificate3 +0 -12
  63. data/test/certificates/invalid_chained_certificate1 +0 -1
  64. data/test/certificates/invalid_private_key1 +0 -1
  65. data/test/certificates/invalid_private_key2 +0 -1
  66. data/test/certificates/invalid_private_key3 +0 -10
  67. data/test/certificates/invalid_rsa_private_key1 +0 -1
  68. data/test/certificates/invalid_rsa_private_key2 +0 -1
  69. data/test/certificates/invalid_rsa_private_key3 +0 -10
  70. data/test/certificates/ruby-saml-2.crt +0 -15
  71. data/test/requests/logoutrequest_fixtures.rb +0 -47
  72. data/test/responses/encrypted_new_attack.xml.base64 +0 -1
  73. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
  74. data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
  75. data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
  76. data/test/responses/invalids/no_signature.xml.base64 +0 -1
  77. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
  78. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
  79. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
  80. data/test/responses/response_node_text_attack.xml.base64 +0 -1
  81. data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
  82. data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
  83. data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
  84. data/test/responses/response_with_signed_assertion_3.xml +0 -30
  85. data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
  86. data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
  87. data/test/responses/response_wrapped.xml.base64 +0 -150
  88. data/test/responses/valid_response.xml.base64 +0 -1
  89. data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
  90. data/test/utils_test.rb +0 -231
@@ -1,16 +1,14 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
2
 
3
- class LogoutRequestTest < Minitest::Test
3
+ class RequestTest < Test::Unit::TestCase
4
4
 
5
- describe "Logoutrequest" do
6
- let(:settings) { OneLogin::RubySaml::Settings.new }
5
+ context "Logoutrequest" do
6
+ settings = OneLogin::RubySaml::Settings.new
7
7
 
8
- before do
8
+ should "create the deflated SAMLRequest URL parameter" do
9
9
  settings.idp_slo_target_url = "http://unauth.com/logout"
10
10
  settings.name_identifier_value = "f00f00"
11
- end
12
11
 
13
- it "create the deflated SAMLRequest URL parameter" do
14
12
  unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
15
13
  assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLRequest=/
16
14
 
@@ -19,7 +17,8 @@ class LogoutRequestTest < Minitest::Test
19
17
  assert_match /^<samlp:LogoutRequest/, inflated
20
18
  end
21
19
 
22
- it "support additional params" do
20
+ should "support additional params" do
21
+
23
22
  unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
24
23
  assert unauth_url =~ /&hello=$/
25
24
 
@@ -27,8 +26,9 @@ class LogoutRequestTest < Minitest::Test
27
26
  assert unauth_url =~ /&foo=bar$/
28
27
  end
29
28
 
30
- it "set sessionindex" do
31
- sessionidx = random_id
29
+ should "set sessionindex" do
30
+ settings.idp_slo_target_url = "http://example.com"
31
+ sessionidx = UUID.new.generate
32
32
  settings.sessionindex = sessionidx
33
33
 
34
34
  unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :name_id => "there" })
@@ -38,7 +38,9 @@ class LogoutRequestTest < Minitest::Test
38
38
  assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
39
39
  end
40
40
 
41
- it "set name_identifier_value" do
41
+ should "set name_identifier_value" do
42
+ settings = OneLogin::RubySaml::Settings.new
43
+ settings.idp_slo_target_url = "http://example.com"
42
44
  settings.name_identifier_format = "transient"
43
45
  name_identifier_value = "abc123"
44
46
  settings.name_identifier_value = name_identifier_value
@@ -50,25 +52,33 @@ class LogoutRequestTest < Minitest::Test
50
52
  assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
51
53
  end
52
54
 
53
- describe "when the target url doesn't contain a query string" do
54
- it "create the SAMLRequest parameter correctly" do
55
+ context "when the target url doesn't contain a query string" do
56
+ should "create the SAMLRequest parameter correctly" do
57
+ settings = OneLogin::RubySaml::Settings.new
55
58
  settings.idp_slo_target_url = "http://example.com"
59
+ settings.name_identifier_value = "f00f00"
60
+
56
61
  unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
57
62
  assert unauth_url =~ /^http:\/\/example.com\?SAMLRequest/
58
63
  end
59
64
  end
60
65
 
61
- describe "when the target url contains a query string" do
62
- it "create the SAMLRequest parameter correctly" do
66
+ context "when the target url contains a query string" do
67
+ should "create the SAMLRequest parameter correctly" do
68
+ settings = OneLogin::RubySaml::Settings.new
63
69
  settings.idp_slo_target_url = "http://example.com?field=value"
70
+ settings.name_identifier_value = "f00f00"
71
+
64
72
  unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
65
73
  assert unauth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
66
74
  end
67
75
  end
68
76
 
69
- describe "consumation of logout may need to track the transaction" do
70
- it "have access to the request uuid" do
77
+ context "consumation of logout may need to track the transaction" do
78
+ should "have access to the request uuid" do
79
+ settings = OneLogin::RubySaml::Settings.new
71
80
  settings.idp_slo_target_url = "http://example.com?field=value"
81
+ settings.name_identifier_value = "f00f00"
72
82
 
73
83
  unauth_req = OneLogin::RubySaml::Logoutrequest.new
74
84
  unauth_url = unauth_req.create(settings)
@@ -78,178 +88,81 @@ class LogoutRequestTest < Minitest::Test
78
88
  end
79
89
  end
80
90
 
81
-
82
- describe "when the settings indicate to sign (embedded) logout request" do
83
-
84
- before do
91
+ context "when the settings indicate to sign (embebed) the logout request" do
92
+ should "created a signed logout request" do
93
+ settings = OneLogin::RubySaml::Settings.new
94
+ settings.idp_slo_target_url = "http://example.com?field=value"
95
+ settings.name_identifier_value = "f00f00"
85
96
  # sign the logout request
86
97
  settings.security[:logout_requests_signed] = true
87
98
  settings.security[:embed_sign] = true
88
99
  settings.certificate = ruby_saml_cert_text
89
100
  settings.private_key = ruby_saml_key_text
90
- end
91
-
92
- it "doesn't sign through create_xml_document" do
93
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
94
- inflated = unauth_req.create_xml_document(settings).to_s
95
-
96
- refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
97
- refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
98
- refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
99
- end
100
-
101
- it "sign unsigned request" do
102
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
103
- unauth_req_doc = unauth_req.create_xml_document(settings)
104
- inflated = unauth_req_doc.to_s
105
-
106
- refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
107
- refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
108
- refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
109
-
110
- inflated = unauth_req.sign_document(unauth_req_doc, settings).to_s
111
-
112
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
113
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
114
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
115
- end
116
-
117
- it "signs through create_logout_request_xml_doc" do
118
- unauth_req = OneLogin::RubySaml::Logoutrequest.new
119
- inflated = unauth_req.create_logout_request_xml_doc(settings).to_s
120
-
121
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
122
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
123
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
124
- end
125
-
126
- it "created a signed logout request" do
127
- settings.compress_request = true
128
101
 
129
102
  unauth_req = OneLogin::RubySaml::Logoutrequest.new
130
103
  unauth_url = unauth_req.create(settings)
131
104
 
132
105
  inflated = decode_saml_request_payload(unauth_url)
133
106
  assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
134
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
135
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
136
107
  end
137
108
 
138
- it "create a signed logout request with 256 digest and signature method" do
109
+ should "create a signed logout request with 256 digest and signature methods" do
110
+ settings = OneLogin::RubySaml::Settings.new
139
111
  settings.compress_request = false
140
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
141
- settings.security[:digest_method] = XMLSecurity::Document::SHA256
142
-
143
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
144
- request_xml = Base64.decode64(params["SAMLRequest"])
145
-
146
- assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
147
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
148
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha256'/>], request_xml
149
- end
150
-
151
- it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
152
- settings.compress_request = false
153
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
112
+ settings.idp_slo_target_url = "http://example.com?field=value"
113
+ settings.name_identifier_value = "f00f00"
114
+ # sign the logout request
115
+ settings.security[:logout_requests_signed] = true
116
+ settings.security[:embed_sign] = true
117
+ settings.security[:signature_method] = XMLSecurity::Document::SHA256
154
118
  settings.security[:digest_method] = XMLSecurity::Document::SHA512
119
+ settings.certificate = ruby_saml_cert_text
120
+ settings.private_key = ruby_saml_key_text
155
121
 
156
122
  params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
157
123
  request_xml = Base64.decode64(params["SAMLRequest"])
158
124
 
159
125
  assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
160
- assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'/>], request_xml
161
- assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha512'/>], request_xml
126
+ request_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/
127
+ request_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha512'\/>/
162
128
  end
163
129
  end
164
130
 
165
- describe "#create_params when the settings indicate to sign the logout request" do
166
-
167
- let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
168
-
169
- before do
170
- # sign the logout request
131
+ context "when the settings indicate to sign the logout request" do
132
+ should "create a signature parameter" do
133
+ settings = OneLogin::RubySaml::Settings.new
134
+ settings.compress_request = false
135
+ settings.idp_slo_target_url = "http://example.com?field=value"
136
+ settings.name_identifier_value = "f00f00"
171
137
  settings.security[:logout_requests_signed] = true
172
138
  settings.security[:embed_sign] = false
173
- settings.certificate = ruby_saml_cert_text
139
+ settings.security[:signature_method] = XMLSecurity::Document::SHA1
140
+ settings.certificate = ruby_saml_cert_text
174
141
  settings.private_key = ruby_saml_key_text
175
- end
176
-
177
- it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
178
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
179
-
180
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
181
- assert params['SAMLRequest']
182
- assert params[:RelayState]
183
- assert params['Signature']
184
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
185
-
186
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
187
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
188
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
189
142
 
190
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
191
- assert_equal signature_algorithm, OpenSSL::Digest::SHA1
192
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
193
- end
194
-
195
- it "create a signature parameter with RSA_SHA256 / SHA256 and validate it" do
196
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
197
-
198
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
143
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
199
144
  assert params['Signature']
200
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
145
+ assert params['SigAlg'] == XMLSecurity::Document::SHA1
201
146
 
202
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
203
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
204
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
205
-
206
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
207
- assert_equal signature_algorithm, OpenSSL::Digest::SHA256
208
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
209
- end
210
-
211
- it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
212
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
213
-
214
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
147
+ # signature_method only affects the embedeed signature
148
+ settings.security[:signature_method] = XMLSecurity::Document::SHA256
149
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
215
150
  assert params['Signature']
216
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
217
-
218
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
219
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
220
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
221
-
222
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
223
- assert_equal signature_algorithm, OpenSSL::Digest::SHA384
224
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
151
+ assert params['SigAlg'] == XMLSecurity::Document::SHA1
225
152
  end
153
+ end
226
154
 
227
- it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
228
- settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
229
-
230
- params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
231
- assert params['Signature']
232
- assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
233
-
234
- query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
235
- query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
236
- query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
155
+ end
237
156
 
238
- signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
239
- assert_equal signature_algorithm, OpenSSL::Digest::SHA512
240
- assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
241
- end
242
- end
157
+ def decode_saml_request_payload(unauth_url)
158
+ payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
159
+ decoded = Base64.decode64(payload)
243
160
 
244
- describe "#manipulate request_id" do
245
- it "be able to modify the request id" do
246
- logoutrequest = OneLogin::RubySaml::Logoutrequest.new
247
- request_id = logoutrequest.request_id
248
- assert_equal request_id, logoutrequest.uuid
249
- logoutrequest.uuid = "new_uuid"
250
- assert_equal logoutrequest.request_id, logoutrequest.uuid
251
- assert_equal "new_uuid", logoutrequest.request_id
252
- end
253
- end
161
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
162
+ inflated = zstream.inflate(decoded)
163
+ zstream.finish
164
+ zstream.close
165
+ inflated
254
166
  end
167
+
255
168
  end
@@ -1,27 +1,26 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
- require File.expand_path(File.join(File.dirname(__FILE__), "responses/logoutresponse_fixtures"))
2
+ require 'rexml/document'
3
+ require 'responses/logoutresponse_fixtures'
4
+ class RubySamlTest < Test::Unit::TestCase
3
5
 
4
- class LogoutResponseTest < Minitest::Test
5
-
6
- describe "Logoutresponse" do
7
-
8
- describe "#new" do
9
- it "raise an exception when response is initialized with nil" do
6
+ context "Logoutresponse" do
7
+ context "#new" do
8
+ should "raise an exception when response is initialized with nil" do
10
9
  assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
11
10
  end
12
- it "default to empty settings" do
11
+ should "default to empty settings" do
13
12
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response)
14
- assert logoutresponse.settings.nil?
13
+ assert_nil logoutresponse.settings
15
14
  end
16
- it "accept constructor-injected settings" do
15
+ should "accept constructor-injected settings" do
17
16
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
18
- assert !logoutresponse.settings.nil?
17
+ assert_not_nil logoutresponse.settings
19
18
  end
20
- it "accept constructor-injected options" do
19
+ should "accept constructor-injected options" do
21
20
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
22
21
  assert !logoutresponse.options.empty?
23
22
  end
24
- it "support base64 encoded responses" do
23
+ should "support base64 encoded responses" do
25
24
  expected_response = valid_response
26
25
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings)
27
26
 
@@ -29,21 +28,21 @@ class LogoutResponseTest < Minitest::Test
29
28
  end
30
29
  end
31
30
 
32
- describe "#validate" do
33
- it "validate the response" do
31
+ context "#validate" do
32
+ should "validate the response" do
34
33
  in_relation_to_request_id = random_id
35
- settings.idp_entity_id = "https://example.com/idp"
36
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid2 => in_relation_to_request_id}), settings)
34
+
35
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
37
36
 
38
37
  assert logoutresponse.validate
39
38
 
40
- assert_equal settings.idp_entity_id, logoutresponse.issuer
39
+ assert_equal settings.issuer, logoutresponse.issuer
41
40
  assert_equal in_relation_to_request_id, logoutresponse.in_response_to
42
41
 
43
42
  assert logoutresponse.success?
44
43
  end
45
44
 
46
- it "invalidate responses with wrong id when given option :matches_uuid" do
45
+ should "invalidate responses with wrong id when given option :matches_uuid" do
47
46
 
48
47
  expected_request_id = "_some_other_expected_uuid"
49
48
  opts = { :matches_request_id => expected_request_id}
@@ -51,10 +50,10 @@ class LogoutResponseTest < Minitest::Test
51
50
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
52
51
 
53
52
  assert !logoutresponse.validate
54
- assert expected_request_id != logoutresponse.in_response_to
53
+ assert_not_equal expected_request_id, logoutresponse.in_response_to
55
54
  end
56
55
 
57
- it "invalidate responses with wrong request status" do
56
+ should "invalidate responses with wrong request status" do
58
57
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
59
58
 
60
59
  assert !logoutresponse.validate
@@ -62,8 +61,8 @@ class LogoutResponseTest < Minitest::Test
62
61
  end
63
62
  end
64
63
 
65
- describe "#validate!" do
66
- it "validates good responses" do
64
+ context "#validate!" do
65
+ should "validates good responses" do
67
66
  in_relation_to_request_id = random_id
68
67
 
69
68
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
@@ -71,7 +70,7 @@ class LogoutResponseTest < Minitest::Test
71
70
  logoutresponse.validate!
72
71
  end
73
72
 
74
- it "raises validation error when matching for wrong request id" do
73
+ should "raises validation error when matching for wrong request id" do
75
74
 
76
75
  expected_request_id = "_some_other_expected_id"
77
76
  opts = { :matches_request_id => expected_request_id}
@@ -81,25 +80,37 @@ class LogoutResponseTest < Minitest::Test
81
80
  assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
82
81
  end
83
82
 
84
- it "raise validation error for wrong request status" do
83
+ should "raise validation error for wrong request status" do
85
84
  logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
86
85
 
87
86
  assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
88
87
  end
89
88
 
90
- it "raise error for invalid xml" do
91
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
89
+ should "raise validation error when in bad state" do
90
+ # no settings
91
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response)
92
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
93
+ end
92
94
 
95
+ should "raise validation error when in lack of issuer setting" do
96
+ bad_settings = settings
97
+ bad_settings.issuer = nil
98
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings)
93
99
  assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
94
100
  end
95
- end
96
101
 
97
- describe "#response_id" do
98
- it "extract the value of the Response ID" do
99
- logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
100
- assert_equal "_28024690-000e-0130-b6d2-38f6b112be8b", logoutresponse.response_id
102
+ should "raise error for invalid xml" do
103
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
104
+
105
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
101
106
  end
102
107
  end
103
108
 
104
109
  end
110
+
111
+ # logoutresponse fixtures
112
+ def random_id
113
+ "_#{UUID.new.generate}"
114
+ end
115
+
105
116
  end
@@ -0,0 +1,87 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ class MetadataTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @settings = OneLogin::RubySaml::Settings.new
7
+ @settings.issuer = "https://example.com"
8
+ @settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
9
+ @settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
10
+ @settings.security[:authn_requests_signed] = false
11
+ end
12
+
13
+ should "generate Service Provider Metadata with X509Certificate" do
14
+ @settings.security[:authn_requests_signed] = true
15
+ @settings.certificate = ruby_saml_cert_text
16
+
17
+ xml_text = OneLogin::RubySaml::Metadata.new.generate(@settings)
18
+
19
+ # assert xml_text can be parsed into an xml doc
20
+ xml_doc = REXML::Document.new(xml_text)
21
+
22
+ spsso_descriptor = REXML::XPath.first(xml_doc, "//md:SPSSODescriptor")
23
+ assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value
24
+
25
+ cert_node = REXML::XPath.first(xml_doc, "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate", {
26
+ "md" => "urn:oasis:names:tc:SAML:2.0:metadata",
27
+ "ds" => "http://www.w3.org/2000/09/xmldsig#"
28
+ })
29
+ cert_text = cert_node.text
30
+ cert = OpenSSL::X509::Certificate.new(Base64.decode64(cert_text))
31
+ assert_equal ruby_saml_cert.to_der, cert.to_der
32
+ end
33
+
34
+ should "should generate Service Provider Metadata" do
35
+ settings = OneLogin::RubySaml::Settings.new
36
+ settings.issuer = "https://example.com"
37
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
38
+ settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
39
+ settings.security[:authn_requests_signed] = false
40
+
41
+ xml_text = OneLogin::RubySaml::Metadata.new.generate(settings)
42
+
43
+ # assert correct xml declaration
44
+ start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
45
+ assert xml_text[0..start.length-1] == start
46
+
47
+ # assert xml_text can be parsed into an xml doc
48
+ xml_doc = REXML::Document.new(xml_text)
49
+
50
+ assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
51
+
52
+ spsso_descriptor = REXML::XPath.first(xml_doc, "//md:SPSSODescriptor")
53
+ assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
54
+ assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
55
+ assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
56
+
57
+ assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
58
+
59
+ acs = REXML::XPath.first(xml_doc, "//md:AssertionConsumerService")
60
+ assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
61
+ assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
62
+ end
63
+
64
+ should "generate attribute service if configured" do
65
+ settings = OneLogin::RubySaml::Settings.new
66
+ settings.issuer = "https://example.com"
67
+ settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
68
+ settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
69
+ settings.attribute_consuming_service.configure do
70
+ service_name "Test Service"
71
+ add_attribute(:name => "Name", :name_format => "Name Format", :friendly_name => "Friendly Name", :attribute_value => "Attribute Value")
72
+ end
73
+
74
+ xml_text = OneLogin::RubySaml::Metadata.new.generate(settings)
75
+ xml_doc = REXML::Document.new(xml_text)
76
+ acs = REXML::XPath.first(xml_doc, "//md:AttributeConsumingService")
77
+ assert_equal "true", acs.attribute("isDefault").value
78
+ assert_equal "1", acs.attribute("index").value
79
+ assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test Service"
80
+ req_attr = REXML::XPath.first(xml_doc, "//md:RequestedAttribute")
81
+ assert_equal "Name", req_attr.attribute("Name").value
82
+ assert_equal "Name Format", req_attr.attribute("NameFormat").value
83
+ assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value
84
+ assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//md:AttributeValue").text.strip
85
+ end
86
+
87
+ end