ruby-saml 0.8.10 → 0.8.15

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.

Potentially problematic release.


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

Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Rakefile +0 -14
  4. data/lib/onelogin/ruby-saml/authrequest.rb +3 -2
  5. data/lib/onelogin/ruby-saml/logoutrequest.rb +3 -0
  6. data/lib/onelogin/ruby-saml/logoutresponse.rb +1 -24
  7. data/lib/onelogin/ruby-saml/response.rb +206 -20
  8. data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
  9. data/lib/onelogin/ruby-saml/settings.rb +26 -0
  10. data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +3 -2
  11. data/lib/onelogin/ruby-saml/utils.rb +90 -0
  12. data/lib/onelogin/ruby-saml/version.rb +1 -1
  13. data/lib/xml_security.rb +222 -87
  14. data/test/certificates/ruby-saml-2.crt +15 -0
  15. data/test/logoutrequest_test.rb +124 -126
  16. data/test/logoutresponse_test.rb +22 -28
  17. data/test/response_test.rb +348 -129
  18. data/test/responses/adfs_response_xmlns.xml +45 -0
  19. data/test/responses/encrypted_new_attack.xml.base64 +1 -0
  20. data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
  21. data/test/responses/invalids/no_signature.xml.base64 +1 -0
  22. data/test/responses/invalids/response_with_concealed_signed_assertion.xml +51 -0
  23. data/test/responses/invalids/response_with_doubled_signed_assertion.xml +49 -0
  24. data/test/responses/invalids/signature_wrapping_attack.xml.base64 +1 -0
  25. data/test/responses/response_with_concealed_signed_assertion.xml +51 -0
  26. data/test/responses/response_with_doubled_signed_assertion.xml +49 -0
  27. data/test/responses/response_with_signed_assertion_3.xml +30 -0
  28. data/test/responses/response_with_signed_message_and_assertion.xml +34 -0
  29. data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
  30. data/test/responses/response_wrapped.xml.base64 +150 -0
  31. data/test/responses/valid_response.xml.base64 +1 -0
  32. data/test/responses/valid_response_without_x509certificate.xml.base64 +1 -0
  33. data/test/settings_test.rb +5 -5
  34. data/test/test_helper.rb +110 -41
  35. data/test/utils_test.rb +10 -10
  36. data/test/xml_security_test.rb +359 -68
  37. metadata +38 -5
@@ -0,0 +1 @@
1
+ PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGJjODI2YWZkLWU5ZmUtZDNmYi1kODc0LWM0NzAwYzNlZjBjOCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2FwcC5tdWRhLm5vL3Nzby9jb25zdW1lIiBJblJlc3BvbnNlVG89Il9mYzRhMzRiMC03ZWZiLTAxMmUtY2FhZS03ODJiY2IxM2JiMzgiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbDI8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhiYzgyNmFmZC1lOWZlLWQzZmItZDg3NC1jNDcwMGMzZWYwYzgiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPkl6NFpRbHMzQUpaRGIzczh2Y1VYLzNSYytGUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+UWhLSm1vbnlzUDFxbW5hN1MrZUUxTGMycktBampDMk9HclFPZ1NqUHBUb2N1bVE2aFlIa3pUU1pyN3QvSS9LVE9TdkhDUXFEMXJoNGxTMGpEUC9FdUhOQUN0azlZN2xsMlV5Z3U3MkwrYkZ0cVoyOURuOXJMa1NkR3JpK0k3SGh4TDM2N2RmQVNTaDYrc3k3V2V2RWRrTWZ3ZURRMkFYL3NhNkJCR2d6N1RFPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDR3pDQ0FZUUNDUUNOTmNRWG9tMzJWREFOQmdrcWhraUc5dzBCQVFVRkFEQlNNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0JNQ1NVNHhGVEFUQmdOVkJBY1RERWx1WkdsaGJtRndiMnhwY3pFUk1BOEdBMVVFQ2hNSVQyNWxURzluYVc0eEREQUtCZ05WQkFzVEEwVnVaekFlRncweE5EQTBNak14T0RReE1ERmFGdzB4TlRBME1qTXhPRFF4TURGYU1GSXhDekFKQmdOVkJBWVRBbFZUTVFzd0NRWURWUVFJRXdKSlRqRVZNQk1HQTFVRUJ4TU1TVzVrYVdGdVlYQnZiR2x6TVJFd0R3WURWUVFLRXdoUGJtVk1iMmRwYmpFTU1Bb0dBMVVFQ3hNRFJXNW5NSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURvNm0rUVp2WVEveEwwRWxMZ3VwSzFRRGNZTDRmNVBja3dzTmdTOXBVdlY3ZnpUcUNIazhUaEx4VGs0Mk1RMk1jSnNPZVVKVlA3MjhLaHltakZDcXhnUDRWdXdSazlycEFsMCttaHk2TVBkeWp5QTZHMTRqckRXUzY1eXNMY2hLNHQvdndwRUR6MFNRbEVvRzFrTXpsbFNtN3paUzNYcmVnQTdEak5hVVlRcXdJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBTE0ydkdDaVEvdm0rYTZ2NDArVlgyemRxSEEyUS8xdkYxaWJReko1NE1KQ09WV3ZzK3ZRWGZaRmhkbTBPUE0ySXJEVTdvcXZLUHFQNnhPQWVKSzZIMHlQN000WUwzZmF0U3ZJWW1tZnlYQzlrdDNTdnovTnlySHpQaFVuSjB5ZS9zVVNYeG56UXh3Y20vOVB3QXFyUWFBM1FwUWtINTd5YkYvT29yeVBlKzJoPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgVmVyc2lvbj0iMi4wIiBJRD0icGZ4OTUxNmIwZjMtNDUzNi0xMGY2LWM2ZmEtOWRkNTIzZTE0OThjIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwyPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+dGVzdEBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMzAtMDYtMDRUMDI6Mjc6MDJaIiBSZWNpcGllbnQ9InJlY2lwaWVudCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDExLTA2LTA0VDAyOjE3OjAyWiIgTm90T25PckFmdGVyPSIyMDMwLTA2LTA0VDAyOjI3OjAyWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovL3NvbWVvbmUuZXhhbXBsZS5jb20vYXVkaWVuY2U8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA2LTA0VDAyOjIyOjAyWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAzMC0wNi0wNVQwMjoyMjowMloiIFNlc3Npb25JbmRleD0iXzE2ZjU3MGZiYzAzMTUwMDdhMDM1NWRmZWE2YjNjNDZjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
@@ -0,0 +1 @@
1
+ pVZdd9o4EH3fc/Y/+LiPOcayDTb4BLoU0oSWfBDTbpuXPbI0Bie25Egi0Pz6lQ04kJI03X2CGY/u3LkjjXT8fpVnxgMImXLWNZ0GMt/3/vzjWOI8K8JrkAVnEgwdxGRYOrvmQrCQY5nKkOEcZKhIGPXPx6HbQCGWEoTSUObOkuL1NYXgihOemcZo2DWLZBWTtuvjhFrQScCiXhJbtB00LdIMECIeJIi0TePrlrPG0EulXMCISYWZ0i7kNC3kW6g5RW7ouiFyb0xjCFKlDKtq1VypIrRtXBSNfEFxg3FbSm4TXe4iBw3ItsVPedf8JyFN7DVjZAWg6SDHBYtgDFbQdmMSO14ce22zV8kWVlxEr8wgNyk4g4zPUtYgPLfLIPfY3o09pjKM0pkmtxBbtamsWS6Xy8bSa3Axs12EkI06to6hMp29M3W3DGO7HuiIJbyCG2DGWUpwlj5WJZ+DmnNq9LMZF6ma5y+AO7aDSnALVsQiTpO9M+0qxVOSiuQb4fa4CoktOcfOBrHEu4YEBDACxpfrUdd899b2VyVOBWYy4SKX++bvsQL2oJtTALXktjhN8PcAD6p2bP/McZjO9C78L+JthHsC+YqzBfRGj82bSSa9/qebYezJ9gP58s32rsnRx0m3IrAbXDlqydfms21TN3i9YjL//Cnn7Ie8cu5zhoPoCE6cMXHF5/7t7cC9PBWTy1l0e1VMOVnkE3/+/ezucRrdiEDZI/vz9DJ6OBtM7oeOmDezCN0Or+yTxdlFf6DuOt+DLHO//JgtAnd8FH9U9zduZ8g6YnwX0VORHo2Cs/lq7PkBTfpRNPeP5I/gb3g4oXfnyRKGE7f/TR8i/8OH09ljMD3p1uXs8NejbM/b20y2SGlT7lsDTsGolr0+sGQVHUYLQkDKqtE/g4b97SjcHOfVS8fZsb+djyMyhxybdWz662ArrcYdgZ9m4XqMdlqOH6PEs5otz7cclPgW8RNsdShtuR44zU6bvGlw/o+xVhnRIr4FojbWhdZzNDQ+6iOB1ctCOw2n8qTUSqrQUJecZn1KRSl6T+lN/ddu/k3mNfx+5gFnSVpilN1Yn73XO0zyMAYsQJgvAw2xwsYFV5fsUvQTBaJUz0M76gXra+caSFqkUMortn/rTXMI+dmnDQUdQdPysyyzfgCtClQNc55SOpuUb6C13aULmpazQF92SqRknX7vS91wyXPQgjdghfMig6rneBO0YVyveWbvodvPyqnzqTkrTxDkWiCjMn9xoUd6J2iEF6ptHQgdMQorfZ07ftIKUBIT5DkthAKMvFaLJoD92CNNn5i7pDRVBSt1wDXI9INHz9Peq28iEpIyTruv9M+SC3qlnzy6s0Cr26HgQtWCHQA/8G3PV4tWe7ejp55M27dM718=
@@ -1,12 +1,12 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
2
 
3
- class SettingsTest < Test::Unit::TestCase
3
+ class SettingsTest < Minitest::Test
4
4
 
5
- context "Settings" do
6
- setup do
5
+ describe "Settings" do
6
+ before do
7
7
  @settings = OneLogin::RubySaml::Settings.new
8
8
  end
9
- should "should provide getters and settings" do
9
+ it "should provide getters and settings" do
10
10
  accessors = [
11
11
  :assertion_consumer_service_url, :issuer, :sp_entity_id, :sp_name_qualifier,
12
12
  :idp_sso_target_url, :idp_cert_fingerprint, :name_identifier_format,
@@ -22,7 +22,7 @@ class SettingsTest < Test::Unit::TestCase
22
22
  end
23
23
  end
24
24
 
25
- should "create settings from hash" do
25
+ it "create settings from hash" do
26
26
 
27
27
  config = {
28
28
  :assertion_consumer_service_url => "http://app.muda.no/sso",
@@ -1,17 +1,22 @@
1
1
  require 'rubygems'
2
- require 'test/unit'
3
2
  require 'minitest/autorun'
4
3
  require 'shoulda'
5
4
  require 'mocha/setup'
6
5
  require 'timecop'
7
6
 
7
+ if RUBY_VERSION < '1.9'
8
+ require 'uuid'
9
+ else
10
+ require 'securerandom'
11
+ end
12
+
8
13
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
14
  $LOAD_PATH.unshift(File.dirname(__FILE__))
10
15
  require 'ruby-saml'
11
16
 
12
17
  ENV["ruby-saml/testing"] = "1"
13
18
 
14
- class Test::Unit::TestCase
19
+ class Minitest::Test
15
20
  def fixture(document, base64 = true)
16
21
  response = Dir.glob(File.join(File.dirname(__FILE__), "responses", "#{document}*")).first
17
22
  if base64 && response =~ /\.xml$/
@@ -21,32 +26,48 @@ class Test::Unit::TestCase
21
26
  end
22
27
  end
23
28
 
29
+ def random_id
30
+ RUBY_VERSION < '1.9' ? "_#{UUID.new.generate}" : "_#{SecureRandom.uuid}"
31
+ end
32
+
33
+ def read_invalid_response(response)
34
+ File.read(File.join(File.dirname(__FILE__), "responses", "invalids", response))
35
+ end
36
+
37
+ def read_response(response)
38
+ File.read(File.join(File.dirname(__FILE__), "responses", response))
39
+ end
40
+
41
+ def read_certificate(certificate)
42
+ File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
43
+ end
44
+
24
45
  def response_document
25
- @response_document ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response1.xml.base64'))
46
+ @response_document ||= read_response('response1.xml.base64')
26
47
  end
27
48
 
28
49
  def response_document_2
29
- @response_document2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response2.xml.base64'))
50
+ @response_document2 ||= read_response('response2.xml.base64')
30
51
  end
31
52
 
32
53
  def response_document_3
33
- @response_document3 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response3.xml.base64'))
54
+ @response_document3 ||= read_response('response3.xml.base64')
34
55
  end
35
56
 
36
57
  def response_document_4
37
- @response_document4 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response4.xml.base64'))
58
+ @response_document4 ||= read_response('response4.xml.base64')
38
59
  end
39
60
 
40
61
  def response_document_5
41
- @response_document5 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response5.xml.base64'))
62
+ @response_document5 ||= read_response('response5.xml.base64')
42
63
  end
43
64
 
44
65
  def r1_response_document_6
45
- @response_document6 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'r1_response6.xml.base64'))
66
+ @response_document6 ||= read_response('r1_response6.xml.base64')
46
67
  end
47
68
 
48
69
  def ampersands_response
49
- @ampersands_resposne ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'response_with_ampersands.xml.base64'))
70
+ @ampersands_resposne ||= read_response('response_with_ampersands.xml.base64')
50
71
  end
51
72
 
52
73
  def response_document_6
@@ -56,6 +77,22 @@ class Test::Unit::TestCase
56
77
  Base64.encode64(doc)
57
78
  end
58
79
 
80
+ def response_document_wrapped
81
+ @response_document_wrapped ||= read_response("response_wrapped.xml.base64")
82
+ end
83
+
84
+ def response_document_valid_signed
85
+ response_document_valid_signed ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'valid_response.xml.base64'))
86
+ end
87
+
88
+ def response_document_valid_signed_without_x509certificate
89
+ @response_document_valid_signed_without_x509certificate ||= read_response("valid_response_without_x509certificate.xml.base64")
90
+ end
91
+
92
+ def response_document_without_recipient
93
+ @response_document_without_recipient ||= read_response("response_with_undefined_recipient.xml.base64")
94
+ end
95
+
59
96
  def wrapped_response_2
60
97
  @wrapped_response_2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'wrapped_response_2.xml.base64'))
61
98
  end
@@ -64,12 +101,24 @@ class Test::Unit::TestCase
64
101
  @signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
65
102
  end
66
103
 
104
+ def signature_fingerprint_valid_res
105
+ @signature_fingerprint1 ||= "4b68c453c7d994aad9025c99d5efcf566287fe8d"
106
+ end
107
+
67
108
  def signature_1
68
- @signature1 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'certificate1'))
109
+ @signature1 ||= read_certificate('certificate1')
69
110
  end
70
111
 
71
112
  def r1_signature_2
72
- @signature2 ||= File.read(File.join(File.dirname(__FILE__), 'certificates', 'r1_certificate2_base64'))
113
+ @signature2 ||= read_certificate('r1_certificate2_base64')
114
+ end
115
+
116
+ def valid_cert
117
+ @signature_valid_cert ||= read_certificate('ruby-saml.crt')
118
+ end
119
+
120
+ def valid_key
121
+ @signature_valid_cert ||= read_certificate('ruby-saml.key')
73
122
  end
74
123
 
75
124
  def response_with_multiple_attribute_statements
@@ -79,40 +128,60 @@ class Test::Unit::TestCase
79
128
  def response_multiple_attr_values
80
129
  @response_multiple_attr_values = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
81
130
  end
82
- end
83
131
 
84
- def ruby_saml_cert_text
85
- read_certificate("ruby-saml.crt")
86
- end
132
+ def ruby_saml_cert
133
+ @ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
134
+ end
87
135
 
88
- def ruby_saml_key_text
89
- read_certificate("ruby-saml.key")
90
- end
136
+ def ruby_saml_cert2
137
+ @ruby_saml_cert2 ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text2)
138
+ end
91
139
 
92
- def read_certificate(certificate)
93
- File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
94
- end
140
+ def ruby_saml_cert_fingerprint
141
+ @ruby_saml_cert_fingerprint ||= Digest::SHA1.hexdigest(ruby_saml_cert.to_der).scan(/../).join(":")
142
+ end
95
143
 
96
- def decode_saml_request_payload(unauth_url)
97
- payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
98
- decoded = Base64.decode64(payload)
144
+ def ruby_saml_cert_text
145
+ read_certificate("ruby-saml.crt")
146
+ end
99
147
 
100
- zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
101
- inflated = zstream.inflate(decoded)
102
- zstream.finish
103
- zstream.close
104
- inflated
105
- end
148
+ def ruby_saml_cert_text2
149
+ read_certificate("ruby-saml-2.crt")
150
+ end
151
+
152
+ def ruby_saml_key_text
153
+ read_certificate("ruby-saml.key")
154
+ end
106
155
 
107
- # decodes a base64 encoded SAML response for use in SloLogoutresponse tests
108
- #
109
- def decode_saml_response_payload(unauth_url)
110
- payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
111
- decoded = Base64.decode64(payload)
112
-
113
- zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
114
- inflated = zstream.inflate(decoded)
115
- zstream.finish
116
- zstream.close
117
- inflated
156
+ def ruby_saml_key
157
+ @ruby_saml_key ||= OpenSSL::PKey::RSA.new(ruby_saml_key_text)
158
+ end
159
+
160
+ def read_certificate(certificate)
161
+ File.read(File.join(File.dirname(__FILE__), "certificates", certificate))
162
+ end
163
+
164
+ def decode_saml_request_payload(unauth_url)
165
+ payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
166
+ decoded = Base64.decode64(payload)
167
+
168
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
169
+ inflated = zstream.inflate(decoded)
170
+ zstream.finish
171
+ zstream.close
172
+ inflated
173
+ end
174
+
175
+ # decodes a base64 encoded SAML response for use in SloLogoutresponse tests
176
+ #
177
+ def decode_saml_response_payload(unauth_url)
178
+ payload = CGI.unescape(unauth_url.split("SAMLResponse=").last)
179
+ decoded = Base64.decode64(payload)
180
+
181
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
182
+ inflated = zstream.inflate(decoded)
183
+ zstream.finish
184
+ zstream.close
185
+ inflated
186
+ end
118
187
  end
@@ -1,38 +1,38 @@
1
1
  require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
2
 
3
- class UtilsTest < Test::Unit::TestCase
4
- context "Utils" do
5
- context 'element_text' do
6
- should 'returns the element text' do
3
+ class UtilsTest < Minitest::Test
4
+ describe "Utils" do
5
+ describe 'element_text' do
6
+ it 'returns the element text' do
7
7
  element = REXML::Document.new('<element>element text</element>').elements.first
8
8
  assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
9
9
  end
10
10
 
11
- should 'returns all segments of the element text' do
11
+ it 'returns all segments of the element text' do
12
12
  element = REXML::Document.new('<element>element <!-- comment -->text</element>').elements.first
13
13
  assert_equal 'element text', OneLogin::RubySaml::Utils.element_text(element)
14
14
  end
15
15
 
16
- should 'returns normalized element text' do
16
+ it 'returns normalized element text' do
17
17
  element = REXML::Document.new('<element>element &amp; text</element>').elements.first
18
18
  assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
19
19
  end
20
20
 
21
- should 'returns the CDATA element text' do
21
+ it 'returns the CDATA element text' do
22
22
  element = REXML::Document.new('<element><![CDATA[element & text]]></element>').elements.first
23
23
  assert_equal 'element & text', OneLogin::RubySaml::Utils.element_text(element)
24
24
  end
25
25
 
26
- should 'returns the element text with newlines and additional whitespace' do
26
+ it 'returns the element text with newlines and additional whitespace' do
27
27
  element = REXML::Document.new("<element> element \n text </element>").elements.first
28
28
  assert_equal " element \n text ", OneLogin::RubySaml::Utils.element_text(element)
29
29
  end
30
30
 
31
- should 'returns nil when element is nil' do
31
+ it 'returns nil when element is nil' do
32
32
  assert_nil OneLogin::RubySaml::Utils.element_text(nil)
33
33
  end
34
34
 
35
- should 'returns empty string when element has no text' do
35
+ it 'returns empty string when element has no text' do
36
36
  element = REXML::Document.new('<element></element>').elements.first
37
37
  assert_equal '', OneLogin::RubySaml::Utils.element_text(element)
38
38
  end
@@ -1,94 +1,197 @@
1
- require 'test_helper'
2
- require 'xml_security'
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
3
2
 
4
- class XmlSecurityTest < Test::Unit::TestCase
3
+ class XmlSecurityTest < Minitest::Test
5
4
  include XMLSecurity
6
5
 
7
- context "XmlSecurity" do
8
- setup do
9
- @document = XMLSecurity::SignedDocument.new(Base64.decode64(response_document))
10
- @base64cert = @document.elements["//ds:X509Certificate"].text
6
+ describe "XmlSecurity" do
7
+
8
+ let(:decoded_response) { Base64.decode64(response_document_without_recipient) }
9
+ let(:document) { XMLSecurity::SignedDocument.new(decoded_response) }
10
+ let(:settings) { OneLogin::RubySaml::Settings.new() }
11
+
12
+ before do
13
+ @base64cert = document.elements["//ds:X509Certificate"].text
11
14
  end
12
15
 
13
- should "should run validate without throwing NS related exceptions" do
14
- assert !@document.validate_signature(@base64cert, true)
16
+ it "should run validate without throwing NS related exceptions" do
17
+ assert !document.validate_signature(@base64cert, true)
15
18
  end
16
19
 
17
- should "should run validate with throwing NS related exceptions" do
18
- assert_raise(OneLogin::RubySaml::ValidationError) do
19
- @document.validate_signature(@base64cert, false)
20
+ it "should run validate with throwing NS related exceptions" do
21
+ assert_raises(OneLogin::RubySaml::ValidationError) do
22
+ document.validate_signature(@base64cert, false)
20
23
  end
21
24
  end
22
25
 
23
- should "not raise an error when softly validating the document multiple times" do
24
- assert_nothing_raised do
25
- 2.times { @document.validate_signature(@base64cert, true) }
26
- end
26
+ it "not raise an error when softly validating the document multiple times" do
27
+ 2.times { assert_equal document.validate_signature(@base64cert, true), false }
27
28
  end
28
29
 
29
- should "should raise Fingerprint mismatch" do
30
- exception = assert_raise(OneLogin::RubySaml::ValidationError) do
31
- @document.validate_document("no:fi:ng:er:pr:in:t", false)
30
+ it "not raise an error when softly validating the document and the X509Certificate is missing" do
31
+ decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
32
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
33
+ assert !mod_document.validate_document("a fingerprint", true) # The fingerprint isn't relevant to this test
34
+ end
35
+
36
+ it "should raise Fingerprint mismatch" do
37
+ exception = assert_raises(OneLogin::RubySaml::ValidationError) do
38
+ document.validate_document("no:fi:ng:er:pr:in:t", false)
32
39
  end
33
40
  assert_equal("Fingerprint mismatch", exception.message)
34
41
  end
35
42
 
36
- should "should raise Digest mismatch" do
37
- exception = assert_raise(OneLogin::RubySaml::ValidationError) do
38
- @document.validate_signature(@base64cert, false)
43
+ it "should raise Digest mismatch" do
44
+ exception = assert_raises(OneLogin::RubySaml::ValidationError) do
45
+ document.validate_signature(@base64cert, false)
39
46
  end
40
47
  assert_equal("Digest mismatch", exception.message)
41
48
  end
42
49
 
43
- should "should raise Key validation error" do
44
- response = Base64.decode64(response_document)
45
- response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
50
+ it "should raise Key validation error" do
51
+ decoded_response.sub!("<ds:DigestValue>pJQ7MS/ek4KRRWGmv/H43ReHYMs=</ds:DigestValue>",
46
52
  "<ds:DigestValue>b9xsAXLsynugg3Wc1CI3kpWku+0=</ds:DigestValue>")
47
- document = XMLSecurity::SignedDocument.new(response)
48
- base64cert = document.elements["//ds:X509Certificate"].text
49
- exception = assert_raise(OneLogin::RubySaml::ValidationError) do
50
- document.validate_signature(base64cert, false)
53
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
54
+ base64cert = mod_document.elements["//ds:X509Certificate"].text
55
+ exception = assert_raises(OneLogin::RubySaml::ValidationError) do
56
+ mod_document.validate_signature(base64cert, false)
51
57
  end
52
58
  assert_equal("Key validation error", exception.message)
53
59
  end
54
60
 
55
- should "raise validation error when the X509Certificate is missing" do
56
- response = Base64.decode64(response_document)
57
- response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
58
- document = XMLSecurity::SignedDocument.new(response)
59
- exception = assert_raise(OneLogin::RubySaml::ValidationError) do
60
- document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
61
+ it "correctly obtain the digest method with alternate namespace declaration" do
62
+ adfs_document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_xmlns, false))
63
+ base64cert = adfs_document.elements["//X509Certificate"].text
64
+ assert adfs_document.validate_signature(base64cert, false)
65
+ end
66
+
67
+ it "raise validation error when the X509Certificate is missing and no cert provided" do
68
+ decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
69
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
70
+ exception = assert_raises(OneLogin::RubySaml::ValidationError) do
71
+ mod_document.validate_document("a fingerprint", false) # The fingerprint isn't relevant to this test
61
72
  end
62
- assert_equal("Certificate element missing in response (ds:X509Certificate)", exception.message)
73
+ assert_equal("Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", exception.message)
74
+ end
75
+
76
+ it "invalidaties when the X509Certificate is missing and the cert is provided but mismatches" do
77
+ decoded_response.sub!(/<ds:X509Certificate>.*<\/ds:X509Certificate>/, "")
78
+ mod_document = XMLSecurity::SignedDocument.new(decoded_response)
79
+ cert = OpenSSL::X509::Certificate.new(ruby_saml_cert)
80
+ assert !mod_document.validate_document("a fingerprint", true, :cert => cert) # The fingerprint isn't relevant to this test
81
+ end
82
+ end
83
+
84
+ describe "#canon_algorithm" do
85
+ it "C14N_EXCLUSIVE_1_0" do
86
+ canon_algorithm = Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
87
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#")
88
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2001/10/xml-exc-c14n#WithComments")
89
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("other")
90
+ end
91
+
92
+ it "C14N_1_0" do
93
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_0
94
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
95
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments")
96
+ end
97
+
98
+ it "XML_C14N_1_1" do
99
+ canon_algorithm = Nokogiri::XML::XML_C14N_1_1
100
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2006/12/xml-c14n11")
101
+ assert_equal canon_algorithm, XMLSecurity::BaseDocument.new.canon_algorithm("http://www.w3.org/2006/12/xml-c14n11#WithComments")
63
102
  end
64
103
  end
65
104
 
66
- context "Algorithms" do
67
- should "validate using SHA1" do
68
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
69
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
105
+ describe "#algorithm" do
106
+ it "SHA1" do
107
+ alg = OpenSSL::Digest::SHA1
108
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#rsa-sha1")
109
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2000/09/xmldsig#sha1")
110
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("other")
70
111
  end
71
112
 
72
- should "validate using SHA256" do
73
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
74
- assert @document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
113
+ it "SHA256" do
114
+ alg = OpenSSL::Digest::SHA256
115
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256")
116
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha256")
75
117
  end
76
118
 
77
- should "validate using SHA384" do
78
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
79
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
119
+ it "SHA384" do
120
+ alg = OpenSSL::Digest::SHA384
121
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384")
122
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha384")
80
123
  end
81
124
 
82
- should "validate using SHA512" do
83
- @document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
84
- assert @document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
125
+ it "SHA512" do
126
+ alg = OpenSSL::Digest::SHA512
127
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512")
128
+ assert_equal alg, XMLSecurity::BaseDocument.new.algorithm("http://www.w3.org/2001/04/xmldsig-more#sha512")
85
129
  end
86
130
  end
87
131
 
88
- context "XmlSecurity::SignedDocument" do
132
+ describe "Fingerprint Algorithms" do
133
+ let(:response_fingerprint_test) { OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha1, false)) }
134
+
135
+ it "validate using SHA1" do
136
+ sha1_fingerprint = "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72"
137
+ sha1_fingerprint_downcase = "f13c6b80905a030e6c913e5d15faddb016454872"
138
+
139
+ assert response_fingerprint_test.document.validate_document(sha1_fingerprint)
140
+ assert response_fingerprint_test.document.validate_document(sha1_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA1)
141
+
142
+ assert response_fingerprint_test.document.validate_document(sha1_fingerprint_downcase)
143
+ assert response_fingerprint_test.document.validate_document(sha1_fingerprint_downcase, true, :fingerprint_alg => XMLSecurity::Document::SHA1)
144
+ end
145
+
146
+ it "validate using SHA256" do
147
+ sha256_fingerprint = "C4:C6:BD:41:EC:AD:57:97:CE:7B:7D:80:06:C3:E4:30:53:29:02:0B:DD:2D:47:02:9E:BD:85:AD:93:02:45:21"
148
+
149
+ assert !response_fingerprint_test.document.validate_document(sha256_fingerprint)
150
+ assert response_fingerprint_test.document.validate_document(sha256_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA256)
151
+ end
152
+
153
+ it "validate using SHA384" do
154
+ sha384_fingerprint = "98:FE:17:90:31:E7:68:18:8A:65:4D:DA:F5:76:E2:09:97:BE:8B:E3:7E:AA:8D:63:64:7C:0C:38:23:9A:AC:A2:EC:CE:48:A6:74:4D:E0:4C:50:80:40:B4:8D:55:14:14"
155
+
156
+ assert !response_fingerprint_test.document.validate_document(sha384_fingerprint)
157
+ assert response_fingerprint_test.document.validate_document(sha384_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA384)
158
+ end
89
159
 
90
- context "#extract_inclusive_namespaces" do
91
- should "support explicit namespace resolution for exclusive canonicalization" do
160
+ it "validate using SHA512" do
161
+ sha512_fingerprint = "5A:AE:BA:D0:BA:9D:1E:25:05:01:1E:1A:C9:E9:FF:DB:ED:FA:6E:F7:52:EB:45:49:BD:DB:06:D8:A3:7E:CC:63:3A:04:A2:DD:DF:EE:61:05:D9:58:95:2A:77:17:30:4B:EB:4A:9F:48:4A:44:1C:D0:9E:0B:1E:04:77:FD:A3:D2"
162
+
163
+ assert !response_fingerprint_test.document.validate_document(sha512_fingerprint)
164
+ assert response_fingerprint_test.document.validate_document(sha512_fingerprint, true, :fingerprint_alg => XMLSecurity::Document::SHA512)
165
+ end
166
+
167
+ end
168
+
169
+ describe "Signature Algorithms" do
170
+ it "validate using SHA1" do
171
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha1, false))
172
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
173
+ end
174
+
175
+ it "validate using SHA256" do
176
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha256, false))
177
+ assert document.validate_document("28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA")
178
+ end
179
+
180
+ it "validate using SHA384" do
181
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha384, false))
182
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
183
+ end
184
+
185
+ it "validate using SHA512" do
186
+ document = XMLSecurity::SignedDocument.new(fixture(:adfs_response_sha512, false))
187
+ assert document.validate_document("F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72")
188
+ end
189
+ end
190
+
191
+ describe "XmlSecurity::SignedDocument" do
192
+
193
+ describe "#extract_inclusive_namespaces" do
194
+ it "support explicit namespace resolution for exclusive canonicalization" do
92
195
  response = fixture(:open_saml_response, false)
93
196
  document = XMLSecurity::SignedDocument.new(response)
94
197
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
@@ -96,7 +199,7 @@ class XmlSecurityTest < Test::Unit::TestCase
96
199
  assert_equal %w[ xs ], inclusive_namespaces
97
200
  end
98
201
 
99
- should "support implicit namespace resolution for exclusive canonicalization" do
202
+ it "support implicit namespace resolution for exclusive canonicalization" do
100
203
  response = fixture(:no_signature_ns, false)
101
204
  document = XMLSecurity::SignedDocument.new(response)
102
205
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
@@ -104,57 +207,245 @@ class XmlSecurityTest < Test::Unit::TestCase
104
207
  assert_equal %w[ #default saml ds xs xsi ], inclusive_namespaces
105
208
  end
106
209
 
107
- should_eventually 'support inclusive canonicalization' do
108
-
210
+ it 'support inclusive canonicalization' do
211
+ skip('test not yet implemented')
109
212
  response = OneLogin::RubySaml::Response.new(fixture("tdnf_response.xml"))
110
213
  response.stubs(:conditions).returns(nil)
111
214
  assert !response.is_valid?
112
- settings = OneLogin::RubySaml::Settings.new
113
215
  assert !response.is_valid?
114
216
  response.settings = settings
115
217
  assert !response.is_valid?
116
218
  settings.idp_cert_fingerprint = "e6 38 9a 20 b7 4f 13 db 6a bc b1 42 6a e7 52 1d d6 56 d4 1b".upcase.gsub(" ", ":")
117
- assert response.validate!
219
+ assert response.is_valid?
118
220
  end
119
221
 
120
- should "return an empty list when inclusive namespace element is missing" do
222
+ it "return nil when inclusive namespace element is missing" do
121
223
  response = fixture(:no_signature_ns, false)
122
224
  response.slice! %r{<InclusiveNamespaces xmlns="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="#default saml ds xs xsi"/>}
123
225
 
124
226
  document = XMLSecurity::SignedDocument.new(response)
125
227
  inclusive_namespaces = document.send(:extract_inclusive_namespaces)
126
228
 
127
- assert inclusive_namespaces.empty?
229
+ assert inclusive_namespaces.nil?
128
230
  end
129
231
  end
130
232
 
131
- context "StarfieldTMS" do
132
- setup do
233
+ describe "XMLSecurity::DSIG" do
234
+ before do
235
+ settings.idp_sso_target_url = "https://idp.example.com/sso"
236
+ settings.protocol_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
237
+ settings.idp_slo_target_url = "https://idp.example.com/slo",
238
+ settings.sp_entity_id = "https://sp.example.com/saml2"
239
+ settings.assertion_consumer_service_url = "https://sp.example.com/acs"
240
+ settings.single_logout_service_url = "https://sp.example.com/sls"
241
+ end
242
+
243
+ it "sign an AuthNRequest" do
244
+ request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
245
+ request.sign_document(ruby_saml_key, ruby_saml_cert)
246
+ # verify our signature
247
+ signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
248
+ assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
249
+
250
+ request2 = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
251
+ request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
252
+ # verify our signature
253
+ signed_doc2 = XMLSecurity::SignedDocument.new(request2.to_s)
254
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
255
+ end
256
+
257
+ it "sign an AuthNRequest with certificate as text" do
258
+ request = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
259
+ request.sign_document(ruby_saml_key, ruby_saml_cert_text)
260
+
261
+ # verify our signature
262
+ signed_doc = XMLSecurity::SignedDocument.new(request.to_s)
263
+ assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
264
+ end
265
+
266
+ it "sign a LogoutRequest" do
267
+ logout_request = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
268
+ logout_request.sign_document(ruby_saml_key, ruby_saml_cert)
269
+ # verify our signature
270
+ signed_doc = XMLSecurity::SignedDocument.new(logout_request.to_s)
271
+ assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
272
+
273
+ logout_request2 = OneLogin::RubySaml::Logoutrequest.new.create_logout_request_xml_doc(settings)
274
+ logout_request2.sign_document(ruby_saml_key, ruby_saml_cert_text)
275
+ # verify our signature
276
+ signed_doc2 = XMLSecurity::SignedDocument.new(logout_request2.to_s)
277
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
278
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
279
+ end
280
+
281
+ it "sign a LogoutResponse" do
282
+ logout_response = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
283
+ logout_response.sign_document(ruby_saml_key, ruby_saml_cert)
284
+ # verify our signature
285
+ signed_doc = XMLSecurity::SignedDocument.new(logout_response.to_s)
286
+ assert signed_doc.validate_document(ruby_saml_cert_fingerprint, false)
287
+
288
+ logout_response2 = OneLogin::RubySaml::SloLogoutresponse.new.create_logout_response_xml_doc(settings, 'request_id_example', "Custom Logout Message")
289
+ logout_response2.sign_document(ruby_saml_key, ruby_saml_cert_text)
290
+ # verify our signature
291
+ signed_doc2 = XMLSecurity::SignedDocument.new(logout_response2.to_s)
292
+ signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
293
+ assert signed_doc2.validate_document(ruby_saml_cert_fingerprint, false)
294
+ end
295
+ end
296
+
297
+ describe "StarfieldTMS" do
298
+ before do
133
299
  @response = OneLogin::RubySaml::Response.new(fixture(:starfield_response))
134
- @response.settings = OneLogin::RubySaml::Settings.new(
135
- :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D"
136
- )
300
+ @response.settings = OneLogin::RubySaml::Settings.new( :idp_cert_fingerprint => "8D:BA:53:8E:A3:B6:F9:F1:69:6C:BB:D9:D8:BD:41:B3:AC:4F:9D:4D")
137
301
  end
138
302
 
139
- should "be able to validate a good response" do
303
+ it "be able to validate a good response" do
140
304
  Timecop.freeze Time.parse('2012-11-28 17:55:00 UTC') do
141
305
  assert @response.validate!
142
306
  end
143
307
  end
144
308
 
145
- should "fail before response is valid" do
309
+ it "fail before response is valid" do
146
310
  Timecop.freeze Time.parse('2012-11-20 17:55:00 UTC') do
147
311
  assert ! @response.is_valid?
148
312
  end
149
313
  end
150
314
 
151
- should "fail after response expires" do
315
+ it "fail after response expires" do
152
316
  Timecop.freeze Time.parse('2012-11-30 17:55:00 UTC') do
153
317
  assert ! @response.is_valid?
154
318
  end
155
319
  end
156
320
  end
157
321
 
158
- end
322
+ describe '#validate_document' do
323
+ describe 'with valid document' do
324
+ describe 'when response has signed message and assertion' do
325
+ let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
326
+ let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
327
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
328
+
329
+ it 'is valid' do
330
+ assert document.validate_document(fingerprint, true), 'Document should be valid'
331
+ end
332
+ end
333
+
334
+ describe 'when response has signed assertion' do
335
+ let(:document_data) { read_response('response_with_signed_assertion_3.xml') }
336
+ let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
337
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
338
+
339
+ it 'is valid' do
340
+ assert document.validate_document(fingerprint, true), 'Document should be valid'
341
+ end
342
+ end
343
+ end
344
+
345
+ describe 'signature_wrapping_attack' do
346
+ let(:document_data) { read_invalid_response("signature_wrapping_attack.xml.base64") }
347
+ let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
348
+ let(:fingerprint) { 'afe71c28ef740bc87425be13a2263d37971da1f9' }
349
+
350
+ it 'is invalid' do
351
+ assert !document.validate_document(fingerprint, true), 'Document should be invalid'
352
+ end
353
+ end
354
+
355
+ describe 'signature wrapping attack - doubled SAML response body' do
356
+ let(:document_data) { read_invalid_response("response_with_doubled_signed_assertion.xml") }
357
+ let(:document) { OneLogin::RubySaml::Response.new(document_data) }
358
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
359
+
360
+ it 'is valid, but the unsigned information is ignored in favour of the signed information' do
361
+ assert document.document.validate_document(fingerprint, true), 'Document should be valid'
362
+ assert_equal 'someone@example.org', document.name_id, 'Document should expose only signed, valid details'
363
+ end
364
+ end
365
+
366
+ describe 'signature wrapping attack - concealed SAML response body' do
367
+ let(:document_data) { read_invalid_response("response_with_concealed_signed_assertion.xml") }
368
+ let(:document) { OneLogin::RubySaml::Response.new(document_data) }
369
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
370
+
371
+ it 'is valid, but fails to retrieve information' do
372
+ assert document.document.validate_document(fingerprint, true), 'Document should be valid'
373
+ assert document.name_id.nil?, 'Document should expose only signed, valid details'
374
+ end
375
+ end
159
376
 
377
+ describe 'when response has no cert and you provide cert' do
378
+ let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
379
+ let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
380
+ let(:options) { {} }
381
+
382
+ it 'is valid' do
383
+ options[:cert] = idp_cert
384
+ assert document.document.validate_document(idp_cert, true, options), 'Document should be valid'
385
+ end
386
+
387
+ it 'is valid if cert text instead x509cert provided' do
388
+ options[:cert] = ruby_saml_cert_text
389
+ assert document.document.validate_document(idp_cert, true, options), 'Document should be valid'
390
+ end
391
+ end
392
+
393
+ describe 'when response has no cert and you dont provide cert' do
394
+ let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
395
+ let(:options) { {} }
396
+ let(:idp_cert) { nil }
397
+
398
+ it 'is invalid' do
399
+ options[:cert] = idp_cert
400
+ assert !document.document.validate_document(idp_cert, true, options), 'Document should not be valid'
401
+ end
402
+
403
+ it 'is invalid and error raised' do
404
+ options[:cert] = idp_cert
405
+ assert_raises(OneLogin::RubySaml::ValidationError) do
406
+ document.document.validate_document(idp_cert, false, options)
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ describe '#validate_document_with_cert' do
413
+ describe 'with valid document ' do
414
+ describe 'when response has cert' do
415
+ let(:document_data) { read_response('response_with_signed_message_and_assertion.xml') }
416
+ let(:document) { OneLogin::RubySaml::Response.new(document_data).document }
417
+ let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
418
+ let(:fingerprint) { '4b68c453c7d994aad9025c99d5efcf566287fe8d' }
419
+
420
+ it 'is valid' do
421
+ assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
422
+ end
423
+ end
424
+
425
+ describe 'when response has no cert and you provide cert' do
426
+ let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
427
+ let(:idp_cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
428
+
429
+ it 'is valid' do
430
+ assert document.validate_document_with_cert(idp_cert), 'Document should be valid'
431
+ end
432
+ end
433
+
434
+ describe 'when response has no cert and you dont provide cert' do
435
+ let(:document) { OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate).document }
436
+ let(:idp_cert) { nil }
437
+
438
+ it 'is invalid' do
439
+ assert !document.validate_document_with_cert(idp_cert), 'Document should not be valid'
440
+ end
441
+
442
+ it 'is invalid and error raised' do
443
+ assert_raises(OneLogin::RubySaml::ValidationError) do
444
+ document.validate_document_with_cert(idp_cert, false)
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end
450
+ end
160
451
  end