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
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
|
+
|
|
3
|
+
class MetadataTest < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
@settings = OneLogin::RubySaml::Settings.new
|
|
7
|
+
@settings.issuer = "https://example.com"
|
|
8
|
+
@settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
9
|
+
@settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
|
|
10
|
+
@settings.security[:authn_requests_signed] = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should "generate Service Provider Metadata with X509Certificate" do
|
|
14
|
+
@settings.security[:authn_requests_signed] = true
|
|
15
|
+
@settings.certificate = ruby_saml_cert_text
|
|
16
|
+
|
|
17
|
+
xml_text = OneLogin::RubySaml::Metadata.new.generate(@settings)
|
|
18
|
+
|
|
19
|
+
# assert xml_text can be parsed into an xml doc
|
|
20
|
+
xml_doc = REXML::Document.new(xml_text)
|
|
21
|
+
|
|
22
|
+
spsso_descriptor = REXML::XPath.first(xml_doc, "//md:SPSSODescriptor")
|
|
23
|
+
assert_equal "true", spsso_descriptor.attribute("AuthnRequestsSigned").value
|
|
24
|
+
|
|
25
|
+
cert_node = REXML::XPath.first(xml_doc, "//md:KeyDescriptor/ds:KeyInfo/ds:X509Data/ds:X509Certificate", {
|
|
26
|
+
"md" => "urn:oasis:names:tc:SAML:2.0:metadata",
|
|
27
|
+
"ds" => "http://www.w3.org/2000/09/xmldsig#"
|
|
28
|
+
})
|
|
29
|
+
cert_text = cert_node.text
|
|
30
|
+
cert = OpenSSL::X509::Certificate.new(Base64.decode64(cert_text))
|
|
31
|
+
assert_equal ruby_saml_cert.to_der, cert.to_der
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
should "should generate Service Provider Metadata" do
|
|
35
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
36
|
+
settings.issuer = "https://example.com"
|
|
37
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
38
|
+
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
|
|
39
|
+
settings.security[:authn_requests_signed] = false
|
|
40
|
+
|
|
41
|
+
xml_text = OneLogin::RubySaml::Metadata.new.generate(settings)
|
|
42
|
+
|
|
43
|
+
# assert correct xml declaration
|
|
44
|
+
start = "<?xml version='1.0' encoding='UTF-8'?>\n<md:EntityDescriptor"
|
|
45
|
+
assert xml_text[0..start.length-1] == start
|
|
46
|
+
|
|
47
|
+
# assert xml_text can be parsed into an xml doc
|
|
48
|
+
xml_doc = REXML::Document.new(xml_text)
|
|
49
|
+
|
|
50
|
+
assert_equal "https://example.com", REXML::XPath.first(xml_doc, "//md:EntityDescriptor").attribute("entityID").value
|
|
51
|
+
|
|
52
|
+
spsso_descriptor = REXML::XPath.first(xml_doc, "//md:SPSSODescriptor")
|
|
53
|
+
assert_equal "urn:oasis:names:tc:SAML:2.0:protocol", spsso_descriptor.attribute("protocolSupportEnumeration").value
|
|
54
|
+
assert_equal "false", spsso_descriptor.attribute("AuthnRequestsSigned").value
|
|
55
|
+
assert_equal "false", spsso_descriptor.attribute("WantAssertionsSigned").value
|
|
56
|
+
|
|
57
|
+
assert_equal "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress", REXML::XPath.first(xml_doc, "//md:NameIDFormat").text.strip
|
|
58
|
+
|
|
59
|
+
acs = REXML::XPath.first(xml_doc, "//md:AssertionConsumerService")
|
|
60
|
+
assert_equal "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST", acs.attribute("Binding").value
|
|
61
|
+
assert_equal "https://foo.example/saml/consume", acs.attribute("Location").value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
should "generate attribute service if configured" do
|
|
65
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
66
|
+
settings.issuer = "https://example.com"
|
|
67
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
68
|
+
settings.assertion_consumer_service_url = "https://foo.example/saml/consume"
|
|
69
|
+
settings.attribute_consuming_service.configure do
|
|
70
|
+
service_name "Test Service"
|
|
71
|
+
add_attribute(:name => "Name", :name_format => "Name Format", :friendly_name => "Friendly Name", :attribute_value => "Attribute Value")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
xml_text = OneLogin::RubySaml::Metadata.new.generate(settings)
|
|
75
|
+
xml_doc = REXML::Document.new(xml_text)
|
|
76
|
+
acs = REXML::XPath.first(xml_doc, "//md:AttributeConsumingService")
|
|
77
|
+
assert_equal "true", acs.attribute("isDefault").value
|
|
78
|
+
assert_equal "1", acs.attribute("index").value
|
|
79
|
+
assert_equal REXML::XPath.first(xml_doc, "//md:ServiceName").text.strip, "Test Service"
|
|
80
|
+
req_attr = REXML::XPath.first(xml_doc, "//md:RequestedAttribute")
|
|
81
|
+
assert_equal "Name", req_attr.attribute("Name").value
|
|
82
|
+
assert_equal "Name Format", req_attr.attribute("NameFormat").value
|
|
83
|
+
assert_equal "Friendly Name", req_attr.attribute("FriendlyName").value
|
|
84
|
+
assert_equal "Attribute Value", REXML::XPath.first(xml_doc, "//md:AttributeValue").text.strip
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
data/test/request_test.rb
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
2
|
|
|
3
|
-
class RequestTest <
|
|
3
|
+
class RequestTest < Test::Unit::TestCase
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
before do
|
|
5
|
+
context "Authrequest" do
|
|
6
|
+
should "create the deflated SAMLRequest URL parameter" do
|
|
7
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
9
8
|
settings.idp_sso_target_url = "http://example.com"
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
it "create the deflated SAMLRequest URL parameter" do
|
|
13
9
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
14
10
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
15
11
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
@@ -23,7 +19,9 @@ class RequestTest < Minitest::Test
|
|
|
23
19
|
assert_match /^<samlp:AuthnRequest/, inflated
|
|
24
20
|
end
|
|
25
21
|
|
|
26
|
-
|
|
22
|
+
should "create the deflated SAMLRequest URL parameter including the Destination" do
|
|
23
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
24
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
27
25
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
28
26
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
29
27
|
decoded = Base64.decode64(payload)
|
|
@@ -36,8 +34,10 @@ class RequestTest < Minitest::Test
|
|
|
36
34
|
assert_match /<samlp:AuthnRequest[^<]* Destination='http:\/\/example.com'/, inflated
|
|
37
35
|
end
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
should "create the SAMLRequest URL parameter without deflating" do
|
|
38
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
40
39
|
settings.compress_request = false
|
|
40
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
41
41
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
42
42
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
43
43
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
@@ -46,7 +46,9 @@ class RequestTest < Minitest::Test
|
|
|
46
46
|
assert_match /^<samlp:AuthnRequest/, decoded
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
should "create the SAMLRequest URL parameter with IsPassive" do
|
|
50
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
51
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
50
52
|
settings.passive = true
|
|
51
53
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
52
54
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
@@ -61,8 +63,10 @@ class RequestTest < Minitest::Test
|
|
|
61
63
|
assert_match /<samlp:AuthnRequest[^<]* IsPassive='true'/, inflated
|
|
62
64
|
end
|
|
63
65
|
|
|
64
|
-
|
|
65
|
-
settings
|
|
66
|
+
should "create the SAMLRequest URL parameter with ProtocolBinding" do
|
|
67
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
68
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
69
|
+
settings.protocol_binding = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
|
|
66
70
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
67
71
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
68
72
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
@@ -76,8 +80,10 @@ class RequestTest < Minitest::Test
|
|
|
76
80
|
assert_match /<samlp:AuthnRequest[^<]* ProtocolBinding='urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'/, inflated
|
|
77
81
|
end
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
settings
|
|
83
|
+
should "create the SAMLRequest URL parameter with AttributeConsumingServiceIndex" do
|
|
84
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
85
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
86
|
+
settings.attributes_index = 30
|
|
81
87
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
82
88
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
83
89
|
payload = CGI.unescape(auth_url.split("=").last)
|
|
@@ -87,42 +93,29 @@ class RequestTest < Minitest::Test
|
|
|
87
93
|
inflated = zstream.inflate(decoded)
|
|
88
94
|
zstream.finish
|
|
89
95
|
zstream.close
|
|
90
|
-
assert_match /<samlp:AuthnRequest[^<]*
|
|
96
|
+
assert_match /<samlp:AuthnRequest[^<]* AttributeConsumingServiceIndex='30'/, inflated
|
|
91
97
|
end
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
settings
|
|
99
|
+
should "create the SAMLRequest URL parameter with ForceAuthn" do
|
|
100
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
101
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
102
|
+
settings.force_authn = true
|
|
95
103
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
96
104
|
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
97
|
-
payload
|
|
98
|
-
decoded
|
|
99
|
-
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
100
|
-
inflated = zstream.inflate(decoded)
|
|
101
|
-
zstream.finish
|
|
102
|
-
zstream.close
|
|
103
|
-
|
|
104
|
-
assert_match /<samlp:NameIDPolicy[^<]* AllowCreate='true'/, inflated
|
|
105
|
-
assert_match /<samlp:NameIDPolicy[^<]* Format='urn:oasis:names:tc:SAML:2.0:nameid-format:transient'/, inflated
|
|
106
|
-
end
|
|
105
|
+
payload = CGI.unescape(auth_url.split("=").last)
|
|
106
|
+
decoded = Base64.decode64(payload)
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
settings.name_identifier_value_requested = "testuser@example.com"
|
|
110
|
-
settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
|
|
111
|
-
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
112
|
-
assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
|
|
113
|
-
payload = CGI.unescape(auth_url.split("=").last)
|
|
114
|
-
decoded = Base64.decode64(payload)
|
|
115
|
-
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
108
|
+
zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
|
|
116
109
|
inflated = zstream.inflate(decoded)
|
|
117
110
|
zstream.finish
|
|
118
111
|
zstream.close
|
|
119
|
-
|
|
120
|
-
assert inflated.include?('<saml:Subject>')
|
|
121
|
-
assert inflated.include?("<saml:NameID Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'>testuser@example.com</saml:NameID>")
|
|
122
|
-
assert inflated.include?("<saml:SubjectConfirmation Method='urn:oasis:names:tc:SAML:2.0:cm:bearer'/>")
|
|
112
|
+
assert_match /<samlp:AuthnRequest[^<]* ForceAuthn='true'/, inflated
|
|
123
113
|
end
|
|
124
114
|
|
|
125
|
-
|
|
115
|
+
should "accept extra parameters" do
|
|
116
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
117
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
118
|
+
|
|
126
119
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :hello => "there" })
|
|
127
120
|
assert auth_url =~ /&hello=there$/
|
|
128
121
|
|
|
@@ -130,15 +123,19 @@ class RequestTest < Minitest::Test
|
|
|
130
123
|
assert auth_url =~ /&hello=$/
|
|
131
124
|
end
|
|
132
125
|
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
context "when the target url doesn't contain a query string" do
|
|
127
|
+
should "create the SAMLRequest parameter correctly" do
|
|
128
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
129
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
130
|
+
|
|
135
131
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
136
132
|
assert auth_url =~ /^http:\/\/example.com\?SAMLRequest/
|
|
137
133
|
end
|
|
138
134
|
end
|
|
139
135
|
|
|
140
|
-
|
|
141
|
-
|
|
136
|
+
context "when the target url contains a query string" do
|
|
137
|
+
should "create the SAMLRequest parameter correctly" do
|
|
138
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
142
139
|
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
143
140
|
|
|
144
141
|
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings)
|
|
@@ -146,84 +143,100 @@ class RequestTest < Minitest::Test
|
|
|
146
143
|
end
|
|
147
144
|
end
|
|
148
145
|
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
context "when the settings indicate to sign (embebed) the request" do
|
|
147
|
+
should "create a signed request" do
|
|
148
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
151
149
|
settings.compress_request = false
|
|
152
150
|
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
153
151
|
settings.security[:authn_requests_signed] = true
|
|
154
152
|
settings.security[:embed_sign] = true
|
|
155
|
-
settings.certificate
|
|
153
|
+
settings.certificate = ruby_saml_cert_text
|
|
156
154
|
settings.private_key = ruby_saml_key_text
|
|
157
|
-
end
|
|
158
155
|
|
|
159
|
-
it "create a signed request" do
|
|
160
156
|
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
161
157
|
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
162
158
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
163
|
-
|
|
159
|
+
request_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/
|
|
160
|
+
request_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2000\/09\/xmldsig#rsa-sha1'\/>/
|
|
164
161
|
end
|
|
165
162
|
|
|
166
|
-
|
|
167
|
-
settings
|
|
163
|
+
should "create a signed request with 256 digest and signature methods" do
|
|
164
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
165
|
+
settings.compress_request = false
|
|
166
|
+
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
167
|
+
settings.security[:authn_requests_signed] = true
|
|
168
|
+
settings.security[:embed_sign] = true
|
|
169
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
168
170
|
settings.security[:digest_method] = XMLSecurity::Document::SHA512
|
|
171
|
+
settings.certificate = ruby_saml_cert_text
|
|
172
|
+
settings.private_key = ruby_saml_key_text
|
|
169
173
|
|
|
170
174
|
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
171
|
-
|
|
172
175
|
request_xml = Base64.decode64(params["SAMLRequest"])
|
|
173
176
|
assert_match %r[<ds:SignatureValue>([a-zA-Z0-9/+=]+)</ds:SignatureValue>], request_xml
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
request_xml =~ /<ds:SignatureMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha256'\/>/
|
|
178
|
+
request_xml =~ /<ds:DigestMethod Algorithm='http:\/\/www.w3.org\/2001\/04\/xmldsig-more#rsa-sha512'\/>/
|
|
176
179
|
end
|
|
177
180
|
end
|
|
178
181
|
|
|
179
|
-
describe "#create_params when the settings indicate to sign the request" do
|
|
180
|
-
let(:cert) { OpenSSL::X509::Certificate.new(ruby_saml_cert_text) }
|
|
181
182
|
|
|
182
|
-
|
|
183
|
+
context "when the settings indicate to sign the request" do
|
|
184
|
+
should "create a signature parameter" do
|
|
185
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
183
186
|
settings.compress_request = false
|
|
184
187
|
settings.idp_sso_target_url = "http://example.com?field=value"
|
|
188
|
+
settings.assertion_consumer_service_binding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign"
|
|
185
189
|
settings.security[:authn_requests_signed] = true
|
|
186
190
|
settings.security[:embed_sign] = false
|
|
187
|
-
settings.
|
|
191
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA1
|
|
192
|
+
settings.certificate = ruby_saml_cert_text
|
|
188
193
|
settings.private_key = ruby_saml_key_text
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
it "create a signature parameter with RSA_SHA1 and validate it" do
|
|
192
|
-
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
|
193
194
|
|
|
194
|
-
params = OneLogin::RubySaml::Authrequest.new.create_params(settings
|
|
195
|
-
assert params['SAMLRequest']
|
|
196
|
-
assert params[:RelayState]
|
|
195
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
197
196
|
assert params['Signature']
|
|
198
|
-
|
|
197
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
199
198
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
assert_equal signature_algorithm, OpenSSL::Digest::SHA1
|
|
206
|
-
|
|
207
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
|
199
|
+
# signature_method only affects the embedeed signature
|
|
200
|
+
settings.security[:signature_method] = XMLSecurity::Document::SHA256
|
|
201
|
+
params = OneLogin::RubySaml::Authrequest.new.create_params(settings)
|
|
202
|
+
assert params['Signature']
|
|
203
|
+
assert params['SigAlg'] == XMLSecurity::Document::SHA1
|
|
208
204
|
end
|
|
205
|
+
end
|
|
209
206
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
207
|
+
should "create the saml:AuthnContextClassRef element correctly" do
|
|
208
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
209
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
210
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
211
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
212
|
+
assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
|
|
213
|
+
end
|
|
216
214
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
215
|
+
should "create the saml:AuthnContextClassRef with comparison exact" do
|
|
216
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
217
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
218
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
219
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
220
|
+
assert auth_doc.to_s =~ /<samlp:RequestedAuthnContext[\S ]+Comparison='exact'/
|
|
221
|
+
assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
|
|
222
|
+
end
|
|
220
223
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
224
|
+
should "create the saml:AuthnContextClassRef with comparison minimun" do
|
|
225
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
226
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
227
|
+
settings.authn_context = 'secure/name/password/uri'
|
|
228
|
+
settings.authn_context_comparison = 'minimun'
|
|
229
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
230
|
+
assert auth_doc.to_s =~ /<samlp:RequestedAuthnContext[\S ]+Comparison='minimun'/
|
|
231
|
+
assert auth_doc.to_s =~ /<saml:AuthnContextClassRef>secure\/name\/password\/uri<\/saml:AuthnContextClassRef>/
|
|
225
232
|
end
|
|
226
233
|
|
|
234
|
+
should "create the saml:AuthnContextDeclRef element correctly" do
|
|
235
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
236
|
+
settings.idp_sso_target_url = "http://example.com"
|
|
237
|
+
settings.authn_context_decl_ref = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
|
238
|
+
auth_doc = OneLogin::RubySaml::Authrequest.new.create_authentication_xml_doc(settings)
|
|
239
|
+
assert auth_doc.to_s =~ /<saml:AuthnContextDeclRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport<\/saml:AuthnContextDeclRef>/
|
|
240
|
+
end
|
|
227
241
|
end
|
|
228
|
-
|
|
229
242
|
end
|
data/test/response_test.rb
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
|
|
2
2
|
|
|
3
|
-
class
|
|
3
|
+
class RubySamlTest < Test::Unit::TestCase
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
OneLogin::RubySaml::Response.new(nil)
|
|
9
|
-
end
|
|
10
|
-
assert_equal "Response cannot be nil", err.message
|
|
5
|
+
context "Response" do
|
|
6
|
+
should "raise an exception when response is initialized with nil" do
|
|
7
|
+
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
|
11
8
|
end
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
should "be able to parse a document which contains ampersands" do
|
|
14
11
|
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
|
15
12
|
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
|
16
13
|
|
|
@@ -21,135 +18,74 @@ class ResponseTest < Minitest::Test
|
|
|
21
18
|
response.validate!
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
should "adapt namespace" do
|
|
25
22
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
26
|
-
|
|
23
|
+
assert_not_nil response.name_id
|
|
27
24
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
|
28
|
-
|
|
25
|
+
assert_not_nil response.name_id
|
|
29
26
|
response = OneLogin::RubySaml::Response.new(response_document_3)
|
|
30
|
-
|
|
27
|
+
assert_not_nil response.name_id
|
|
31
28
|
end
|
|
32
29
|
|
|
33
|
-
|
|
30
|
+
should "default to raw input when a response is not Base64 encoded" do
|
|
34
31
|
decoded = Base64.decode64(response_document_2)
|
|
35
32
|
response = OneLogin::RubySaml::Response.new(decoded)
|
|
36
33
|
assert response.document
|
|
37
34
|
end
|
|
38
35
|
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
context "Assertion" do
|
|
37
|
+
should "only retreive an assertion with an ID that matches the signature's reference URI" do
|
|
41
38
|
response = OneLogin::RubySaml::Response.new(wrapped_response_2)
|
|
42
39
|
response.stubs(:conditions).returns(nil)
|
|
43
40
|
settings = OneLogin::RubySaml::Settings.new
|
|
44
41
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
45
42
|
response.settings = settings
|
|
46
|
-
|
|
43
|
+
assert_nil response.name_id
|
|
47
44
|
end
|
|
48
45
|
end
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
context "#validate!" do
|
|
48
|
+
should "raise when encountering a condition that prevents the document from being valid" do
|
|
52
49
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
53
|
-
|
|
54
|
-
response.validate!
|
|
55
|
-
end
|
|
56
|
-
assert_equal "No settings on response", err.message
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "raise when encountering a condition that prevents the document from being valid" do
|
|
60
|
-
response = OneLogin::RubySaml::Response.new(response_document)
|
|
61
|
-
response.settings = settings
|
|
62
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
63
|
-
response.validate!
|
|
64
|
-
end
|
|
65
|
-
assert_equal "Current time is on or after NotOnOrAfter condition", err.message
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
it "raises an exception when no cert or fingerprint provided" do
|
|
69
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
70
|
-
response.stubs(:conditions).returns(nil)
|
|
71
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
72
|
-
response.settings = settings
|
|
73
|
-
settings.idp_cert = nil
|
|
74
|
-
settings.idp_cert_fingerprint = nil
|
|
75
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
50
|
+
assert_raise(OneLogin::RubySaml::ValidationError) do
|
|
76
51
|
response.validate!
|
|
77
52
|
end
|
|
78
|
-
assert_equal "No fingerprint or certificate on settings", err.message
|
|
79
53
|
end
|
|
54
|
+
end
|
|
80
55
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
response_no_signed_elements.validate!
|
|
87
|
-
end
|
|
88
|
-
assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it "raise when multiple signatures" do
|
|
92
|
-
response_multiple_signed = OneLogin::RubySaml::Response.new(read_invalid_response("multiple_signed.xml.base64"))
|
|
93
|
-
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
94
|
-
response_multiple_signed.settings = settings
|
|
95
|
-
response_multiple_signed.stubs(:validate_structure).returns(true)
|
|
96
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
97
|
-
response_multiple_signed.validate!
|
|
98
|
-
end
|
|
99
|
-
assert_equal "Duplicated ID. SAML Response rejected", err.message
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
it "raise when fingerprint missmatch" do
|
|
103
|
-
resp_xml = Base64.decode64(response_document_valid_signed)
|
|
104
|
-
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
105
|
-
response.stubs(:conditions).returns(nil)
|
|
106
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
107
|
-
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
108
|
-
response.settings = settings
|
|
109
|
-
|
|
110
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
111
|
-
response.validate!
|
|
112
|
-
end
|
|
113
|
-
assert_equal 'Fingerprint mismatch', err.message
|
|
56
|
+
context "#validate_structure" do
|
|
57
|
+
should "raise when encountering a condition that prevents the document from being valid" do
|
|
58
|
+
response = OneLogin::RubySaml::Response.new(response_document_2)
|
|
59
|
+
response.send(:validate_structure)
|
|
60
|
+
assert response.errors.include? "Schema validation failed"
|
|
114
61
|
end
|
|
115
|
-
|
|
116
62
|
end
|
|
117
63
|
|
|
118
|
-
|
|
119
|
-
|
|
64
|
+
context "#is_valid?" do
|
|
65
|
+
should "return false when response is initialized with blank data" do
|
|
120
66
|
response = OneLogin::RubySaml::Response.new('')
|
|
121
67
|
assert !response.is_valid?
|
|
122
68
|
end
|
|
123
69
|
|
|
124
|
-
|
|
70
|
+
should "return false if settings have not been set" do
|
|
125
71
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
126
72
|
assert !response.is_valid?
|
|
127
73
|
end
|
|
128
74
|
|
|
129
|
-
|
|
130
|
-
response = OneLogin::RubySaml::Response.new(
|
|
131
|
-
response.stubs(:conditions).returns(nil)
|
|
132
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
133
|
-
response.settings = settings
|
|
134
|
-
settings.idp_cert = nil
|
|
135
|
-
settings.idp_cert_fingerprint = nil
|
|
136
|
-
assert !response.is_valid?
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
it "return true when the response is initialized with valid data" do
|
|
140
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
75
|
+
should "return true when the response is initialized with valid data" do
|
|
76
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
141
77
|
response.stubs(:conditions).returns(nil)
|
|
142
78
|
assert !response.is_valid?
|
|
143
79
|
settings = OneLogin::RubySaml::Settings.new
|
|
144
80
|
assert !response.is_valid?
|
|
145
81
|
response.settings = settings
|
|
146
82
|
assert !response.is_valid?
|
|
147
|
-
|
|
148
|
-
response.
|
|
83
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
84
|
+
assert response.is_valid?
|
|
149
85
|
end
|
|
150
86
|
|
|
151
|
-
|
|
152
|
-
response = OneLogin::RubySaml::Response.new(
|
|
87
|
+
should "should be idempotent when the response is initialized with invalid data" do
|
|
88
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
153
89
|
response.stubs(:conditions).returns(nil)
|
|
154
90
|
settings = OneLogin::RubySaml::Settings.new
|
|
155
91
|
response.settings = settings
|
|
@@ -157,58 +93,36 @@ class ResponseTest < Minitest::Test
|
|
|
157
93
|
assert !response.is_valid?
|
|
158
94
|
end
|
|
159
95
|
|
|
160
|
-
|
|
161
|
-
response = OneLogin::RubySaml::Response.new(
|
|
96
|
+
should "should be idempotent when the response is initialized with valid data" do
|
|
97
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
162
98
|
response.stubs(:conditions).returns(nil)
|
|
163
99
|
settings = OneLogin::RubySaml::Settings.new
|
|
164
100
|
response.settings = settings
|
|
165
|
-
|
|
101
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
166
102
|
assert response.is_valid?
|
|
167
103
|
assert response.is_valid?
|
|
168
104
|
end
|
|
169
105
|
|
|
170
|
-
|
|
171
|
-
response = OneLogin::RubySaml::Response.new(
|
|
172
|
-
response.stubs(:conditions).returns(nil)
|
|
173
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
174
|
-
response.settings = settings
|
|
175
|
-
settings.idp_cert = nil
|
|
176
|
-
settings.idp_cert_fingerprint = "4B:68:C4:53:C7:D9:94:AA:D9:02:5C:99:D5:EF:CF:56:62:87:FE:8D"
|
|
177
|
-
assert response.is_valid?
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it "return true when valid response using certificate" do
|
|
181
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
106
|
+
should "return true when using certificate instead of fingerprint" do
|
|
107
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
182
108
|
response.stubs(:conditions).returns(nil)
|
|
183
109
|
settings = OneLogin::RubySaml::Settings.new
|
|
184
110
|
response.settings = settings
|
|
185
|
-
settings.idp_cert =
|
|
111
|
+
settings.idp_cert = signature_1
|
|
186
112
|
assert response.is_valid?
|
|
187
113
|
end
|
|
188
114
|
|
|
189
|
-
|
|
115
|
+
should "not allow signature wrapping attack" do
|
|
190
116
|
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
191
117
|
response.stubs(:conditions).returns(nil)
|
|
192
118
|
settings = OneLogin::RubySaml::Settings.new
|
|
193
119
|
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
194
120
|
response.settings = settings
|
|
195
|
-
assert
|
|
121
|
+
assert response.is_valid?
|
|
196
122
|
assert response.name_id == "test@onelogin.com"
|
|
197
123
|
end
|
|
198
124
|
|
|
199
|
-
|
|
200
|
-
response_wrapped = OneLogin::RubySaml::Response.new(response_document_wrapped)
|
|
201
|
-
response_wrapped.stubs(:conditions).returns(nil)
|
|
202
|
-
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
203
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
204
|
-
response_wrapped.settings = settings
|
|
205
|
-
response_wrapped.settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
206
|
-
|
|
207
|
-
assert !response_wrapped.is_valid?
|
|
208
|
-
assert_nil response_wrapped.name_id
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
it "support dynamic namespace resolution on signature elements" do
|
|
125
|
+
should "support dynamic namespace resolution on signature elements" do
|
|
212
126
|
response = OneLogin::RubySaml::Response.new(fixture("no_signature_ns.xml"))
|
|
213
127
|
response.stubs(:conditions).returns(nil)
|
|
214
128
|
settings = OneLogin::RubySaml::Settings.new
|
|
@@ -218,52 +132,7 @@ class ResponseTest < Minitest::Test
|
|
|
218
132
|
assert response.validate!
|
|
219
133
|
end
|
|
220
134
|
|
|
221
|
-
|
|
222
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
|
223
|
-
response.stubs(:conditions).returns(nil)
|
|
224
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
225
|
-
response.settings = settings
|
|
226
|
-
settings.idp_cert = ruby_saml_cert
|
|
227
|
-
settings.idp_cert_fingerprint = nil
|
|
228
|
-
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
|
229
|
-
assert response.validate!
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
it "support signature elements with no KeyInfo if cert provided as text" do
|
|
233
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
|
234
|
-
response.stubs(:conditions).returns(nil)
|
|
235
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
236
|
-
response.settings = settings
|
|
237
|
-
settings.idp_cert = ruby_saml_cert_text
|
|
238
|
-
settings.idp_cert_fingerprint = nil
|
|
239
|
-
XMLSecurity::SignedDocument.any_instance.expects(:validate_signature).returns(true)
|
|
240
|
-
assert response.validate!
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
it "returns an error if the signature contains no KeyInfo, cert is not provided and soft" do
|
|
244
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
|
245
|
-
response.stubs(:conditions).returns(nil)
|
|
246
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
247
|
-
response.settings = settings
|
|
248
|
-
settings.idp_cert = nil
|
|
249
|
-
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
250
|
-
assert !response.is_valid?
|
|
251
|
-
end
|
|
252
|
-
|
|
253
|
-
it "raises an exception if the signature contains no KeyInfo, cert is not provided and no soft" do
|
|
254
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed_without_x509certificate)
|
|
255
|
-
response.stubs(:conditions).returns(nil)
|
|
256
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
257
|
-
response.settings = settings
|
|
258
|
-
settings.idp_cert = nil
|
|
259
|
-
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
260
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
261
|
-
response.validate!
|
|
262
|
-
end
|
|
263
|
-
assert_equal "Certificate element missing in response (ds:X509Certificate) and not cert provided at settings", err.message
|
|
264
|
-
end
|
|
265
|
-
|
|
266
|
-
it "validate ADFS assertions" do
|
|
135
|
+
should "validate ADFS assertions" do
|
|
267
136
|
response = OneLogin::RubySaml::Response.new(fixture(:adfs_response_sha256))
|
|
268
137
|
response.stubs(:conditions).returns(nil)
|
|
269
138
|
settings = OneLogin::RubySaml::Settings.new
|
|
@@ -272,7 +141,7 @@ class ResponseTest < Minitest::Test
|
|
|
272
141
|
assert response.validate!
|
|
273
142
|
end
|
|
274
143
|
|
|
275
|
-
|
|
144
|
+
should "validate the digest" do
|
|
276
145
|
response = OneLogin::RubySaml::Response.new(r1_response_document_6)
|
|
277
146
|
response.stubs(:conditions).returns(nil)
|
|
278
147
|
settings = OneLogin::RubySaml::Settings.new
|
|
@@ -281,75 +150,19 @@ class ResponseTest < Minitest::Test
|
|
|
281
150
|
assert response.validate!
|
|
282
151
|
end
|
|
283
152
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
response = OneLogin::RubySaml::Response.new(
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
end
|
|
291
|
-
|
|
292
|
-
describe '#validate_audience' do
|
|
293
|
-
it "return true when sp_entity_id not set or empty" do
|
|
294
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
295
|
-
response.stubs(:conditions).returns(nil)
|
|
296
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
297
|
-
response.settings = settings
|
|
298
|
-
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
|
299
|
-
assert response.is_valid?
|
|
300
|
-
settings.sp_entity_id = ''
|
|
301
|
-
assert response.is_valid?
|
|
302
|
-
end
|
|
303
|
-
|
|
304
|
-
it "return false when sp_entity_id set to incorrectly" do
|
|
305
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
306
|
-
response.stubs(:conditions).returns(nil)
|
|
307
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
308
|
-
response.settings = settings
|
|
309
|
-
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
|
310
|
-
settings.sp_entity_id = 'wrong_audience'
|
|
311
|
-
assert !response.is_valid?
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
it "return true when sp_entity_id set to correctly" do
|
|
315
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
316
|
-
response.stubs(:conditions).returns(nil)
|
|
317
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
318
|
-
response.settings = settings
|
|
319
|
-
settings.idp_cert_fingerprint = signature_fingerprint_valid_res
|
|
320
|
-
settings.sp_entity_id = 'https://someone.example.com/audience'
|
|
321
|
-
assert response.is_valid?
|
|
322
|
-
end
|
|
323
|
-
end
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
describe "#validate_issuer" do
|
|
327
|
-
it "return true when the issuer of the Message/Assertion matches the IdP entityId" do
|
|
328
|
-
response = OneLogin::RubySaml::Response.new(response_document_valid_signed)
|
|
329
|
-
response.settings = settings
|
|
330
|
-
assert response.send(:validate_issuer)
|
|
331
|
-
|
|
332
|
-
response.settings.idp_entity_id = 'https://app.onelogin.com/saml2'
|
|
333
|
-
assert response.send(:validate_issuer)
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
it "return false when the issuer of the Message does not match the IdP entityId" do
|
|
337
|
-
response = OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_message.xml.base64"))
|
|
338
|
-
response.settings = settings
|
|
339
|
-
response.settings.idp_entity_id = 'http://idp.example.com/'
|
|
340
|
-
assert !response.send(:validate_issuer)
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
it "return false when the issuer of the Assertion does not match the IdP entityId" do
|
|
344
|
-
response = OneLogin::RubySaml::Response.new(read_invalid_response("invalid_issuer_assertion.xml.base64"))
|
|
153
|
+
should "validate SAML 2.0 XML structure" do
|
|
154
|
+
resp_xml = Base64.decode64(response_document_4).gsub(/emailAddress/,'test')
|
|
155
|
+
response = OneLogin::RubySaml::Response.new(Base64.encode64(resp_xml))
|
|
156
|
+
response.stubs(:conditions).returns(nil)
|
|
157
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
158
|
+
settings.idp_cert_fingerprint = signature_fingerprint_1
|
|
345
159
|
response.settings = settings
|
|
346
|
-
|
|
347
|
-
assert !response.send(:validate_issuer)
|
|
160
|
+
assert_raises(OneLogin::RubySaml::ValidationError, 'Digest mismatch'){ response.validate! }
|
|
348
161
|
end
|
|
349
162
|
end
|
|
350
163
|
|
|
351
|
-
|
|
352
|
-
|
|
164
|
+
context "#name_id" do
|
|
165
|
+
should "extract the value of the name id element" do
|
|
353
166
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
354
167
|
assert_equal "support@onelogin.com", response.name_id
|
|
355
168
|
|
|
@@ -357,19 +170,19 @@ class ResponseTest < Minitest::Test
|
|
|
357
170
|
assert_equal "someone@example.com", response.name_id
|
|
358
171
|
end
|
|
359
172
|
|
|
360
|
-
|
|
173
|
+
should "be extractable from an OpenSAML response" do
|
|
361
174
|
response = OneLogin::RubySaml::Response.new(fixture(:open_saml))
|
|
362
175
|
assert_equal "someone@example.org", response.name_id
|
|
363
176
|
end
|
|
364
177
|
|
|
365
|
-
|
|
178
|
+
should "be extractable from a Simple SAML PHP response" do
|
|
366
179
|
response = OneLogin::RubySaml::Response.new(fixture(:simple_saml_php))
|
|
367
180
|
assert_equal "someone@example.com", response.name_id
|
|
368
181
|
end
|
|
369
182
|
end
|
|
370
183
|
|
|
371
|
-
|
|
372
|
-
|
|
184
|
+
context "#check_conditions" do
|
|
185
|
+
should "check time conditions" do
|
|
373
186
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
374
187
|
assert !response.send(:validate_conditions, true)
|
|
375
188
|
response = OneLogin::RubySaml::Response.new(response_document_6)
|
|
@@ -380,290 +193,187 @@ class ResponseTest < Minitest::Test
|
|
|
380
193
|
assert response.send(:validate_conditions, true)
|
|
381
194
|
end
|
|
382
195
|
|
|
383
|
-
|
|
196
|
+
should "optionally allow for clock drift" do
|
|
384
197
|
# The NotBefore condition in the document is 2011-06-14T18:21:01.516Z
|
|
385
|
-
|
|
386
|
-
Time.stubs(:now).returns(expected_time)
|
|
198
|
+
Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z"))
|
|
387
199
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.515)
|
|
388
200
|
assert !response.send(:validate_conditions, true)
|
|
389
201
|
|
|
390
|
-
|
|
391
|
-
Time.stubs(:now).returns(expected_time)
|
|
202
|
+
Time.stubs(:now).returns(Time.parse("2011-06-14T18:21:01Z"))
|
|
392
203
|
response = OneLogin::RubySaml::Response.new(response_document_5, :allowed_clock_drift => 0.516)
|
|
393
204
|
assert response.send(:validate_conditions, true)
|
|
394
205
|
end
|
|
395
206
|
end
|
|
396
207
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
response = OneLogin::RubySaml::Response.new(
|
|
400
|
-
|
|
401
|
-
response.settings = settings
|
|
402
|
-
settings.idp_cert = nil
|
|
403
|
-
settings.idp_cert_fingerprint = nil
|
|
404
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
405
|
-
response.send(:validate_signature, false)
|
|
406
|
-
end
|
|
407
|
-
assert_equal "No fingerprint or certificate on settings", err.message
|
|
208
|
+
context "#attributes" do
|
|
209
|
+
should "extract the first attribute in a hash accessed via its symbol" do
|
|
210
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
|
211
|
+
assert_equal "demo", response.attributes[:uid]
|
|
408
212
|
end
|
|
409
213
|
|
|
410
|
-
|
|
411
|
-
response = OneLogin::RubySaml::Response.new(
|
|
412
|
-
|
|
413
|
-
response.settings = settings
|
|
414
|
-
settings.idp_cert = ruby_saml_cert2
|
|
415
|
-
settings.idp_cert_fingerprint = nil
|
|
416
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
417
|
-
response.send(:validate_signature, false)
|
|
418
|
-
end
|
|
419
|
-
assert_equal "Fingerprint mismatch", err.message
|
|
214
|
+
should "extract the first attribute in a hash accessed via its name" do
|
|
215
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
|
216
|
+
assert_equal "demo", response.attributes["uid"]
|
|
420
217
|
end
|
|
421
218
|
|
|
422
|
-
|
|
423
|
-
response = OneLogin::RubySaml::Response.new(
|
|
424
|
-
|
|
425
|
-
response.
|
|
426
|
-
settings.idp_cert = nil
|
|
427
|
-
settings.idp_cert_fingerprint = "28:74:9B:E8:1F:E8:10:9C:A8:7C:A9:C3:E3:C5:01:6C:92:1C:B4:BA"
|
|
428
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
429
|
-
response.send(:validate_signature, false)
|
|
430
|
-
end
|
|
431
|
-
assert_equal "Fingerprint mismatch", err.message
|
|
219
|
+
should "extract all attributes" do
|
|
220
|
+
response = OneLogin::RubySaml::Response.new(response_document)
|
|
221
|
+
assert_equal "demo", response.attributes[:uid]
|
|
222
|
+
assert_equal "value", response.attributes[:another_value]
|
|
432
223
|
end
|
|
433
224
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
response_no_signed_elements.settings = settings
|
|
438
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
439
|
-
response_no_signed_elements.validate!
|
|
440
|
-
end
|
|
441
|
-
assert_equal "Found an unexpected number of Signature Element. SAML Response rejected", err.message
|
|
225
|
+
should "work for implicit namespaces" do
|
|
226
|
+
response = OneLogin::RubySaml::Response.new(response_document_3)
|
|
227
|
+
assert_equal "someone@example.com", response.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"]
|
|
442
228
|
end
|
|
443
|
-
end
|
|
444
229
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
230
|
+
should "not raise errors about nil/empty attributes for EncryptedAttributes" do
|
|
231
|
+
response = OneLogin::RubySaml::Response.new(response_document_7)
|
|
232
|
+
assert_equal 'Demo', response.attributes["first_name"]
|
|
448
233
|
end
|
|
449
234
|
|
|
450
|
-
|
|
451
|
-
|
|
235
|
+
should "not raise on responses without attributes" do
|
|
236
|
+
response = OneLogin::RubySaml::Response.new(response_document_4)
|
|
237
|
+
assert_equal OneLogin::RubySaml::Attributes.new, response.attributes
|
|
452
238
|
end
|
|
453
239
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
240
|
+
context "#multiple values" do
|
|
241
|
+
should "extract single value as string" do
|
|
242
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
243
|
+
assert_equal "demo", response.attributes[:uid]
|
|
244
|
+
end
|
|
457
245
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
246
|
+
should "extract single value as string in compatibility mode off" do
|
|
247
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
248
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
249
|
+
assert_equal ["demo"], response.attributes[:uid]
|
|
250
|
+
# classes are not reloaded between tests so restore default
|
|
251
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
252
|
+
end
|
|
462
253
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
254
|
+
should "extract first of multiple values as string for b/w compatibility" do
|
|
255
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
256
|
+
assert_equal 'value1', response.attributes[:another_value]
|
|
257
|
+
end
|
|
467
258
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
259
|
+
should "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
|
260
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
261
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
262
|
+
assert_equal ['value1', 'value2'], response.attributes[:another_value]
|
|
263
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
264
|
+
end
|
|
472
265
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
266
|
+
should "return array with all attributes when asked in XML order" do
|
|
267
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
268
|
+
assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
|
|
269
|
+
end
|
|
477
270
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
271
|
+
should "return array with all attributes when asked in XML order in compatibility mode off" do
|
|
272
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
273
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
274
|
+
assert_equal ['value1', 'value2'], response.attributes.multi(:another_value)
|
|
275
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
276
|
+
end
|
|
481
277
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
278
|
+
should "return first of multiple values when multiple Attribute tags in XML" do
|
|
279
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
280
|
+
assert_equal 'role1', response.attributes[:role]
|
|
281
|
+
end
|
|
485
282
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
283
|
+
should "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
|
284
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
285
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
286
|
+
assert_equal ['role1', 'role2', 'role3'], response.attributes[:role]
|
|
287
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
should "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
|
291
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
292
|
+
assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
should "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
|
296
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
297
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
298
|
+
assert_equal ['role1', 'role2', 'role3'], response.attributes.multi(:role)
|
|
299
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
should "return nil value correctly" do
|
|
303
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
304
|
+
assert_nil response.attributes[:attribute_with_nil_value]
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
should "return nil value correctly when not in compatibility mode off" do
|
|
308
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
309
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
310
|
+
assert_equal [nil], response.attributes[:attribute_with_nil_value]
|
|
311
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
should "return multiple values including nil and empty string" do
|
|
315
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
316
|
+
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
should "return multiple values from [] when not in compatibility mode off" do
|
|
320
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
321
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
322
|
+
assert_equal ["", "valuePresent", nil, nil], response.attributes[:attribute_with_nils_and_empty_strings]
|
|
323
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
should "check what happens when trying retrieve attribute that does not exists" do
|
|
327
|
+
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
328
|
+
assert_equal nil, response.attributes[:attribute_not_exists]
|
|
329
|
+
assert_equal nil, response.attributes.single(:attribute_not_exists)
|
|
330
|
+
assert_equal nil, response.attributes.multi(:attribute_not_exists)
|
|
331
|
+
|
|
332
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
333
|
+
assert_equal nil, response.attributes[:attribute_not_exists]
|
|
334
|
+
assert_equal nil, response.attributes.single(:attribute_not_exists)
|
|
335
|
+
assert_equal nil, response.attributes.multi(:attribute_not_exists)
|
|
336
|
+
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
337
|
+
end
|
|
490
338
|
|
|
491
|
-
it "be manipulable by hash methods such as #shift and actually remove the value" do
|
|
492
|
-
removed_value = @response.attributes.shift
|
|
493
|
-
assert_nil @response.attributes[removed_value[0]]
|
|
494
339
|
end
|
|
495
340
|
end
|
|
496
341
|
|
|
497
|
-
|
|
498
|
-
|
|
342
|
+
context "#session_expires_at" do
|
|
343
|
+
should "extract the value of the SessionNotOnOrAfter attribute" do
|
|
499
344
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
500
345
|
assert response.session_expires_at.is_a?(Time)
|
|
501
346
|
|
|
502
347
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
|
503
|
-
|
|
348
|
+
assert_nil response.session_expires_at
|
|
504
349
|
end
|
|
505
350
|
end
|
|
506
351
|
|
|
507
|
-
|
|
508
|
-
|
|
352
|
+
context "#issuer" do
|
|
353
|
+
should "return the issuer inside the response assertion" do
|
|
509
354
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
510
355
|
assert_equal "https://app.onelogin.com/saml/metadata/13590", response.issuer
|
|
511
356
|
end
|
|
512
357
|
|
|
513
|
-
|
|
358
|
+
should "return the issuer inside the response" do
|
|
514
359
|
response = OneLogin::RubySaml::Response.new(response_document_2)
|
|
515
360
|
assert_equal "wibble", response.issuer
|
|
516
361
|
end
|
|
517
362
|
end
|
|
518
363
|
|
|
519
|
-
|
|
520
|
-
|
|
364
|
+
context "#success" do
|
|
365
|
+
should "find a status code that says success" do
|
|
521
366
|
response = OneLogin::RubySaml::Response.new(response_document)
|
|
522
|
-
|
|
367
|
+
response.success?
|
|
523
368
|
end
|
|
524
369
|
end
|
|
525
370
|
|
|
526
|
-
|
|
527
|
-
|
|
371
|
+
context '#xpath_first_from_signed_assertion' do
|
|
372
|
+
should 'not allow arbitrary code execution' do
|
|
528
373
|
malicious_response_document = fixture('response_eval', false)
|
|
529
374
|
response = OneLogin::RubySaml::Response.new(malicious_response_document)
|
|
530
375
|
response.send(:xpath_first_from_signed_assertion)
|
|
531
|
-
|
|
532
|
-
end
|
|
533
|
-
end
|
|
534
|
-
|
|
535
|
-
describe "#multiple values" do
|
|
536
|
-
it "extract single value as string" do
|
|
537
|
-
assert_equal "demo", response_multiple_attr_values.attributes[:uid]
|
|
538
|
-
end
|
|
539
|
-
|
|
540
|
-
it "extract single value as string in compatibility mode off" do
|
|
541
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
542
|
-
assert_equal ["demo"], response_multiple_attr_values.attributes[:uid]
|
|
543
|
-
# classes are not reloaded between tests so restore default
|
|
544
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
545
|
-
end
|
|
546
|
-
|
|
547
|
-
it "extract first of multiple values as string for b/w compatibility" do
|
|
548
|
-
assert_equal 'value1', response_multiple_attr_values.attributes[:another_value]
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
it "extract first of multiple values as string for b/w compatibility in compatibility mode off" do
|
|
552
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
553
|
-
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes[:another_value]
|
|
554
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
it "return array with all attributes when asked in XML order" do
|
|
558
|
-
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
559
|
-
end
|
|
560
|
-
|
|
561
|
-
it "return array with all attributes when asked in XML order in compatibility mode off" do
|
|
562
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
563
|
-
assert_equal ['value1', 'value2'], response_multiple_attr_values.attributes.multi(:another_value)
|
|
564
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
it "return first of multiple values when multiple Attribute tags in XML" do
|
|
568
|
-
assert_equal 'role1', response_multiple_attr_values.attributes[:role]
|
|
569
|
-
end
|
|
570
|
-
|
|
571
|
-
it "return first of multiple values when multiple Attribute tags in XML in compatibility mode off" do
|
|
572
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
573
|
-
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes[:role]
|
|
574
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
575
|
-
end
|
|
576
|
-
|
|
577
|
-
it "return all of multiple values in reverse order when multiple Attribute tags in XML" do
|
|
578
|
-
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
579
|
-
end
|
|
580
|
-
|
|
581
|
-
it "return all of multiple values in reverse order when multiple Attribute tags in XML in compatibility mode off" do
|
|
582
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
583
|
-
assert_equal ['role1', 'role2', 'role3'], response_multiple_attr_values.attributes.multi(:role)
|
|
584
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
585
|
-
end
|
|
586
|
-
|
|
587
|
-
it "return all of multiple values when multiple Attribute tags in multiple AttributeStatement tags" do
|
|
588
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
589
|
-
assert_equal ['role1', 'role2', 'role3'], response_with_multiple_attribute_statements.attributes.multi(:role)
|
|
590
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
591
|
-
end
|
|
592
|
-
|
|
593
|
-
it "return nil value correctly" do
|
|
594
|
-
assert_nil response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
595
|
-
end
|
|
596
|
-
|
|
597
|
-
it "return nil value correctly when not in compatibility mode off" do
|
|
598
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
599
|
-
assert [nil] == response_multiple_attr_values.attributes[:attribute_with_nil_value]
|
|
600
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
601
|
-
end
|
|
602
|
-
|
|
603
|
-
it "return multiple values including nil and empty string" do
|
|
604
|
-
response = OneLogin::RubySaml::Response.new(fixture(:response_with_multiple_attribute_values))
|
|
605
|
-
assert_equal ["", "valuePresent", nil, nil], response.attributes.multi(:attribute_with_nils_and_empty_strings)
|
|
606
|
-
end
|
|
607
|
-
|
|
608
|
-
it "return multiple values from [] when not in compatibility mode off" do
|
|
609
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
610
|
-
assert_equal ["", "valuePresent", nil, nil], response_multiple_attr_values.attributes[:attribute_with_nils_and_empty_strings]
|
|
611
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
612
|
-
end
|
|
613
|
-
|
|
614
|
-
it "check what happens when trying retrieve attribute that does not exists" do
|
|
615
|
-
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
616
|
-
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
617
|
-
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
618
|
-
|
|
619
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = false
|
|
620
|
-
assert_nil response_multiple_attr_values.attributes[:attribute_not_exists]
|
|
621
|
-
assert_nil response_multiple_attr_values.attributes.single(:attribute_not_exists)
|
|
622
|
-
assert_nil response_multiple_attr_values.attributes.multi(:attribute_not_exists)
|
|
623
|
-
OneLogin::RubySaml::Attributes.single_value_compatibility = true
|
|
624
|
-
end
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
describe "signature wrapping attack with encrypted assertion" do
|
|
628
|
-
it "should not be valid" do
|
|
629
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
630
|
-
settings.private_key = valid_key
|
|
631
|
-
signature_wrapping_attack = read_response("encrypted_new_attack.xml.base64")
|
|
632
|
-
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
|
633
|
-
response_wrapped.stubs(:conditions).returns(nil)
|
|
634
|
-
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
635
|
-
settings.idp_cert_fingerprint = "385b1eec71143f00db6af936e2ea12a28771d72c"
|
|
636
|
-
assert !response_wrapped.is_valid?
|
|
637
|
-
err = assert_raises(OneLogin::RubySaml::ValidationError) do
|
|
638
|
-
response_wrapped.validate!
|
|
639
|
-
end
|
|
640
|
-
assert_equal "Found an invalid Signed Element. SAML Response rejected", err.message
|
|
641
|
-
end
|
|
642
|
-
end
|
|
643
|
-
|
|
644
|
-
describe "signature wrapping attack - concealed SAML response body" do
|
|
645
|
-
it "should not be valid" do
|
|
646
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
647
|
-
signature_wrapping_attack = read_response("response_with_concealed_signed_assertion.xml")
|
|
648
|
-
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
|
649
|
-
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
|
650
|
-
response_wrapped.stubs(:conditions).returns(nil)
|
|
651
|
-
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
652
|
-
response_wrapped.stubs(:validate_structure).returns(true)
|
|
653
|
-
assert !response_wrapped.is_valid?
|
|
654
|
-
assert !response_wrapped.validate!
|
|
655
|
-
end
|
|
656
|
-
end
|
|
657
|
-
|
|
658
|
-
describe "signature wrapping attack - doubled signed assertion SAML response" do
|
|
659
|
-
it "should not be valid" do
|
|
660
|
-
settings = OneLogin::RubySaml::Settings.new
|
|
661
|
-
signature_wrapping_attack = read_response("response_with_doubled_signed_assertion.xml")
|
|
662
|
-
response_wrapped = OneLogin::RubySaml::Response.new(signature_wrapping_attack, :settings => settings)
|
|
663
|
-
settings.idp_cert_fingerprint = '4b68c453c7d994aad9025c99d5efcf566287fe8d'
|
|
664
|
-
response_wrapped.stubs(:conditions).returns(nil)
|
|
665
|
-
response_wrapped.stubs(:validate_subject_confirmation).returns(true)
|
|
666
|
-
assert !response_wrapped.is_valid?
|
|
376
|
+
assert_equal($evalled, nil)
|
|
667
377
|
end
|
|
668
378
|
end
|
|
669
379
|
end
|