kl-ruby-saml 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +14 -0
  4. data/.travis.yml +17 -0
  5. data/Gemfile +9 -0
  6. data/LICENSE +19 -0
  7. data/README.md +575 -0
  8. data/Rakefile +41 -0
  9. data/changelog.md +75 -0
  10. data/gemfiles/nokogiri-1.5.gemfile +5 -0
  11. data/lib/onelogin/ruby-saml.rb +17 -0
  12. data/lib/onelogin/ruby-saml/attribute_service.rb +57 -0
  13. data/lib/onelogin/ruby-saml/attributes.rb +128 -0
  14. data/lib/onelogin/ruby-saml/authrequest.rb +156 -0
  15. data/lib/onelogin/ruby-saml/http_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +161 -0
  17. data/lib/onelogin/ruby-saml/logging.rb +30 -0
  18. data/lib/onelogin/ruby-saml/logoutrequest.rb +131 -0
  19. data/lib/onelogin/ruby-saml/logoutresponse.rb +241 -0
  20. data/lib/onelogin/ruby-saml/metadata.rb +123 -0
  21. data/lib/onelogin/ruby-saml/response.rb +722 -0
  22. data/lib/onelogin/ruby-saml/saml_message.rb +158 -0
  23. data/lib/onelogin/ruby-saml/settings.rb +165 -0
  24. data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +258 -0
  25. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +136 -0
  26. data/lib/onelogin/ruby-saml/utils.rb +172 -0
  27. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  28. data/lib/onelogin/ruby-saml/version.rb +5 -0
  29. data/lib/ruby-saml.rb +1 -0
  30. data/lib/schemas/saml-schema-assertion-2.0.xsd +283 -0
  31. data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
  32. data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
  33. data/lib/schemas/saml-schema-metadata-2.0.xsd +337 -0
  34. data/lib/schemas/saml-schema-protocol-2.0.xsd +302 -0
  35. data/lib/schemas/sstc-metadata-attr.xsd +35 -0
  36. data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
  37. data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
  38. data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
  39. data/lib/schemas/xenc-schema.xsd +136 -0
  40. data/lib/schemas/xml.xsd +287 -0
  41. data/lib/schemas/xmldsig-core-schema.xsd +309 -0
  42. data/lib/xml_security.rb +358 -0
  43. data/ruby-saml.gemspec +57 -0
  44. data/test/certificates/certificate1 +12 -0
  45. data/test/certificates/certificate_without_head_foot +1 -0
  46. data/test/certificates/formatted_certificate +14 -0
  47. data/test/certificates/formatted_private_key +12 -0
  48. data/test/certificates/formatted_rsa_private_key +12 -0
  49. data/test/certificates/invalid_certificate1 +1 -0
  50. data/test/certificates/invalid_certificate2 +1 -0
  51. data/test/certificates/invalid_certificate3 +12 -0
  52. data/test/certificates/invalid_private_key1 +1 -0
  53. data/test/certificates/invalid_private_key2 +1 -0
  54. data/test/certificates/invalid_private_key3 +10 -0
  55. data/test/certificates/invalid_rsa_private_key1 +1 -0
  56. data/test/certificates/invalid_rsa_private_key2 +1 -0
  57. data/test/certificates/invalid_rsa_private_key3 +10 -0
  58. data/test/certificates/ruby-saml.crt +14 -0
  59. data/test/certificates/ruby-saml.key +15 -0
  60. data/test/idp_metadata_parser_test.rb +95 -0
  61. data/test/logging_test.rb +62 -0
  62. data/test/logout_requests/invalid_slo_request.xml +6 -0
  63. data/test/logout_requests/slo_request.xml +4 -0
  64. data/test/logout_requests/slo_request.xml.base64 +1 -0
  65. data/test/logout_requests/slo_request_deflated.xml.base64 +1 -0
  66. data/test/logout_requests/slo_request_with_session_index.xml +5 -0
  67. data/test/logout_responses/logoutresponse_fixtures.rb +67 -0
  68. data/test/logoutrequest_test.rb +211 -0
  69. data/test/logoutresponse_test.rb +258 -0
  70. data/test/metadata_test.rb +203 -0
  71. data/test/request_test.rb +282 -0
  72. data/test/response_test.rb +1094 -0
  73. data/test/responses/adfs_response_sha1.xml +46 -0
  74. data/test/responses/adfs_response_sha256.xml +46 -0
  75. data/test/responses/adfs_response_sha384.xml +46 -0
  76. data/test/responses/adfs_response_sha512.xml +46 -0
  77. data/test/responses/adfs_response_xmlns.xml +45 -0
  78. data/test/responses/attackxee.xml +13 -0
  79. data/test/responses/idp_descriptor.xml +3 -0
  80. data/test/responses/invalids/invalid_audience.xml.base64 +1 -0
  81. data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +1 -0
  82. data/test/responses/invalids/invalid_issuer_message.xml.base64 +1 -0
  83. data/test/responses/invalids/invalid_signature_position.xml.base64 +1 -0
  84. data/test/responses/invalids/invalid_subjectconfirmation_inresponse.xml.base64 +1 -0
  85. data/test/responses/invalids/invalid_subjectconfirmation_nb.xml.base64 +1 -0
  86. data/test/responses/invalids/invalid_subjectconfirmation_noa.xml.base64 +1 -0
  87. data/test/responses/invalids/invalid_subjectconfirmation_recipient.xml.base64 +1 -0
  88. data/test/responses/invalids/multiple_assertions.xml.base64 +2 -0
  89. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  90. data/test/responses/invalids/no_id.xml.base64 +1 -0
  91. data/test/responses/invalids/no_saml2.xml.base64 +1 -0
  92. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  93. data/test/responses/invalids/no_status.xml.base64 +1 -0
  94. data/test/responses/invalids/no_status_code.xml.base64 +1 -0
  95. data/test/responses/invalids/no_subjectconfirmation_data.xml.base64 +1 -0
  96. data/test/responses/invalids/no_subjectconfirmation_method.xml.base64 +1 -0
  97. data/test/responses/invalids/response_encrypted_attrs.xml.base64 +1 -0
  98. data/test/responses/invalids/response_invalid_signed_element.xml.base64 +1 -0
  99. data/test/responses/invalids/status_code_responder.xml.base64 +1 -0
  100. data/test/responses/invalids/status_code_responer_and_msg.xml.base64 +1 -0
  101. data/test/responses/no_signature_ns.xml +48 -0
  102. data/test/responses/open_saml_response.xml +56 -0
  103. data/test/responses/response_assertion_wrapped.xml.base64 +93 -0
  104. data/test/responses/response_encrypted_nameid.xml.base64 +1 -0
  105. data/test/responses/response_eval.xml +7 -0
  106. data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
  107. data/test/responses/response_unsigned_xml_base64 +1 -0
  108. data/test/responses/response_with_ampersands.xml +139 -0
  109. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  110. data/test/responses/response_with_multiple_attribute_values.xml +67 -0
  111. data/test/responses/response_with_saml2_namespace.xml.base64 +102 -0
  112. data/test/responses/response_with_signed_assertion.xml.base64 +66 -0
  113. data/test/responses/response_with_signed_assertion_2.xml.base64 +1 -0
  114. data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
  115. data/test/responses/response_without_attributes.xml.base64 +79 -0
  116. data/test/responses/response_wrapped.xml.base64 +150 -0
  117. data/test/responses/signed_message_encrypted_signed_assertion.xml.base64 +1 -0
  118. data/test/responses/signed_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  119. data/test/responses/simple_saml_php.xml +71 -0
  120. data/test/responses/starfield_response.xml.base64 +1 -0
  121. data/test/responses/test_sign.xml +43 -0
  122. data/test/responses/unsigned_message_aes128_encrypted_signed_assertion.xml.base64 +1 -0
  123. data/test/responses/unsigned_message_aes192_encrypted_signed_assertion.xml.base64 +1 -0
  124. data/test/responses/unsigned_message_aes256_encrypted_signed_assertion.xml.base64 +1 -0
  125. data/test/responses/unsigned_message_des192_encrypted_signed_assertion.xml.base64 +1 -0
  126. data/test/responses/unsigned_message_encrypted_assertion_without_saml_namespace.xml.base64 +1 -0
  127. data/test/responses/unsigned_message_encrypted_signed_assertion.xml.base64 +1 -0
  128. data/test/responses/unsigned_message_encrypted_unsigned_assertion.xml.base64 +1 -0
  129. data/test/responses/valid_response.xml.base64 +1 -0
  130. data/test/saml_message_test.rb +56 -0
  131. data/test/settings_test.rb +218 -0
  132. data/test/slo_logoutrequest_test.rb +275 -0
  133. data/test/slo_logoutresponse_test.rb +185 -0
  134. data/test/test_helper.rb +252 -0
  135. data/test/utils_test.rb +145 -0
  136. data/test/xml_security_test.rb +329 -0
  137. metadata +415 -0
@@ -0,0 +1,211 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ require 'onelogin/ruby-saml/logoutrequest'
4
+
5
+ class RequestTest < Minitest::Test
6
+
7
+ describe "Logoutrequest" do
8
+ let(:settings) { OneLogin::RubySaml::Settings.new }
9
+
10
+ before do
11
+ settings.idp_slo_target_url = "http://unauth.com/logout"
12
+ settings.name_identifier_value = "f00f00"
13
+ end
14
+
15
+ it "create the deflated SAMLRequest URL parameter" do
16
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
17
+ assert_match /^http:\/\/unauth\.com\/logout\?SAMLRequest=/, unauth_url
18
+
19
+ inflated = decode_saml_request_payload(unauth_url)
20
+ assert_match /^<samlp:LogoutRequest/, inflated
21
+ end
22
+
23
+ it "support additional params" do
24
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
25
+ assert_match /&hello=$/, unauth_url
26
+
27
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :foo => "bar" })
28
+ assert_match /&foo=bar$/, unauth_url
29
+ end
30
+
31
+ it "set sessionindex" do
32
+ sessionidx = UUID.new.generate
33
+ settings.sessionindex = sessionidx
34
+
35
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
36
+ inflated = decode_saml_request_payload(unauth_url)
37
+
38
+ assert_match /<samlp:SessionIndex/, inflated
39
+ assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
40
+ end
41
+
42
+ it "set name_identifier_value" do
43
+ settings.name_identifier_format = "transient"
44
+ name_identifier_value = "abc123"
45
+ settings.name_identifier_value = name_identifier_value
46
+
47
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :nameid => "there" })
48
+ inflated = decode_saml_request_payload(unauth_url)
49
+
50
+ assert_match /<saml:NameID/, inflated
51
+ assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
52
+ end
53
+
54
+ describe "when the target url doesn't contain a query string" do
55
+ it "create the SAMLRequest parameter correctly" do
56
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
57
+ assert_match /^http:\/\/unauth.com\/logout\?SAMLRequest/, unauth_url
58
+ end
59
+ end
60
+
61
+ describe "when the target url contains a query string" do
62
+ it "create the SAMLRequest parameter correctly" do
63
+ settings.idp_slo_target_url = "http://example.com?field=value"
64
+
65
+ unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
66
+ assert_match /^http:\/\/example.com\?field=value&SAMLRequest/, unauth_url
67
+ end
68
+ end
69
+
70
+ describe "consumation of logout may need to track the transaction" do
71
+ it "have access to the request uuid" do
72
+ settings.idp_slo_target_url = "http://example.com?field=value"
73
+
74
+ unauth_req = OneLogin::RubySaml::Logoutrequest.new
75
+ unauth_url = unauth_req.create(settings)
76
+
77
+ inflated = decode_saml_request_payload(unauth_url)
78
+ assert_match %r[ID='#{unauth_req.uuid}'], inflated
79
+ end
80
+ end
81
+
82
+ describe "when the settings indicate to sign (embedded) logout request" do
83
+
84
+ before do
85
+ # sign the logout request
86
+ settings.security[:logout_requests_signed] = true
87
+ settings.security[:embed_sign] = true
88
+ settings.certificate = ruby_saml_cert_text
89
+ settings.private_key = ruby_saml_key_text
90
+ end
91
+
92
+ it "created a signed logout request" do
93
+ settings.compress_request = true
94
+
95
+ unauth_req = OneLogin::RubySaml::Logoutrequest.new
96
+ unauth_url = unauth_req.create(settings)
97
+
98
+ inflated = decode_saml_request_payload(unauth_url)
99
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
100
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
101
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
102
+ end
103
+
104
+ it "create a signed logout request with 256 digest and signature method" do
105
+ settings.compress_request = false
106
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
107
+ settings.security[:digest_method] = XMLSecurity::Document::SHA256
108
+
109
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
110
+ request_xml = Base64.decode64(params["SAMLRequest"])
111
+
112
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
113
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
114
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha256'/>], request_xml
115
+ end
116
+
117
+ it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
118
+ settings.compress_request = false
119
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
120
+ settings.security[:digest_method] = XMLSecurity::Document::SHA512
121
+
122
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
123
+ request_xml = Base64.decode64(params["SAMLRequest"])
124
+
125
+ assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
126
+ assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha384'/>], request_xml
127
+ assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#sha512'/>], request_xml
128
+ end
129
+ end
130
+
131
+ describe "#create_params when the settings indicate to sign the logout request" do
132
+
133
+ let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
134
+
135
+ before do
136
+ # sign the logout request
137
+ settings.security[:logout_requests_signed] = true
138
+ settings.security[:embed_sign] = false
139
+ settings.certificate = ruby_saml_cert_text
140
+ settings.private_key = ruby_saml_key_text
141
+ end
142
+
143
+ it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
144
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
145
+
146
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
147
+ assert params['SAMLRequest']
148
+ assert params[:RelayState]
149
+ assert params['Signature']
150
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
151
+
152
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
153
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
154
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
155
+
156
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
157
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA1
158
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
159
+ end
160
+
161
+ it "create a signature parameter with RSA_SHA256 / SHA256 and validate it" do
162
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
163
+
164
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
165
+ assert params['Signature']
166
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
167
+
168
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
169
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
170
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
171
+
172
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
173
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA256
174
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
175
+ end
176
+
177
+ it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
178
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
179
+
180
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
181
+ assert params['Signature']
182
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
183
+
184
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
185
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
186
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
187
+
188
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
189
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA384
190
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
191
+ end
192
+
193
+ it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
194
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
195
+
196
+ params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
197
+ assert params['Signature']
198
+ assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
199
+
200
+ query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
201
+ query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
202
+ query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
203
+
204
+ signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
205
+ assert_equal signature_algorithm, OpenSSL::Digest::SHA512
206
+ assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
207
+ end
208
+
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,258 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ require 'onelogin/ruby-saml/logoutresponse'
4
+ require 'logout_responses/logoutresponse_fixtures'
5
+
6
+ class RubySamlTest < Minitest::Test
7
+
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
+
13
+ describe "#new" do
14
+ it "raise an exception when response is initialized with nil" do
15
+ assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
16
+ end
17
+ it "default to empty settings" do
18
+ assert_nil valid_logout_response_without_settings.settings
19
+ end
20
+ it "accept constructor-injected settings" do
21
+ refute_nil valid_logout_response.settings
22
+ end
23
+ it "accept constructor-injected options" do
24
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, nil, { :foo => :bar} )
25
+ assert !logoutresponse.options.empty?
26
+ end
27
+ it "support base64 encoded responses" do
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
31
+ end
32
+ end
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
+
51
+ describe "#validate" do
52
+ describe "when soft=true" do
53
+ before do
54
+ settings.soft = true
55
+ end
56
+
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}
60
+
61
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document({:uuid => in_relation_to_request_id}), settings, opts)
62
+
63
+ assert logoutresponse.validate
64
+
65
+ assert_equal settings.issuer, logoutresponse.issuer
66
+ assert_equal in_relation_to_request_id, logoutresponse.in_response_to
67
+
68
+ assert logoutresponse.success?
69
+ assert_empty logoutresponse.errors
70
+ end
71
+
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}
76
+
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
83
+
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
89
+
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
97
+
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}
101
+
102
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_logout_response_document, settings, opts)
103
+
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
108
+
109
+ it "invalidate logout response with wrong request status" do
110
+ logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_logout_response_document, settings)
111
+
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
117
+
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
125
+
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
133
+
134
+ end
135
+
136
+ describe "when soft=false" do
137
+ before do
138
+ settings.soft = false
139
+ end
140
+
141
+ it "validates good logout response" do
142
+ in_relation_to_request_id = random_id
143
+
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)
151
+
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
202
+ end
203
+
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)
251
+
252
+ assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.send(:validate_signature) }
253
+ assert logoutresponse.errors.include? "Invalid Signature on Logout Response"
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end