File: slo_logoutrequest_test.rb

package info (click to toggle)
ruby-saml 1.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,524 kB
  • ctags: 436
  • sloc: ruby: 5,687; xml: 1,070; makefile: 4
file content (302 lines) | stat: -rw-r--r-- 13,619 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
require 'logout_responses/logoutresponse_fixtures'

require 'onelogin/ruby-saml/slo_logoutrequest'
require 'timecop'

class RubySamlTest < Minitest::Test

  describe "SloLogoutrequest" do

    let(:settings) { OneLogin::RubySaml::Settings.new }
    let(:logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(logout_request_document) }
    let(:invalid_logout_request) { OneLogin::RubySaml::SloLogoutrequest.new(invalid_logout_request_document) }

    before do
      settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/SOMEACCOUNT'
      settings.soft = true
      logout_request.settings = settings
      invalid_logout_request.settings = settings
    end

    describe "initiator" do
      it "raise an exception when logout request is initialized with nil" do
        assert_raises(ArgumentError) { OneLogin::RubySaml::SloLogoutrequest.new(nil) }
      end
    end

    describe "#is_valid?" do
      it "return false when logout request is initialized with blank data" do
        logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
        assert !logout_request_blank.is_valid?
        assert_includes logout_request_blank.errors, 'Blank logout request'
      end

      it "return true when the logout request is initialized with valid data" do
        assert logout_request.is_valid?
        assert_empty logout_request.errors
        assert_equal 'someone@example.org', logout_request.nameid
      end

      it "should be idempotent when the logout request is initialized with invalid data" do
        assert !invalid_logout_request.is_valid?
        assert_equal ['Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'], invalid_logout_request.errors
        assert !invalid_logout_request.is_valid?
        assert_equal ['Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd'], invalid_logout_request.errors
      end

      it "should be idempotent when the logout request is initialized with valid data" do
        assert logout_request.is_valid?
        assert_empty logout_request.errors
        assert logout_request.is_valid?
        assert_empty logout_request.errors
      end

      it "collect errors when collect_errors=true" do
        settings.idp_entity_id = 'http://idp.example.com/invalid'
        settings.idp_slo_target_url = "http://example.com?field=value"
        settings.security[:logout_requests_signed] = true
        settings.security[:embed_sign] = false
        settings.certificate = ruby_saml_cert_text
        settings.private_key = ruby_saml_key_text
        settings.idp_cert = ruby_saml_cert_text
        settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
        params = {}
        params['SAMLRequest'] = logout_request_deflated_base64
        params['RelayState'] = 'http://invalid.example.com'
        params['Signature'] = 'invalid_signature'
        params['SigAlg'] = XMLSecurity::Document::RSA_SHA1
        options = {}
        options[:get_params] = params

        logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
        logout_request_sign_test.settings = settings

        collect_errors = true
        assert !logout_request_sign_test.is_valid?(collect_errors)
        assert_includes logout_request_sign_test.errors,  "Invalid Signature on Logout Request"
        assert_includes logout_request_sign_test.errors,  "Doesn't match the issuer, expected: <http://idp.example.com/invalid>, but was: <https://app.onelogin.com/saml/metadata/SOMEACCOUNT>"
      end

      it "raise error for invalid xml" do
        invalid_logout_request.soft = false
        assert_raises(OneLogin::RubySaml::ValidationError) { invalid_logout_request.is_valid? }
      end

    end

    describe "#nameid" do
      it "extract the value of the name id element" do
        assert_equal "someone@example.org", logout_request.nameid
      end
    end

    describe "#issuer" do
      it "return the issuer inside the logout request" do
        assert_equal "https://app.onelogin.com/saml/metadata/SOMEACCOUNT", logout_request.issuer
      end
    end

    describe "#id" do
      it "extract the value of the ID attribute" do
        assert_equal "_c0348950-935b-0131-1060-782bcb56fcaa", logout_request.id
      end
    end

   describe "#not_on_or_after" do
      it "extract the value of the NotOnOrAfter attribute" do
        time_value = '2014-07-17T01:01:48Z'
        assert_equal nil, logout_request.not_on_or_after
        logout_request.document.root.attributes['NotOnOrAfter'] = time_value
        assert_equal Time.parse(time_value), logout_request.not_on_or_after
      end
    end

    describe '#session_indexes' do
      it "return empty array when no SessionIndex" do
        assert_equal [], logout_request.session_indexes
      end

      it "return an Array with one SessionIndex" do
        logout_request_with_session_index = OneLogin::RubySaml::SloLogoutrequest.new(logout_request_xml_with_session_index)
        assert_equal ['_ea853497-c58a-408a-bc23-c849752d9741'], logout_request_with_session_index.session_indexes
      end
    end

    describe "#validate_id" do
      it "return true when there is a valid ID in the logout request" do
        assert logout_request.send(:validate_id)
        assert_empty logout_request.errors
      end

      it "return false when there is an invalid ID in the logout request" do
        logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
        assert !logout_request_blank.send(:validate_id)
        assert_includes logout_request_blank.errors, "Missing ID attribute on Logout Request"
      end
    end

    describe "#validate_version" do
      it "return true when the logout request is SAML 2.0 Version" do
        assert logout_request.send(:validate_version)
      end

      it "return false when the logout request is not SAML 2.0 Version" do
        logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
        assert !logout_request_blank.send(:validate_version)
        assert_includes logout_request_blank.errors, "Unsupported SAML version"
      end
    end

    describe "#validate_not_on_or_after" do
      it "return true when the logout request has a valid NotOnOrAfter or does not contain any" do
        assert logout_request.send(:validate_not_on_or_after)
        assert_empty logout_request.errors
        Timecop.freeze Time.parse('2011-06-14T18:25:01.516Z') do
          time_value = '2014-07-17T01:01:48Z'
          logout_request.document.root.attributes['NotOnOrAfter'] = time_value
          assert logout_request.send(:validate_not_on_or_after)
          assert_empty logout_request.errors
        end
      end

      it "return false when the logout request has an invalid NotOnOrAfter" do
        logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z'
        assert !logout_request.send(:validate_not_on_or_after)
        assert /Current time is on or after NotOnOrAfter/.match(logout_request.errors[0])
      end

      it "raise when the logout request has an invalid NotOnOrAfter" do
        logout_request.document.root.attributes['NotOnOrAfter'] = '2014-07-17T01:01:48Z'
        logout_request.soft = false
        assert_raises(OneLogin::RubySaml::ValidationError, "Current time is on or after NotOnOrAfter") do
          logout_request.send(:validate_not_on_or_after)
        end
      end
    end

    describe "#validate_request_state" do
      it "return true when valid logout request xml" do
        assert logout_request.send(:validate_request_state)
        assert_empty logout_request.errors
        assert logout_request.send(:validate_request_state)
        assert_empty logout_request.errors
      end

      it "return false when invalid logout request xml" do
        logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
        logout_request_blank.soft = true
        assert !logout_request_blank.send(:validate_request_state)
        assert_includes logout_request_blank.errors, "Blank logout request"
      end

      it "raise error for invalid xml" do
        logout_request_blank = OneLogin::RubySaml::SloLogoutrequest.new('')
        logout_request_blank.soft = false
        assert_raises(OneLogin::RubySaml::ValidationError, "Blank logout request") do
          logout_request_blank.send(:validate_request_state)
        end
      end
    end

    describe "#validate_structure" do
      it "return true when encountering a valid Logout Request xml" do
        assert logout_request.send(:validate_structure)
        assert_empty logout_request.errors
      end

      it "return false when encountering a Logout Request bad formatted" do
        assert !invalid_logout_request.send(:validate_structure)
        assert_includes invalid_logout_request.errors, "Invalid SAML Logout Request. Not match the saml-schema-protocol-2.0.xsd"
      end

      it "raise when encountering a Logout Request bad formatted" do
        invalid_logout_request.soft = false
        assert_raises(OneLogin::RubySaml::ValidationError, "Element '{urn:oasis:names:tc:SAML:2.0:assertion}Issuer': This element is not expected") do
          invalid_logout_request.send(:validate_structure)
        end
      end
    end

    describe "#validate_issuer" do
      it "return true when the issuer of the Logout Request matchs the IdP entityId" do
        logout_request.settings.idp_entity_id = 'https://app.onelogin.com/saml/metadata/SOMEACCOUNT'
        assert logout_request.send(:validate_issuer)
      end
      it "return false when the issuer of the Logout Request does not match the IdP entityId" do
        logout_request.settings.idp_entity_id = 'http://idp.example.com/invalid'
        assert !logout_request.send(:validate_issuer)
        assert_includes logout_request.errors, "Doesn't match the issuer, expected: <#{logout_request.settings.idp_entity_id}>, but was: <https://app.onelogin.com/saml/metadata/SOMEACCOUNT>"
      end
      it "raise when the issuer of the Logout Request does not match the IdP entityId" do
        logout_request.settings.idp_entity_id = 'http://idp.example.com/invalid'
        logout_request.soft = false
        assert_raises(OneLogin::RubySaml::ValidationError, "Doesn't match the issuer, expected: <#{logout_request.settings.idp_entity_id}>, but was: <https://app.onelogin.com/saml/metadata/SOMEACCOUNT>") do
          logout_request.send(:validate_issuer)
        end
      end
    end

    describe "#validate_signature" do
      before do
        settings.idp_slo_target_url = "http://example.com?field=value"
        settings.security[:logout_requests_signed] = true
        settings.security[:embed_sign] = false
        settings.certificate = ruby_saml_cert_text
        settings.private_key = ruby_saml_key_text
        settings.idp_cert = ruby_saml_cert_text
      end

      it "return true when valid RSA_SHA1 Signature" do
        settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
        params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
        params['RelayState'] = params[:RelayState]
        options = {}
        options[:get_params] = params
        logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
        logout_request_sign_test.settings = settings
        assert logout_request_sign_test.send(:validate_signature)
      end

      it "return true when valid RSA_SHA256 Signature" do
        settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
        params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
        options = {}
        options[:get_params] = params
        logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
        params['RelayState'] = params[:RelayState]
        logout_request_sign_test.settings = settings
        assert logout_request_sign_test.send(:validate_signature)
      end

      it "return false when invalid RSA_SHA1 Signature" do
        settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
        params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
        params['RelayState'] = 'http://invalid.example.com'
        params[:RelayState] = params['RelayState']
        options = {}
        options[:get_params] = params

        logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
        logout_request_sign_test.settings = settings
        assert !logout_request_sign_test.send(:validate_signature)
      end

      it "raise when invalid RSA_SHA1 Signature" do
        settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
        settings.soft = false
        params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
        params['RelayState'] = 'http://invalid.example.com'
        params[:RelayState] = params['RelayState']
        options = {}
        options[:get_params] = params
        options[:settings] = settings

        logout_request_sign_test = OneLogin::RubySaml::SloLogoutrequest.new(params['SAMLRequest'], options)
        assert_raises(OneLogin::RubySaml::ValidationError, "Invalid Signature on Logout Request") do
          logout_request_sign_test.send(:validate_signature)
        end
      end
    end
  end
end