ruby-saml 0.8.16 → 0.9
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.
- checksums.yaml +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +1 -6
- data/Gemfile +2 -12
- data/README.md +363 -35
- data/Rakefile +14 -0
- data/changelog.md +22 -9
- data/lib/onelogin/ruby-saml/attribute_service.rb +34 -0
- data/lib/onelogin/ruby-saml/attributes.rb +26 -64
- data/lib/onelogin/ruby-saml/authrequest.rb +47 -89
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +87 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +34 -93
- data/lib/onelogin/ruby-saml/logoutresponse.rb +25 -24
- data/lib/onelogin/ruby-saml/metadata.rb +46 -16
- data/lib/onelogin/ruby-saml/response.rb +62 -322
- data/lib/onelogin/ruby-saml/saml_message.rb +78 -0
- data/lib/onelogin/ruby-saml/settings.rb +54 -121
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +26 -61
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +27 -84
- data/lib/onelogin/ruby-saml/utils.rb +32 -199
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +5 -2
- data/lib/schemas/{saml20assertion_schema.xsd → saml-schema-assertion-2.0.xsd} +283 -283
- data/lib/schemas/saml-schema-authn-context-2.0.xsd +23 -0
- data/lib/schemas/saml-schema-authn-context-types-2.0.xsd +821 -0
- data/lib/schemas/saml-schema-metadata-2.0.xsd +339 -0
- data/lib/schemas/{saml20protocol_schema.xsd → saml-schema-protocol-2.0.xsd} +302 -302
- data/lib/schemas/sstc-metadata-attr.xsd +35 -0
- data/lib/schemas/sstc-saml-attribute-ext.xsd +25 -0
- data/lib/schemas/sstc-saml-metadata-algsupport-v1.0.xsd +41 -0
- data/lib/schemas/sstc-saml-metadata-ui-v1.0.xsd +89 -0
- data/lib/schemas/{xenc_schema.xsd → xenc-schema.xsd} +1 -11
- data/lib/schemas/xml.xsd +287 -0
- data/lib/schemas/{xmldsig_schema.xsd → xmldsig-core-schema.xsd} +0 -9
- data/lib/xml_security.rb +83 -235
- data/ruby-saml.gemspec +1 -0
- data/test/idp_metadata_parser_test.rb +54 -0
- data/test/logoutrequest_test.rb +68 -144
- data/test/logoutresponse_test.rb +43 -25
- data/test/metadata_test.rb +87 -0
- data/test/request_test.rb +103 -90
- data/test/response_test.rb +181 -471
- data/test/responses/idp_descriptor.xml +3 -0
- data/test/responses/logoutresponse_fixtures.rb +5 -5
- data/test/responses/response_no_cert_and_encrypted_attrs.xml +29 -0
- data/test/responses/response_with_multiple_attribute_values.xml +1 -1
- data/test/responses/slo_request.xml +4 -0
- data/test/settings_test.rb +25 -112
- data/test/slo_logoutrequest_test.rb +41 -44
- data/test/slo_logoutresponse_test.rb +87 -167
- data/test/test_helper.rb +27 -102
- data/test/xml_security_test.rb +114 -337
- metadata +34 -84
- data/lib/onelogin/ruby-saml/setting_error.rb +0 -6
- data/test/certificates/certificate.der +0 -0
- data/test/certificates/formatted_certificate +0 -14
- data/test/certificates/formatted_chained_certificate +0 -42
- data/test/certificates/formatted_private_key +0 -12
- data/test/certificates/formatted_rsa_private_key +0 -12
- data/test/certificates/invalid_certificate1 +0 -1
- data/test/certificates/invalid_certificate2 +0 -1
- data/test/certificates/invalid_certificate3 +0 -12
- data/test/certificates/invalid_chained_certificate1 +0 -1
- data/test/certificates/invalid_private_key1 +0 -1
- data/test/certificates/invalid_private_key2 +0 -1
- data/test/certificates/invalid_private_key3 +0 -10
- data/test/certificates/invalid_rsa_private_key1 +0 -1
- data/test/certificates/invalid_rsa_private_key2 +0 -1
- data/test/certificates/invalid_rsa_private_key3 +0 -10
- data/test/certificates/ruby-saml-2.crt +0 -15
- data/test/requests/logoutrequest_fixtures.rb +0 -47
- data/test/responses/encrypted_new_attack.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_assertion.xml.base64 +0 -1
- data/test/responses/invalids/invalid_issuer_message.xml.base64 +0 -1
- data/test/responses/invalids/multiple_signed.xml.base64 +0 -1
- data/test/responses/invalids/no_signature.xml.base64 +0 -1
- data/test/responses/invalids/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/invalids/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/invalids/signature_wrapping_attack.xml.base64 +0 -1
- data/test/responses/response_node_text_attack.xml.base64 +0 -1
- data/test/responses/response_with_concealed_signed_assertion.xml +0 -51
- data/test/responses/response_with_doubled_signed_assertion.xml +0 -49
- data/test/responses/response_with_multiple_attribute_statements.xml +0 -72
- data/test/responses/response_with_signed_assertion_3.xml +0 -30
- data/test/responses/response_with_signed_message_and_assertion.xml +0 -34
- data/test/responses/response_with_undefined_recipient.xml.base64 +0 -1
- data/test/responses/response_wrapped.xml.base64 +0 -150
- data/test/responses/valid_response.xml.base64 +0 -1
- data/test/responses/valid_response_without_x509certificate.xml.base64 +0 -1
- data/test/utils_test.rb +0 -231
data/ruby-saml.gemspec
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'net/https'
|
|
4
|
+
|
|
5
|
+
class IdpMetadataParserTest < Test::Unit::TestCase
|
|
6
|
+
|
|
7
|
+
class MockResponse
|
|
8
|
+
attr_accessor :body
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
context "parsing an IdP descriptor file" do
|
|
12
|
+
should "extract settings details from xml" do
|
|
13
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
|
14
|
+
|
|
15
|
+
settings = idp_metadata_parser.parse(idp_metadata)
|
|
16
|
+
|
|
17
|
+
assert_equal "https://example.hello.com/access/saml/login", settings.idp_sso_target_url
|
|
18
|
+
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
|
|
19
|
+
assert_equal "https://example.hello.com/access/saml/logout", settings.idp_slo_target_url
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
context "download and parse IdP descriptor file" do
|
|
24
|
+
setup do
|
|
25
|
+
mock_response = MockResponse.new
|
|
26
|
+
mock_response.body = idp_metadata
|
|
27
|
+
@url = "https://example.com"
|
|
28
|
+
uri = URI(@url)
|
|
29
|
+
|
|
30
|
+
@http = Net::HTTP.new(uri.host, uri.port)
|
|
31
|
+
Net::HTTP.expects(:new).returns(@http)
|
|
32
|
+
@http.expects(:request).returns(mock_response)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
should "extract settings from remote xml" do
|
|
37
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
|
38
|
+
settings = idp_metadata_parser.parse_remote(@url)
|
|
39
|
+
|
|
40
|
+
assert_equal "https://example.hello.com/access/saml/login", settings.idp_sso_target_url
|
|
41
|
+
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", settings.idp_cert_fingerprint
|
|
42
|
+
assert_equal "https://example.hello.com/access/saml/logout", settings.idp_slo_target_url
|
|
43
|
+
assert_equal OpenSSL::SSL::VERIFY_PEER, @http.verify_mode
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
should "accept self signed certificate if insturcted" do
|
|
47
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
|
48
|
+
settings = idp_metadata_parser.parse_remote(@url, false)
|
|
49
|
+
|
|
50
|
+
assert_equal OpenSSL::SSL::VERIFY_NONE, @http.verify_mode
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
data/test/logoutrequest_test.rb
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
context "Logoutrequest" do
|
|
6
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
should "create the deflated SAMLRequest URL parameter" do
|
|
9
9
|
settings.idp_slo_target_url = "http://unauth.com/logout"
|
|
10
10
|
settings.name_identifier_value = "f00f00"
|
|
11
|
-
end
|
|
12
11
|
|
|
13
|
-
it "create the deflated SAMLRequest URL parameter" do
|
|
14
12
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
15
13
|
assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLRequest=/
|
|
16
14
|
|
|
@@ -19,7 +17,8 @@ class LogoutRequestTest < Minitest::Test
|
|
|
19
17
|
assert_match /^<samlp:LogoutRequest/, inflated
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
|
|
20
|
+
should "support additional params" do
|
|
21
|
+
|
|
23
22
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :hello => nil })
|
|
24
23
|
assert unauth_url =~ /&hello=$/
|
|
25
24
|
|
|
@@ -27,8 +26,9 @@ class LogoutRequestTest < Minitest::Test
|
|
|
27
26
|
assert unauth_url =~ /&foo=bar$/
|
|
28
27
|
end
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
should "set sessionindex" do
|
|
30
|
+
settings.idp_slo_target_url = "http://example.com"
|
|
31
|
+
sessionidx = UUID.new.generate
|
|
32
32
|
settings.sessionindex = sessionidx
|
|
33
33
|
|
|
34
34
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :name_id => "there" })
|
|
@@ -38,7 +38,9 @@ class LogoutRequestTest < Minitest::Test
|
|
|
38
38
|
assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
should "set name_identifier_value" do
|
|
42
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
43
|
+
settings.idp_slo_target_url = "http://example.com"
|
|
42
44
|
settings.name_identifier_format = "transient"
|
|
43
45
|
name_identifier_value = "abc123"
|
|
44
46
|
settings.name_identifier_value = name_identifier_value
|
|
@@ -50,25 +52,33 @@ class LogoutRequestTest < Minitest::Test
|
|
|
50
52
|
assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
|
|
51
53
|
end
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
+
context "when the target url doesn't contain a query string" do
|
|
56
|
+
should "create the SAMLRequest parameter correctly" do
|
|
57
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
55
58
|
settings.idp_slo_target_url = "http://example.com"
|
|
59
|
+
settings.name_identifier_value = "f00f00"
|
|
60
|
+
|
|
56
61
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
57
62
|
assert unauth_url =~ /^http:\/\/example.com\?SAMLRequest/
|
|
58
63
|
end
|
|
59
64
|
end
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
context "when the target url contains a query string" do
|
|
67
|
+
should "create the SAMLRequest parameter correctly" do
|
|
68
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
63
69
|
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
70
|
+
settings.name_identifier_value = "f00f00"
|
|
71
|
+
|
|
64
72
|
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings)
|
|
65
73
|
assert unauth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
|
|
66
74
|
end
|
|
67
75
|
end
|
|
68
76
|
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
context "consumation of logout may need to track the transaction" do
|
|
78
|
+
should "have access to the request uuid" do
|
|
79
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
71
80
|
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
81
|
+
settings.name_identifier_value = "f00f00"
|
|
72
82
|
|
|
73
83
|
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
74
84
|
unauth_url = unauth_req.create(settings)
|
|
@@ -78,167 +88,81 @@ class LogoutRequestTest < Minitest::Test
|
|
|
78
88
|
end
|
|
79
89
|
end
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
context "when the settings indicate to sign (embebed) the logout request" do
|
|
92
|
+
should "created a signed logout request" do
|
|
93
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
94
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
95
|
+
settings.name_identifier_value = "f00f00"
|
|
85
96
|
# sign the logout request
|
|
86
97
|
settings.security[:logout_requests_signed] = true
|
|
87
98
|
settings.security[:embed_sign] = true
|
|
88
99
|
settings.certificate = ruby_saml_cert_text
|
|
89
100
|
settings.private_key = ruby_saml_key_text
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
it "doesn't sign through create_xml_document" do
|
|
93
|
-
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
94
|
-
inflated = unauth_req.create_xml_document(settings).to_s
|
|
95
|
-
|
|
96
|
-
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
97
|
-
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
98
|
-
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
it "sign unsigned request" do
|
|
102
|
-
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
103
|
-
unauth_req_doc = unauth_req.create_xml_document(settings)
|
|
104
|
-
inflated = unauth_req_doc.to_s
|
|
105
|
-
|
|
106
|
-
refute_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
107
|
-
refute_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
108
|
-
refute_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
109
|
-
|
|
110
|
-
inflated = unauth_req.sign_document(unauth_req_doc, settings).to_s
|
|
111
|
-
|
|
112
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
113
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
114
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
it "signs through create_logout_request_xml_doc" do
|
|
118
|
-
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
119
|
-
inflated = unauth_req.create_logout_request_xml_doc(settings).to_s
|
|
120
|
-
|
|
121
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
122
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
123
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
it "created a signed logout request" do
|
|
127
|
-
settings.compress_request = true
|
|
128
101
|
|
|
129
102
|
unauth_req = OneLogin::RubySaml::Logoutrequest.new
|
|
130
103
|
unauth_url = unauth_req.create(settings)
|
|
131
104
|
|
|
132
105
|
inflated = decode_saml_request_payload(unauth_url)
|
|
133
106
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], inflated
|
|
134
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2000/09/xmldsig#rsa-sha1'/>], inflated
|
|
135
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2000/09/xmldsig#sha1'/>], inflated
|
|
136
107
|
end
|
|
137
108
|
|
|
138
|
-
|
|
109
|
+
should "create a signed logout request with 256 digest and signature methods" do
|
|
110
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
139
111
|
settings.compress_request = false
|
|
140
|
-
settings.
|
|
141
|
-
settings.
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
147
|
-
assert_match %r[<ds:SignatureMethod Algorithm='http://www.w3.org/2001/04/xmldsig-more#rsa-sha256'/>], request_xml
|
|
148
|
-
assert_match %r[<ds:DigestMethod Algorithm='http://www.w3.org/2001/04/xmlenc#sha256'/>], request_xml
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
it "create a signed logout request with 512 digest and signature method RSA_SHA384" do
|
|
152
|
-
settings.compress_request = false
|
|
153
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
112
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
113
|
+
settings.name_identifier_value = "f00f00"
|
|
114
|
+
# sign the logout request
|
|
115
|
+
settings.security[:logout_requests_signed] = true
|
|
116
|
+
settings.security[:embed_sign] = true
|
|
117
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
154
118
|
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
|
119
|
+
settings.certificate = ruby_saml_cert_text
|
|
120
|
+
settings.private_key = ruby_saml_key_text
|
|
155
121
|
|
|
156
122
|
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
|
157
123
|
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
158
124
|
|
|
159
125
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
160
|
-
|
|
161
|
-
|
|
126
|
+
request_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/
|
|
127
|
+
request_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha512'\/>/
|
|
162
128
|
end
|
|
163
129
|
end
|
|
164
130
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
131
|
+
context "when the settings indicate to sign the logout request" do
|
|
132
|
+
should "create a signature parameter" do
|
|
133
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
134
|
+
settings.compress_request = false
|
|
135
|
+
settings.idp_slo_target_url = "http://example.com?field=value"
|
|
136
|
+
settings.name_identifier_value = "f00f00"
|
|
171
137
|
settings.security[:logout_requests_signed] = true
|
|
172
138
|
settings.security[:embed_sign] = false
|
|
173
|
-
settings.
|
|
139
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA1
|
|
140
|
+
settings.certificate = ruby_saml_cert_text
|
|
174
141
|
settings.private_key = ruby_saml_key_text
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
it "create a signature parameter with RSA_SHA1 / SHA1 and validate it" do
|
|
178
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
179
|
-
|
|
180
|
-
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
181
|
-
assert params['SAMLRequest']
|
|
182
|
-
assert params[:RelayState]
|
|
183
|
-
assert params['Signature']
|
|
184
|
-
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA1
|
|
185
|
-
|
|
186
|
-
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
187
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
188
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
189
|
-
|
|
190
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
191
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
|
192
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
193
|
-
end
|
|
194
142
|
|
|
195
|
-
|
|
196
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA256
|
|
197
|
-
|
|
198
|
-
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
143
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
|
199
144
|
assert params['Signature']
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
203
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
204
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
205
|
-
|
|
206
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
207
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
|
208
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
it "create a signature parameter with RSA_SHA384 / SHA384 and validate it" do
|
|
212
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA384
|
|
145
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
213
146
|
|
|
214
|
-
|
|
147
|
+
# signature_method only affects the embedeed signature
|
|
148
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
149
|
+
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings)
|
|
215
150
|
assert params['Signature']
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
query_string = "SAMLRequest=#{CGI.escape(params['SAMLRequest'])}"
|
|
219
|
-
query_string << "&RelayState=#{CGI.escape(params[:RelayState])}"
|
|
220
|
-
query_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
|
|
221
|
-
|
|
222
|
-
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
|
223
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA384
|
|
224
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
151
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
225
152
|
end
|
|
153
|
+
end
|
|
226
154
|
|
|
227
|
-
|
|
228
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA512
|
|
229
|
-
|
|
230
|
-
params = OneLogin::RubySaml::Logoutrequest.new.create_params(settings, :RelayState => 'http://example.com')
|
|
231
|
-
assert params['Signature']
|
|
232
|
-
assert_equal params['SigAlg'], XMLSecurity::Document::RSA_SHA512
|
|
155
|
+
end
|
|
233
156
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
157
|
+
def decode_saml_request_payload(unauth_url)
|
|
158
|
+
payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
|
|
159
|
+
decoded = Base64.decode64(payload)
|
|
237
160
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
161
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
162
|
+
inflated = zstream.inflate(decoded)
|
|
163
|
+
zstream.finish
|
|
164
|
+
zstream.close
|
|
165
|
+
inflated
|
|
243
166
|
end
|
|
167
|
+
|
|
244
168
|
end
|
data/test/logoutresponse_test.rb
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
-
require
|
|
2
|
+
require 'rexml/document'
|
|
3
|
+
require 'responses/logoutresponse_fixtures'
|
|
4
|
+
class RubySamlTest < Test::Unit::TestCase
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
describe "#new" do
|
|
9
|
-
it "raise an exception when response is initialized with nil" do
|
|
6
|
+
context "Logoutresponse" do
|
|
7
|
+
context "#new" do
|
|
8
|
+
should "raise an exception when response is initialized with nil" do
|
|
10
9
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Logoutresponse.new(nil) }
|
|
11
10
|
end
|
|
12
|
-
|
|
11
|
+
should "default to empty settings" do
|
|
13
12
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new( valid_response)
|
|
14
|
-
|
|
13
|
+
assert_nil logoutresponse.settings
|
|
15
14
|
end
|
|
16
|
-
|
|
15
|
+
should "accept constructor-injected settings" do
|
|
17
16
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings)
|
|
18
|
-
|
|
17
|
+
assert_not_nil logoutresponse.settings
|
|
19
18
|
end
|
|
20
|
-
|
|
19
|
+
should "accept constructor-injected options" do
|
|
21
20
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
|
|
22
21
|
assert !logoutresponse.options.empty?
|
|
23
22
|
end
|
|
24
|
-
|
|
23
|
+
should "support base64 encoded responses" do
|
|
25
24
|
expected_response = valid_response
|
|
26
25
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(Base64.encode64(expected_response), settings)
|
|
27
26
|
|
|
@@ -29,21 +28,21 @@ class LogoutResponseTest < Minitest::Test
|
|
|
29
28
|
end
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
context "#validate" do
|
|
32
|
+
should "validate the response" do
|
|
34
33
|
in_relation_to_request_id = random_id
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
|
37
36
|
|
|
38
37
|
assert logoutresponse.validate
|
|
39
38
|
|
|
40
|
-
assert_equal settings.
|
|
39
|
+
assert_equal settings.issuer, logoutresponse.issuer
|
|
41
40
|
assert_equal in_relation_to_request_id, logoutresponse.in_response_to
|
|
42
41
|
|
|
43
42
|
assert logoutresponse.success?
|
|
44
43
|
end
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
should "invalidate responses with wrong id when given option :matches_uuid" do
|
|
47
46
|
|
|
48
47
|
expected_request_id = "_some_other_expected_uuid"
|
|
49
48
|
opts = { :matches_request_id => expected_request_id}
|
|
@@ -51,10 +50,10 @@ class LogoutResponseTest < Minitest::Test
|
|
|
51
50
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response, settings, opts)
|
|
52
51
|
|
|
53
52
|
assert !logoutresponse.validate
|
|
54
|
-
|
|
53
|
+
assert_not_equal expected_request_id, logoutresponse.in_response_to
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
should "invalidate responses with wrong request status" do
|
|
58
57
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
|
59
58
|
|
|
60
59
|
assert !logoutresponse.validate
|
|
@@ -62,8 +61,8 @@ class LogoutResponseTest < Minitest::Test
|
|
|
62
61
|
end
|
|
63
62
|
end
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
context "#validate!" do
|
|
65
|
+
should "validates good responses" do
|
|
67
66
|
in_relation_to_request_id = random_id
|
|
68
67
|
|
|
69
68
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
|
|
@@ -71,7 +70,7 @@ class LogoutResponseTest < Minitest::Test
|
|
|
71
70
|
logoutresponse.validate!
|
|
72
71
|
end
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
should "raises validation error when matching for wrong request id" do
|
|
75
74
|
|
|
76
75
|
expected_request_id = "_some_other_expected_id"
|
|
77
76
|
opts = { :matches_request_id => expected_request_id}
|
|
@@ -81,13 +80,26 @@ class LogoutResponseTest < Minitest::Test
|
|
|
81
80
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
|
|
83
|
+
should "raise validation error for wrong request status" do
|
|
85
84
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, settings)
|
|
86
85
|
|
|
87
86
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
88
87
|
end
|
|
89
88
|
|
|
90
|
-
|
|
89
|
+
should "raise validation error when in bad state" do
|
|
90
|
+
# no settings
|
|
91
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response)
|
|
92
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
should "raise validation error when in lack of issuer setting" do
|
|
96
|
+
bad_settings = settings
|
|
97
|
+
bad_settings.issuer = nil
|
|
98
|
+
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(unsuccessful_response, bad_settings)
|
|
99
|
+
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
should "raise error for invalid xml" do
|
|
91
103
|
logoutresponse = OneLogin::RubySaml::Logoutresponse.new(invalid_xml_response, settings)
|
|
92
104
|
|
|
93
105
|
assert_raises(OneLogin::RubySaml::ValidationError) { logoutresponse.validate! }
|
|
@@ -95,4 +107,10 @@ class LogoutResponseTest < Minitest::Test
|
|
|
95
107
|
end
|
|
96
108
|
|
|
97
109
|
end
|
|
110
|
+
|
|
111
|
+
# logoutresponse fixtures
|
|
112
|
+
def random_id
|
|
113
|
+
"_#{UUID.new.generate}"
|
|
114
|
+
end
|
|
115
|
+
|
|
98
116
|
end
|