ruby-saml 0.8.9 → 0.8.14
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.
- data/Gemfile +11 -1
- data/Rakefile +0 -14
- data/lib/onelogin/ruby-saml/authrequest.rb +84 -18
- data/lib/onelogin/ruby-saml/logoutrequest.rb +93 -18
- data/lib/onelogin/ruby-saml/logoutresponse.rb +1 -24
- data/lib/onelogin/ruby-saml/response.rb +206 -11
- data/lib/onelogin/ruby-saml/setting_error.rb +6 -0
- data/lib/onelogin/ruby-saml/settings.rb +73 -12
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +158 -0
- data/lib/onelogin/ruby-saml/utils.rb +169 -0
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +2 -1
- data/lib/xml_security.rb +332 -78
- data/test/certificates/ruby-saml-2.crt +15 -0
- data/test/certificates/ruby-saml.crt +14 -0
- data/test/certificates/ruby-saml.key +15 -0
- data/test/logoutrequest_test.rb +177 -44
- data/test/logoutresponse_test.rb +23 -28
- data/test/request_test.rb +100 -37
- data/test/response_test.rb +337 -129
- data/test/responses/adfs_response_xmlns.xml +45 -0
- data/test/responses/encrypted_new_attack.xml.base64 +1 -0
- data/test/responses/invalids/multiple_signed.xml.base64 +1 -0
- data/test/responses/invalids/no_signature.xml.base64 +1 -0
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +1 -0
- data/test/responses/response_with_concealed_signed_assertion.xml +51 -0
- data/test/responses/response_with_doubled_signed_assertion.xml +49 -0
- data/test/responses/response_with_signed_assertion_3.xml +30 -0
- data/test/responses/response_with_signed_message_and_assertion.xml +34 -0
- data/test/responses/response_with_undefined_recipient.xml.base64 +1 -0
- data/test/responses/response_wrapped.xml.base64 +150 -0
- data/test/responses/valid_response.xml.base64 +1 -0
- data/test/responses/valid_response_without_x509certificate.xml.base64 +1 -0
- data/test/settings_test.rb +5 -5
- data/test/slo_logoutresponse_test.rb +226 -0
- data/test/test_helper.rb +117 -12
- data/test/utils_test.rb +10 -10
- data/test/xml_security_test.rb +354 -68
- metadata +64 -18
- checksums.yaml +0 -7
@@ -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=
|
data/test/settings_test.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
2
|
|
3
|
-
class SettingsTest < Test
|
3
|
+
class SettingsTest < Minitest::Test
|
4
4
|
|
5
|
-
|
6
|
-
|
5
|
+
describe "Settings" do
|
6
|
+
before do
|
7
7
|
@settings = OneLogin::RubySaml::Settings.new
|
8
8
|
end
|
9
|
-
|
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
|
-
|
25
|
+
it "create settings from hash" do
|
26
26
|
|
27
27
|
config = {
|
28
28
|
:assertion_consumer_service_url => "http://app.muda.no/sso",
|
@@ -0,0 +1,226 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
2
|
+
|
3
|
+
class SloLogoutresponseTest < Minitest::Test
|
4
|
+
|
5
|
+
describe "SloLogoutresponse" do
|
6
|
+
|
7
|
+
let(:settings) { OneLogin::RubySaml::Settings.new }
|
8
|
+
|
9
|
+
before do
|
10
|
+
settings.idp_slo_target_url = "http://unauth.com/logout"
|
11
|
+
settings.name_identifier_value = "f00f00"
|
12
|
+
settings.compress_request = true
|
13
|
+
settings.certificate = ruby_saml_cert_text
|
14
|
+
settings.private_key = ruby_saml_key_text
|
15
|
+
end
|
16
|
+
|
17
|
+
it "create the deflated SAMLResponse URL parameter" do
|
18
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings)
|
19
|
+
assert_match /^http:\/\/unauth\.com\/logout\?SAMLResponse=/, unauth_url
|
20
|
+
|
21
|
+
inflated = decode_saml_response_payload(unauth_url)
|
22
|
+
assert_match /^<samlp:LogoutResponse/, inflated
|
23
|
+
end
|
24
|
+
|
25
|
+
it "support additional params" do
|
26
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { :hello => nil })
|
27
|
+
assert_match /&hello=$/, unauth_url
|
28
|
+
|
29
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { :foo => "bar" })
|
30
|
+
assert_match /&foo=bar$/, unauth_url
|
31
|
+
|
32
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { :RelayState => "http://idp.example.com" })
|
33
|
+
assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url
|
34
|
+
end
|
35
|
+
|
36
|
+
it "RelayState cases" do
|
37
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { :RelayState => nil })
|
38
|
+
assert !unauth_url.include?('RelayState')
|
39
|
+
|
40
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { :RelayState => "http://example.com" })
|
41
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
42
|
+
|
43
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { 'RelayState' => nil })
|
44
|
+
assert !unauth_url.include?('RelayState')
|
45
|
+
|
46
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, nil, { 'RelayState' => "http://example.com" })
|
47
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
48
|
+
end
|
49
|
+
|
50
|
+
it "set InResponseTo to the ID from the logout request" do
|
51
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, '_c0348950-935b-0131-1060-782bcb56fcaa')
|
52
|
+
|
53
|
+
inflated = decode_saml_response_payload(unauth_url)
|
54
|
+
assert_match /InResponseTo='_c0348950-935b-0131-1060-782bcb56fcaa'/, inflated
|
55
|
+
end
|
56
|
+
|
57
|
+
it "set a custom successful logout message on the response" do
|
58
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, nil, "Custom Logout Message")
|
59
|
+
|
60
|
+
inflated = decode_saml_response_payload(unauth_url)
|
61
|
+
assert_match /<samlp:StatusMessage>Custom Logout Message<\/samlp:StatusMessage>/, inflated
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "when the settings indicate to sign (embedded) logout response" do
|
65
|
+
|
66
|
+
before do
|
67
|
+
settings.compress_response = false
|
68
|
+
settings.security[:logout_responses_signed] = true
|
69
|
+
settings.security[:embed_sign] = true
|
70
|
+
end
|
71
|
+
|
72
|
+
it "doesn't sign through create_xml_document" do
|
73
|
+
unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
|
74
|
+
inflated = unauth_res.create_xml_document(settings).to_s
|
75
|
+
|
76
|
+
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
77
|
+
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
78
|
+
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
79
|
+
end
|
80
|
+
|
81
|
+
it "sign unsigned request" do
|
82
|
+
unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
|
83
|
+
unauth_res_doc = unauth_res.create_xml_document(settings)
|
84
|
+
inflated = unauth_res_doc.to_s
|
85
|
+
|
86
|
+
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
87
|
+
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
88
|
+
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
89
|
+
|
90
|
+
inflated = unauth_res.sign_document(unauth_res_doc, settings).to_s
|
91
|
+
|
92
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
93
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
94
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
95
|
+
end
|
96
|
+
|
97
|
+
it "signs through create_logout_response_xml_doc" do
|
98
|
+
unauth_res = OneLogin::RubySaml::SloLogoutresponse.new
|
99
|
+
inflated = unauth_res.create_logout_response_xml_doc(settings).to_s
|
100
|
+
|
101
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
102
|
+
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
103
|
+
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
104
|
+
end
|
105
|
+
|
106
|
+
it "create a signed logout response" do
|
107
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message")
|
108
|
+
|
109
|
+
response_xml = Base64.decode64(params["SAMLResponse"])
|
110
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
111
|
+
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/, response_xml
|
112
|
+
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#sha1'\/>/, response_xml
|
113
|
+
end
|
114
|
+
|
115
|
+
it "create a signed logout response with 256 digest and signature methods" do
|
116
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
117
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA256
|
118
|
+
|
119
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message")
|
120
|
+
|
121
|
+
response_xml = Base64.decode64(params["SAMLResponse"])
|
122
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
123
|
+
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/, response_xml
|
124
|
+
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha256'\/>/, response_xml
|
125
|
+
end
|
126
|
+
|
127
|
+
it "create a signed logout response with 512 digest and signature method RSA_SHA384" do
|
128
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
129
|
+
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
130
|
+
|
131
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message")
|
132
|
+
|
133
|
+
response_xml = Base64.decode64(params["SAMLResponse"])
|
134
|
+
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], response_xml
|
135
|
+
assert_match /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha384'\/>/, response_xml
|
136
|
+
assert_match /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmlenc#sha512'\/>/, response_xml
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#create_params when the settings indicate to sign the logout response" do
|
141
|
+
|
142
|
+
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
143
|
+
|
144
|
+
before do
|
145
|
+
settings.compress_response = false
|
146
|
+
settings.security[:logout_responses_signed] = true
|
147
|
+
settings.security[:embed_sign] = false
|
148
|
+
end
|
149
|
+
|
150
|
+
it "create a signature parameter with RSA_SHA1 and validate it" do
|
151
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
152
|
+
|
153
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
154
|
+
assert params['SAMLResponse']
|
155
|
+
assert params[:RelayState]
|
156
|
+
assert params['Signature']
|
157
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
|
158
|
+
|
159
|
+
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
160
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
161
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
162
|
+
|
163
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
164
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
165
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "create a signature parameter with RSA_SHA256 /SHA256 and validate it" do
|
169
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
170
|
+
|
171
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
172
|
+
assert params['SAMLResponse']
|
173
|
+
assert params[:RelayState]
|
174
|
+
assert params['Signature']
|
175
|
+
|
176
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA256
|
177
|
+
|
178
|
+
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
179
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
180
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
181
|
+
|
182
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
183
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
184
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
185
|
+
end
|
186
|
+
|
187
|
+
it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
|
188
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
189
|
+
|
190
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
191
|
+
assert params['SAMLResponse']
|
192
|
+
assert params[:RelayState]
|
193
|
+
assert params['Signature']
|
194
|
+
|
195
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA384
|
196
|
+
|
197
|
+
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
198
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
199
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
200
|
+
|
201
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
202
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA384
|
203
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
204
|
+
end
|
205
|
+
|
206
|
+
it "create a signature parameter with RSA_SHA512 / SHA512 and validate it" do
|
207
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
|
208
|
+
|
209
|
+
params = OneLogin::RubySaml::SloLogoutresponse.new.create_params(settings, nil, "Custom Logout Message", :RelayState => 'http://example.com')
|
210
|
+
assert params['SAMLResponse']
|
211
|
+
assert params[:RelayState]
|
212
|
+
assert params['Signature']
|
213
|
+
|
214
|
+
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
|
215
|
+
|
216
|
+
query_string = "SAMLResponse=#{CGI.escape(params['SAMLResponse'])}"
|
217
|
+
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
218
|
+
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
219
|
+
|
220
|
+
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
221
|
+
assert_equal signature_algorithm, OpenSSL::Digest::SHA512
|
222
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
data/test/test_helper.rb
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
2
|
+
require 'minitest/autorun'
|
3
3
|
require 'shoulda'
|
4
4
|
require 'mocha/setup'
|
5
5
|
require 'timecop'
|
6
6
|
|
7
|
+
if RUBY_VERSION < '1.9'
|
8
|
+
require 'uuid'
|
9
|
+
else
|
10
|
+
require 'securerandom'
|
11
|
+
end
|
12
|
+
|
7
13
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
8
14
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
15
|
require 'ruby-saml'
|
10
16
|
|
11
17
|
ENV["ruby-saml/testing"] = "1"
|
12
18
|
|
13
|
-
class Test
|
19
|
+
class Minitest::Test
|
14
20
|
def fixture(document, base64 = true)
|
15
21
|
response = Dir.glob(File.join(File.dirname(__FILE__), "responses", "#{document}*")).first
|
16
22
|
if base64 && response =~ /\.xml$/
|
@@ -20,32 +26,48 @@ class Test::Unit::TestCase
|
|
20
26
|
end
|
21
27
|
end
|
22
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
|
+
|
23
45
|
def response_document
|
24
|
-
@response_document ||=
|
46
|
+
@response_document ||= read_response('response1.xml.base64')
|
25
47
|
end
|
26
48
|
|
27
49
|
def response_document_2
|
28
|
-
@response_document2 ||=
|
50
|
+
@response_document2 ||= read_response('response2.xml.base64')
|
29
51
|
end
|
30
52
|
|
31
53
|
def response_document_3
|
32
|
-
@response_document3 ||=
|
54
|
+
@response_document3 ||= read_response('response3.xml.base64')
|
33
55
|
end
|
34
56
|
|
35
57
|
def response_document_4
|
36
|
-
@response_document4 ||=
|
58
|
+
@response_document4 ||= read_response('response4.xml.base64')
|
37
59
|
end
|
38
60
|
|
39
61
|
def response_document_5
|
40
|
-
@response_document5 ||=
|
62
|
+
@response_document5 ||= read_response('response5.xml.base64')
|
41
63
|
end
|
42
64
|
|
43
65
|
def r1_response_document_6
|
44
|
-
@response_document6 ||=
|
66
|
+
@response_document6 ||= read_response('r1_response6.xml.base64')
|
45
67
|
end
|
46
68
|
|
47
69
|
def ampersands_response
|
48
|
-
@ampersands_resposne ||=
|
70
|
+
@ampersands_resposne ||= read_response('response_with_ampersands.xml.base64')
|
49
71
|
end
|
50
72
|
|
51
73
|
def response_document_6
|
@@ -55,6 +77,22 @@ class Test::Unit::TestCase
|
|
55
77
|
Base64.encode64(doc)
|
56
78
|
end
|
57
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
|
+
|
58
96
|
def wrapped_response_2
|
59
97
|
@wrapped_response_2 ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'wrapped_response_2.xml.base64'))
|
60
98
|
end
|
@@ -63,12 +101,24 @@ class Test::Unit::TestCase
|
|
63
101
|
@signature_fingerprint1 ||= "C5:19:85:D9:47:F1:BE:57:08:20:25:05:08:46:EB:27:F6:CA:B7:83"
|
64
102
|
end
|
65
103
|
|
104
|
+
def signature_fingerprint_valid_res
|
105
|
+
@signature_fingerprint1 ||= "4b68c453c7d994aad9025c99d5efcf566287fe8d"
|
106
|
+
end
|
107
|
+
|
66
108
|
def signature_1
|
67
|
-
@signature1 ||=
|
109
|
+
@signature1 ||= read_certificate('certificate1')
|
68
110
|
end
|
69
111
|
|
70
112
|
def r1_signature_2
|
71
|
-
@signature2 ||=
|
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')
|
72
122
|
end
|
73
123
|
|
74
124
|
def response_with_multiple_attribute_statements
|
@@ -76,7 +126,62 @@ class Test::Unit::TestCase
|
|
76
126
|
end
|
77
127
|
|
78
128
|
def response_multiple_attr_values
|
79
|
-
@response_multiple_attr_values = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
129
|
+
@response_multiple_attr_values = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
130
|
+
end
|
131
|
+
|
132
|
+
def ruby_saml_cert
|
133
|
+
@ruby_saml_cert ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text)
|
134
|
+
end
|
135
|
+
|
136
|
+
def ruby_saml_cert2
|
137
|
+
@ruby_saml_cert2 ||= OpenSSL::X509::Certificate.new(ruby_saml_cert_text2)
|
138
|
+
end
|
139
|
+
|
140
|
+
def ruby_saml_cert_fingerprint
|
141
|
+
@ruby_saml_cert_fingerprint ||= Digest::SHA1.hexdigest(ruby_saml_cert.to_der).scan(/../).join(":")
|
142
|
+
end
|
143
|
+
|
144
|
+
def ruby_saml_cert_text
|
145
|
+
read_certificate("ruby-saml.crt")
|
146
|
+
end
|
147
|
+
|
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")
|
80
154
|
end
|
81
155
|
|
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
|
82
187
|
end
|