ruby-saml 0.9.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of ruby-saml might be problematic. Click here for more details.

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
@@ -6,125 +6,109 @@ class SloLogoutresponseTest < Minitest::Test
6
6
 
7
7
  describe "SloLogoutresponse" do
8
8
  let(:settings) { OneLogin::RubySaml::Settings.new }
9
+ let(:logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) }
9
10
 
10
- it "create the deflated SAMLResponse URL parameter" do
11
+ before do
12
+ settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/SOMEACCOUNT'
11
13
  settings.idp_slo_target_url = "http://unauth.com/logout"
12
14
  settings.name_identifier_value = "f00f00"
13
15
  settings.compress_request = true
16
+ settings.certificate = ruby_saml_cert_text
17
+ settings.private_key = ruby_saml_key_text
18
+ logout_request.settings = settings
19
+ end
14
20
 
15
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
16
-
17
- assert request.is_valid?
18
-
19
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id)
20
- assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLResponse=/
21
+ it "create the deflated SAMLResponse URL parameter" do
22
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
23
+ assert_match /^http:\/\/unauth\.com\/logout\?SAMLResponse=/, unauth_url
21
24
 
22
25
  inflated = decode_saml_response_payload(unauth_url)
23
-
24
26
  assert_match /^<samlp:LogoutResponse/, inflated
25
27
  end
26
28
 
27
29
  it "support additional params" do
28
- settings.idp_slo_target_url = "http://unauth.com/logout"
29
- settings.name_identifier_value = "f00f00"
30
- settings.compress_request = true
30
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :hello => nil })
31
+ assert_match /&hello=$/, unauth_url
31
32
 
32
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
33
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :foo => "bar" })
34
+ assert_match /&foo=bar$/, unauth_url
33
35
 
34
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :hello => nil })
35
- assert unauth_url =~ /&hello=$/
36
-
37
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :foo => "bar" })
38
- assert unauth_url =~ /&foo=bar$/
39
-
40
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, nil, { :RelayState => "http://idp.example.com" })
41
- assert unauth_url =~ /&RelayState=http%3A%2F%2Fidp.example.com$/
36
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :RelayState => "http://idp.example.com" })
37
+ assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url
42
38
  end
43
39
 
44
40
  it "set InResponseTo to the ID from the logout request" do
45
- settings.idp_slo_target_url = "http://unauth.com/logout"
46
- settings.name_identifier_value = "f00f00"
47
- settings.compress_request = true
48
-
49
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
50
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id)
41
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
51
42
 
52
43
  inflated = decode_saml_response_payload(unauth_url)
53
-
54
44
  assert_match /InResponseTo='_c0348950-935b-0131-1060-782bcb56fcaa'/, inflated
55
45
  end
56
46
 
57
47
  it "set a custom successful logout message on the response" do
58
- settings.idp_slo_target_url = "http://unauth.com/logout"
59
- settings.name_identifier_value = "f00f00"
60
- settings.compress_request = true
61
-
62
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
63
- unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, request.id, "Custom Logout Message")
48
+ unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, "Custom Logout Message")
64
49
 
65
50
  inflated = decode_saml_response_payload(unauth_url)
66
-
67
51
  assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated
68
52
  end
69
53
 
70
54
  describe "when the settings indicate to sign (embedded) logout response" do
71
- it "create a signed logout response" do
72
- settings = OneLogin::RubySaml::Settings.new
55
+
56
+ before do
73
57
  settings.compress_response = false
74
- settings.idp_slo_target_url = "http://example.com?field=value"
75
58
  settings.security[:logout_responses_signed] = true
76
59
  settings.security[:embed_sign] = true
77
- settings.certificate = ruby_saml_cert_text
78
- settings.private_key = ruby_saml_key_text
60
+ end
79
61
 
80
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
81
- params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
62
+ it "create a signed logout response" do
63
+ logout_request.settings = settings
64
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message")
82
65
 
83
66
  response_xml = Base64.decode64(params["SAMLResponse"])
84
67
  assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
85
- response_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/
86
- response_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1'\/>/
68
+ assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/, response_xml
69
+ assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1'\/>/, response_xml
87
70
  end
88
71
 
89
72
  it "create a signed logout response with 256 digest and signature methods" do
90
- settings = OneLogin::RubySaml::Settings.new
91
- settings.compress_response = false
92
- settings.idp_slo_target_url = "http://example.com?field=value"
93
- settings.security[:logout_responses_signed] = true
94
- settings.security[:embed_sign] = true
95
73
  settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
74
+ settings.security[:digest_method] = XMLSecurity::Document::SHA256
75
+
76
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message")
77
+
78
+ response_xml = Base64.decode64(params["SAMLResponse"])
79
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
80
+ assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/, response_xml
81
+ assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#sha256'\/>/, response_xml
82
+ end
83
+
84
+ it "create a signed logout response with 512 digest and signature method RSA_SHA384" do
85
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
96
86
  settings.security[:digest_method] = XMLSecurity::Document::SHA512
97
- settings.certificate = ruby_saml_cert_text
98
- settings.private_key = ruby_saml_key_text
87
+ logout_request.settings = settings
99
88
 
100
- request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
101
- params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, request.id, "Custom Logout Message")
89
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message")
102
90
 
103
91
  response_xml = Base64.decode64(params["SAMLResponse"])
104
92
  assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
105
- response_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/
106
- response_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha512'\/>/
93
+ assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha384'\/>/, response_xml
94
+ assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#sha512'\/>/, response_xml
107
95
  end
108
96
  end
109
97
 
110
98
  describe "#create_params when the settings indicate to sign the logout response" do
111
- def setup
112
- @settings = OneLogin::RubySaml::Settings.new
113
- @settings.compress_response = false
114
- @settings.idp_slo_target_url = "http://example.com?field=value"
115
- @settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
116
- @settings.security[:logout_responses_signed] = true
117
- @settings.security[:embed_sign] = false
118
- @settings.certificate = ruby_saml_cert_text
119
- @settings.private_key = ruby_saml_key_text
120
- @cert = OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
121
- @request = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document)
99
+
100
+ let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
101
+
102
+ before do
103
+ settings.compress_response = false
104
+ settings.security[:logout_responses_signed] = true
105
+ settings.security[:embed_sign] = false
122
106
  end
123
107
 
124
108
  it "create a signature parameter with RSA_SHA1 and validate it" do
125
109
  settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
126
110
 
127
- params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(@settings, @request.id, "Custom Logout Message", :RelayState => 'http://example.com')
111
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message", :RelayState => 'http://example.com')
128
112
  assert params['SAMLResponse']
129
113
  assert params[:RelayState]
130
114
  assert params['Signature']
@@ -136,13 +120,13 @@ class SloLogoutresponseTest < Minitest::Test
136
120
 
137
121
  signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
138
122
  assert_equal signature_algorithm, OpenSSL::Digest::SHA1
139
- assert @cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
123
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
140
124
  end
141
125
 
142
- it "create a signature parameter with RSA_SHA256 and validate it" do
143
- @settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
126
+ it "create a signature parameter with RSA_SHA256 /SHA256 and validate it" do
127
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
144
128
 
145
- params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(@settings, @request.id, "Custom Logout Message", :RelayState => 'http://example.com')
129
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message", :RelayState => 'http://example.com')
146
130
  assert params['SAMLResponse']
147
131
  assert params[:RelayState]
148
132
  assert params['Signature']
@@ -155,7 +139,45 @@ class SloLogoutresponseTest < Minitest::Test
155
139
 
156
140
  signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
157
141
  assert_equal signature_algorithm, OpenSSL::Digest::SHA256
158
- assert @cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
142
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
143
+ end
144
+
145
+ it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
146
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
147
+
148
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message", :RelayState => 'http://example.com')
149
+ assert params['SAMLResponse']
150
+ assert params[:RelayState]
151
+ assert params['Signature']
152
+
153
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
154
+
155
+ query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
156
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
157
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
158
+
159
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
160
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA384
161
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
162
+ end
163
+
164
+ it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
165
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
166
+
167
+ params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, logout_request.id, "Custom Logout Message", :RelayState => 'http://example.com')
168
+ assert params['SAMLResponse']
169
+ assert params[:RelayState]
170
+ assert params['Signature']
171
+
172
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
173
+
174
+ query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
175
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
176
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
177
+
178
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
179
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA512
180
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
159
181
  end
160
182
 
161
183
  end
@@ -1,3 +1,11 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ add_filter "test/"
5
+ add_filter "lib/onelogin/ruby-saml/logging.rb"
6
+ end
7
+
8
+ require 'stringio'
1
9
  require 'rubygems'
2
10
  require 'bundler'
3
11
  require 'minitest/autorun'
@@ -8,7 +16,10 @@ Bundler.require :default, :test
8
16
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
17
  $LOAD_PATH.unshift(File.dirname(__FILE__))
10
18
 
11
- ENV["ruby-saml/testing"] = "1"
19
+ require 'onelogin/ruby-saml/logging'
20
+
21
+ TEST_LOGGER = Logger.new(StringIO.new)
22
+ OneLogin::RubySaml::Logging.logger = TEST_LOGGER
12
23
 
13
24
  class Minitest::Test
14
25
  def fixture(document, base64 = true)
@@ -20,74 +31,141 @@ class Minitest::Test
20
31
  end
21
32
  end
22
33
 
23
- def response_document
24
- @response_document ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response1.xml.base64'))
25
- end
26
-
27
- def response_document_2
28
- @response_document2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response2.xml.base64'))
34
+ def read_response(response)
35
+ File.read(File.join(File.dirname(__FILE__), "responses", response))
29
36
  end
30
37
 
31
- def response_document_3
32
- @response_document3 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response3.xml.base64'))
38
+ def read_invalid_response(response)
39
+ File.read(File.join(File.dirname(__FILE__), "responses", "invalids", response))
33
40
  end
34
41
 
35
- def response_document_4
36
- @response_document4 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response4.xml.base64'))
42
+ def read_logout_request(request)
43
+ File.read(File.join(File.dirname(__FILE__), "logout_requests", request))
37
44
  end
38
45
 
39
- def response_document_5
40
- @response_document5 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response5.xml.base64'))
46
+ def read_certificate(certificate)
47
+ File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
41
48
  end
42
49
 
43
- def r1_response_document_6
44
- @response_document6 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'r1_response6.xml.base64'))
50
+ def response_document_valid_signed
51
+ @response_document_valid_signed ||= read_response("valid_response.xml.base64")
45
52
  end
46
53
 
47
- def ampersands_response
48
- @ampersands_response ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_with_ampersands.xml.base64'))
54
+ def response_document_without_recipient
55
+ @response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
49
56
  end
50
57
 
51
- def response_document_6
52
- doc = Base64.decode64(response_document)
58
+ def response_document_without_recipient_with_time_updated
59
+ doc = Base64.decode64(response_document_without_recipient)
53
60
  doc.gsub!(/NotBefore=\"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\"/, "NotBefore=\"#{(Time.now-300).getutc.strftime("%Y-%m-%dT%XZ")}\"")
54
61
  doc.gsub!(/NotOnOrAfter=\"(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})Z\"/, "NotOnOrAfter=\"#{(Time.now+300).getutc.strftime("%Y-%m-%dT%XZ")}\"")
55
62
  Base64.encode64(doc)
56
63
  end
57
64
 
58
- def response_document_7
59
- @response_document7 ||= Base64.encode64(File.read(File.join(File.dirname(__FILE__), 'responses', 'response_no_cert_and_encrypted_attrs.xml')))
65
+ def response_document_without_attributes
66
+ @response_document_without_attributes ||= read_response("response_without_attributes.xml.base64")
67
+ end
68
+
69
+ def response_document_with_signed_assertion
70
+ @response_document_with_signed_assertion ||= read_response("response_with_signed_assertion.xml.base64")
71
+ end
72
+
73
+ def response_document_with_signed_assertion_2
74
+ @response_document_with_signed_assertion_2 ||= read_response("response_with_signed_assertion_2.xml.base64")
75
+ end
76
+
77
+ def response_document_unsigned
78
+ @response_document_unsigned ||= read_response("response_unsigned_xml_base64")
79
+ end
80
+
81
+ def response_document_with_saml2_namespace
82
+ @response_document_with_saml2_namespace ||= read_response("response_with_saml2_namespace.xml.base64")
83
+ end
84
+
85
+ def ampersands_document
86
+ @ampersands_response ||= read_response("response_with_ampersands.xml.base64")
87
+ end
88
+
89
+ def response_document_no_cert_and_encrypted_attrs
90
+ @response_document_no_cert_and_encrypted_attrs ||= Base64.encode64(read_response("response_no_cert_and_encrypted_attrs.xml"))
91
+ end
92
+
93
+ def response_document_wrapped
94
+ @response_document_wrapped ||= read_response("response_wrapped.xml.base64")
95
+ end
96
+
97
+ def response_document_assertion_wrapped
98
+ @response_document_assertion_wrapped ||= read_response("response_assertion_wrapped.xml.base64")
99
+ end
100
+
101
+ def response_document_encrypted_nameid
102
+ @response_document_encrypted_nameid ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_encrypted_nameid.xml.base64'))
103
+ end
104
+
105
+ def signed_message_encrypted_unsigned_assertion
106
+ @signed_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_unsigned_assertion.xml.base64'))
107
+ end
108
+
109
+ def signed_message_encrypted_signed_assertion
110
+ @signed_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_signed_assertion.xml.base64'))
111
+ end
112
+
113
+ def unsigned_message_encrypted_signed_assertion
114
+ @unsigned_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_signed_assertion.xml.base64'))
60
115
  end
61
116
 
62
- def wrapped_response_2
63
- @wrapped_response_2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'wrapped_response_2.xml.base64'))
117
+ def unsigned_message_encrypted_unsigned_assertion
118
+ @unsigned_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_unsigned_assertion.xml.base64'))
64
119
  end
65
120
 
66
121
  def signature_fingerprint_1
67
122
  @signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
68
123
  end
69
124
 
70
- def signature_1
71
- @signature1 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'certificate1'))
125
+ # certificate used on response_with_undefined_recipient
126
+ def signature_1
127
+ @signature1 ||= read_certificate("certificate1")
72
128
  end
73
129
 
74
- def r1_signature_2
75
- @signature2 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'r1_certificate2_base64'))
130
+ # certificate used on response_document_with_signed_assertion_2
131
+ def certificate_without_head_foot
132
+ @certificate_without_head_foot ||= read_certificate("certificate_without_head_foot")
76
133
  end
77
134
 
78
135
  def idp_metadata
79
- @idp_metadata ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'idp_descriptor.xml'))
136
+ @idp_metadata ||= read_response("idp_descriptor.xml")
80
137
  end
81
138
 
82
139
  def logout_request_document
83
140
  unless @logout_request_document
84
- xml = File.read(File.join(File.dirname(__FILE__), 'responses', 'slo_request.xml'))
141
+ xml = read_logout_request("slo_request.xml")
85
142
  deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
86
143
  @logout_request_document = Base64.encode64(deflated)
87
144
  end
88
145
  @logout_request_document
89
146
  end
90
147
 
148
+ def logout_request_xml_with_session_index
149
+ @logout_request_xml_with_session_index ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_with_session_index.xml'))
150
+ end
151
+
152
+ def invalid_logout_request_document
153
+ unless @invalid_logout_request_document
154
+ xml = File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'invalid_slo_request.xml'))
155
+ deflated = Zlib::Deflate.deflate(xml, 9)[2..-5]
156
+ @invalid_logout_request_document = Base64.encode64(deflated)
157
+ end
158
+ @invalid_logout_request_document
159
+ end
160
+
161
+ def logout_request_base64
162
+ @logout_request_base64 ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request.xml.base64'))
163
+ end
164
+
165
+ def logout_request_deflated_base64
166
+ @logout_request_deflated_base64 ||= File.read(File.join(File.dirname(__FILE__), 'logout_requests', 'slo_request_deflated.xml.base64'))
167
+ end
168
+
91
169
  def ruby_saml_cert
92
170
  @ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
93
171
  end
@@ -97,7 +175,7 @@ class Minitest::Test
97
175
  end
98
176
 
99
177
  def ruby_saml_cert_text
100
- File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.crt'))
178
+ read_certificate("ruby-saml.crt")
101
179
  end
102
180
 
103
181
  def ruby_saml_key
@@ -105,7 +183,7 @@ class Minitest::Test
105
183
  end
106
184
 
107
185
  def ruby_saml_key_text
108
- File.read(File.join(File.dirname(__FILE__), 'certificates', 'ruby-saml.key'))
186
+ read_certificate("ruby-saml.key")
109
187
  end
110
188
 
111
189
  #
@@ -142,4 +220,33 @@ class Minitest::Test
142
220
  zstream.close
143
221
  inflated
144
222
  end
223
+
224
+ SCHEMA_DIR = File.expand_path(File.join(__FILE__, '../../lib/schemas'))
225
+
226
+ #
227
+ # validate an xml document against the given schema
228
+ #
229
+ def validate_xml!(document, schema)
230
+ Dir.chdir(SCHEMA_DIR) do
231
+ xsd = if schema.is_a? Nokogiri::XML::Schema
232
+ schema
233
+ else
234
+ Nokogiri::XML::Schema(File.read(schema))
235
+ end
236
+
237
+ xml = if document.is_a? Nokogiri::XML::Document
238
+ document
239
+ else
240
+ Nokogiri::XML(document) { |c| c.strict }
241
+ end
242
+
243
+ result = xsd.validate(xml)
244
+
245
+ if result.length != 0
246
+ raise "Schema validation failed! XSD validation errors: #{result.join(", ")}"
247
+ else
248
+ true
249
+ end
250
+ end
251
+ end
145
252
  end