ruby-saml 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of ruby-saml might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +9 -1
- data/changelog.md +9 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +7 -1
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +2 -0
- data/lib/onelogin/ruby-saml/logoutrequest.rb +5 -0
- data/lib/onelogin/ruby-saml/response.rb +16 -1
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +6 -1
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/test/idp_metadata_parser_test.rb +11 -0
- data/test/logoutrequest_test.rb +14 -0
- data/test/metadata/idp_descriptor_4.xml +72 -0
- data/test/request_test.rb +29 -2
- data/test/response_test.rb +56 -5
- data/test/responses/response_node_text_attack2.xml.base64 +1 -0
- data/test/responses/response_node_text_attack3.xml.base64 +1 -0
- data/test/slo_logoutresponse_test.rb +14 -0
- data/test/test_helper.rb +8 -4
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e2594fe194305df6e9fae1f6d2ec5f642155fec
|
4
|
+
data.tar.gz: c0493eb47ce432fff7575241117d69860a5d1a36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 941cba217d85b8b05b51d22d39f2136ed62ac1a00ce5f52166fbb76a0e82681d0d4ea565d6990476eaaf563af9b3be676df6adb47dce1134235928c21614f149
|
7
|
+
data.tar.gz: 972bb6d3d9f71b5278035928a2188de402ff730e9e83d2a9be7e49a1075d08ca76e8c866263415170bdd439071ff956f0c283edb531410a34dd01de8e23d0aaf
|
data/README.md
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# Ruby SAML [](http://travis-ci.org/onelogin/ruby-saml) [](https://coveralls.io/r/onelogin/ruby-saml?branch=master%0A) [](http://badge.fury.io/rb/ruby-saml)
|
2
2
|
|
3
|
+
## Updating from 1.7.X to 1.8.0
|
4
|
+
On Version `1.8.0`, creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState param will not generate a URL with an empty RelayState parameter anymore. It also changes the invalid audience error message.
|
5
|
+
|
3
6
|
## Updating from 1.6.0 to 1.7.0
|
4
7
|
|
5
8
|
Version `1.7.0` is a recommended update for all Ruby SAML users as it includes a fix for the [CVE-2017-11428](https://www.cvedetails.com/cve/CVE-2017-11428/) vulnerability.
|
@@ -41,6 +44,10 @@ value.
|
|
41
44
|
If you want to skip that validation, add the :skip_recipient_check option to the
|
42
45
|
initialize method of the Response object.
|
43
46
|
|
47
|
+
Parsing metadata that contains more than one certificate will propagate the
|
48
|
+
idp_cert_multi property rather than idp_cert. See [signature validation
|
49
|
+
section](#signature-validation) for details.
|
50
|
+
|
44
51
|
## Updating from 1.3.x to 1.4.X
|
45
52
|
|
46
53
|
Version `1.4.0` is a recommended update for all Ruby SAML users as it includes security improvements.
|
@@ -235,9 +242,10 @@ def saml_settings
|
|
235
242
|
end
|
236
243
|
```
|
237
244
|
|
238
|
-
Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`. For example, you can skip the `Conditions`, `Recipient`, or the `SubjectConfirmation` validations by initializing the response with different options:
|
245
|
+
Some assertion validations can be skipped by passing parameters to `OneLogin::RubySaml::Response.new()`. For example, you can skip the `AuthnStatement`, `Conditions`, `Recipient`, or the `SubjectConfirmation` validations by initializing the response with different options:
|
239
246
|
|
240
247
|
```ruby
|
248
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_authnstatement: true}) # skips AuthnStatement
|
241
249
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_conditions: true}) # skips conditions
|
242
250
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_subject_confirmation: true}) # skips subject confirmation
|
243
251
|
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse], {skip_recipient_check: true}) # doens't skip subject confirmation, but skips the recipient check which is a sub check of the subject_confirmation check
|
data/changelog.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# RubySaml Changelog
|
2
2
|
|
3
|
+
### 1.8.0 (April 23, 2018)
|
4
|
+
* [#437](https://github.com/onelogin/ruby-saml/issues/437) Creating AuthRequests/LogoutRequests/LogoutResponses with nil RelayState should not send empty RelayState URL param
|
5
|
+
* [#454](https://github.com/onelogin/ruby-saml/pull/454) Added Response available options
|
6
|
+
* [#453](https://github.com/onelogin/ruby-saml/pull/453) Raise a more descriptive exception if idp_sso_target_url is missing
|
7
|
+
* [#452](https://github.com/onelogin/ruby-saml/pull/452) Fix behavior of skip_conditions flag on Response
|
8
|
+
* [#449](https://github.com/onelogin/ruby-saml/pull/449) Add ability to skip authnstatement validation
|
9
|
+
* Clear cached values to be able to use IdpMetadataParser more than once
|
10
|
+
* Updated invalid audience error message
|
11
|
+
|
3
12
|
### 1.7.2 (Feb 28, 2018)
|
4
13
|
* [#446](https://github.com/onelogin/ruby-saml/pull/446) Normalize text returned by OneLogin::RubySaml::Utils.element_text
|
5
14
|
|
@@ -36,6 +36,7 @@ module OneLogin
|
|
36
36
|
params.each_pair do |key, value|
|
37
37
|
request_params << "&#{key.to_s}=#{CGI.escape(value.to_s)}"
|
38
38
|
end
|
39
|
+
raise "Invalid settings, idp_sso_target_url is not set!" if settings.idp_sso_target_url.nil?
|
39
40
|
@login_url = settings.idp_sso_target_url + request_params
|
40
41
|
end
|
41
42
|
|
@@ -50,6 +51,11 @@ module OneLogin
|
|
50
51
|
# conflicts so this line will solve them.
|
51
52
|
relay_state = params[:RelayState] || params['RelayState']
|
52
53
|
|
54
|
+
if relay_state.nil?
|
55
|
+
params.delete(:RelayState)
|
56
|
+
params.delete('RelayState')
|
57
|
+
end
|
58
|
+
|
53
59
|
request_doc = create_authentication_xml_doc(settings)
|
54
60
|
request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
|
55
61
|
|
@@ -157,7 +163,7 @@ module OneLogin
|
|
157
163
|
|
158
164
|
def sign_document(document, settings)
|
159
165
|
# embed signature
|
160
|
-
if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
|
166
|
+
if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
|
161
167
|
private_key = settings.get_sp_key
|
162
168
|
cert = settings.get_sp_cert
|
163
169
|
document.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
|
@@ -101,6 +101,8 @@ module OneLogin
|
|
101
101
|
@document = REXML::Document.new(idp_metadata)
|
102
102
|
@options = options
|
103
103
|
@entity_descriptor = nil
|
104
|
+
@certificates = nil
|
105
|
+
@fingerprint = nil
|
104
106
|
|
105
107
|
if idpsso_descriptor.nil?
|
106
108
|
raise ArgumentError.new("idp_metadata must contain an IDPSSODescriptor element")
|
@@ -47,6 +47,11 @@ module OneLogin
|
|
47
47
|
# conflicts so this line will solve them.
|
48
48
|
relay_state = params[:RelayState] || params['RelayState']
|
49
49
|
|
50
|
+
if relay_state.nil?
|
51
|
+
params.delete(:RelayState)
|
52
|
+
params.delete('RelayState')
|
53
|
+
end
|
54
|
+
|
50
55
|
request_doc = create_logout_request_xml_doc(settings)
|
51
56
|
request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
|
52
57
|
|
@@ -30,6 +30,15 @@ module OneLogin
|
|
30
30
|
|
31
31
|
attr_accessor :soft
|
32
32
|
|
33
|
+
# Response available options
|
34
|
+
# This is not a whitelist to allow people extending OneLogin::RubySaml:Response
|
35
|
+
# and pass custom options
|
36
|
+
AVAILABLE_OPTIONS = [
|
37
|
+
:allowed_clock_drift, :check_duplicated_attributes, :matches_request_id, :settings, :skip_authnstatement, :skip_conditions,
|
38
|
+
:skip_destination, :skip_recipient_check, :skip_subject_confirmation
|
39
|
+
]
|
40
|
+
# TODO: Update the comment on initialize to describe every option
|
41
|
+
|
33
42
|
# Constructs the SAML Response. A Response Object that is an extension of the SamlMessage class.
|
34
43
|
# @param response [String] A UUEncoded SAML response from the IdP.
|
35
44
|
# @param options [Hash] :settings to provide the OneLogin::RubySaml::Settings object
|
@@ -583,7 +592,8 @@ module OneLogin
|
|
583
592
|
return true if audiences.empty? || settings.issuer.nil? || settings.issuer.empty?
|
584
593
|
|
585
594
|
unless audiences.include? settings.issuer
|
586
|
-
|
595
|
+
s = audiences.count > 1 ? 's' : '';
|
596
|
+
error_msg = "Invalid Audience#{s}. The audience#{s} #{audiences.join(',')}, did not match the expected audience #{settings.issuer}"
|
587
597
|
return append_error(error_msg)
|
588
598
|
end
|
589
599
|
|
@@ -615,10 +625,13 @@ module OneLogin
|
|
615
625
|
end
|
616
626
|
|
617
627
|
# Checks that the samlp:Response/saml:Assertion/saml:Conditions element exists and is unique.
|
628
|
+
# (If the response was initialized with the :skip_conditions option, this validation is skipped)
|
618
629
|
# If fails, the error is added to the errors array
|
619
630
|
# @return [Boolean] True if there is a conditions element and is unique
|
620
631
|
#
|
621
632
|
def validate_one_conditions
|
633
|
+
return true if options[:skip_conditions]
|
634
|
+
|
622
635
|
conditions_nodes = xpath_from_signed_assertion('/a:Conditions')
|
623
636
|
unless conditions_nodes.size == 1
|
624
637
|
error_msg = "The Assertion must include one Conditions element"
|
@@ -633,6 +646,8 @@ module OneLogin
|
|
633
646
|
# @return [Boolean] True if there is a authnstatement element and is unique
|
634
647
|
#
|
635
648
|
def validate_one_authnstatement
|
649
|
+
return true if options[:skip_authnstatement]
|
650
|
+
|
636
651
|
authnstatement_nodes = xpath_from_signed_assertion('/a:AuthnStatement')
|
637
652
|
unless authnstatement_nodes.size == 1
|
638
653
|
error_msg = "The Assertion must include one AuthnStatement element"
|
@@ -53,6 +53,11 @@ module OneLogin
|
|
53
53
|
# conflicts so this line will solve them.
|
54
54
|
relay_state = params[:RelayState] || params['RelayState']
|
55
55
|
|
56
|
+
if relay_state.nil?
|
57
|
+
params.delete(:RelayState)
|
58
|
+
params.delete('RelayState')
|
59
|
+
end
|
60
|
+
|
56
61
|
response_doc = create_logout_response_xml_doc(settings, request_id, logout_message)
|
57
62
|
response_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
|
58
63
|
|
@@ -108,7 +113,7 @@ module OneLogin
|
|
108
113
|
issuer = root.add_element "saml:Issuer"
|
109
114
|
issuer.text = settings.issuer
|
110
115
|
end
|
111
|
-
|
116
|
+
|
112
117
|
# add success message
|
113
118
|
status = root.add_element 'samlp:Status'
|
114
119
|
|
@@ -185,6 +185,17 @@ class IdpMetadataParserTest < Minitest::Test
|
|
185
185
|
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", parsed_metadata[:idp_cert_fingerprint]
|
186
186
|
assert_nil parsed_metadata[:security]
|
187
187
|
end
|
188
|
+
|
189
|
+
it "can extract certificates multiple times in sequence" do
|
190
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
191
|
+
idp_metadata1 = idp_metadata_descriptor
|
192
|
+
idp_metadata2 = idp_metadata_descriptor4
|
193
|
+
metadata1 = idp_metadata_parser.parse_to_hash(idp_metadata1)
|
194
|
+
metadata2 = idp_metadata_parser.parse_to_hash(idp_metadata2)
|
195
|
+
|
196
|
+
assert_equal "F1:3C:6B:80:90:5A:03:0E:6C:91:3E:5D:15:FA:DD:B0:16:45:48:72", metadata1[:idp_cert_fingerprint]
|
197
|
+
assert_equal "CD:2B:2B:DA:FF:F5:DB:64:10:7C:AC:FD:FE:0F:CB:5D:73:5F:16:07", metadata2[:idp_cert_fingerprint]
|
198
|
+
end
|
188
199
|
end
|
189
200
|
|
190
201
|
describe "parsing an IdP descriptor file with multiple signing certs" do
|
data/test/logoutrequest_test.rb
CHANGED
@@ -28,6 +28,20 @@ class RequestTest < Minitest::Test
|
|
28
28
|
assert_match /&foo=bar$/, unauth_url
|
29
29
|
end
|
30
30
|
|
31
|
+
it "RelayState cases" do
|
32
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :RelayState => nil })
|
33
|
+
assert !unauth_url.include?('RelayState')
|
34
|
+
|
35
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { :RelayState => "http://example.com" })
|
36
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
37
|
+
|
38
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { 'RelayState' => nil })
|
39
|
+
assert !unauth_url.include?('RelayState')
|
40
|
+
|
41
|
+
unauth_url = OneLogin::RubySaml::Logoutrequest.new.create(settings, { 'RelayState' => "http://example.com" })
|
42
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
43
|
+
end
|
44
|
+
|
31
45
|
it "set sessionindex" do
|
32
46
|
settings.idp_slo_target_url = "http://example.com"
|
33
47
|
sessionidx = OneLogin::RubySaml::Utils.uuid
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<md:EntityDescriptor entityID="https://hello.example.com/access/saml/idp.xml" validUntil="2014-04-17T18:02:33.910Z" xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata">
|
3
|
+
<md:IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
|
4
|
+
<md:KeyDescriptor use="signing">
|
5
|
+
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
6
|
+
<ds:X509Data>
|
7
|
+
<ds:X509Certificate>MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF
|
8
|
+
BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj
|
9
|
+
aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW
|
10
|
+
T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy
|
11
|
+
MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz
|
12
|
+
Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV
|
13
|
+
BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
14
|
+
DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo
|
15
|
+
3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw
|
16
|
+
tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx
|
17
|
+
VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5
|
18
|
+
L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t
|
19
|
+
1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/
|
20
|
+
BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB
|
21
|
+
pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD
|
22
|
+
VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL
|
23
|
+
DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC
|
24
|
+
FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B
|
25
|
+
AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM
|
26
|
+
GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c
|
27
|
+
hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB
|
28
|
+
vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37
|
29
|
+
MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ
|
30
|
+
WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==</ds:X509Certificate>
|
31
|
+
</ds:X509Data>
|
32
|
+
</ds:KeyInfo>
|
33
|
+
</md:KeyDescriptor>
|
34
|
+
<md:KeyDescriptor use="encryption">
|
35
|
+
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
|
36
|
+
<ds:X509Data>
|
37
|
+
<ds:X509Certificate>MIIEZTCCA02gAwIBAgIUPyy/A3bZAZ4m28PzEUUoT7RJhxIwDQYJKoZIhvcNAQEF
|
38
|
+
BQAwcjELMAkGA1UEBhMCVVMxKzApBgNVBAoMIk9uZUxvZ2luIFRlc3QgKHNnYXJj
|
39
|
+
aWEtdXMtcHJlcHJvZCkxFTATBgNVBAsMDE9uZUxvZ2luIElkUDEfMB0GA1UEAwwW
|
40
|
+
T25lTG9naW4gQWNjb3VudCA4OTE0NjAeFw0xNjA4MDQyMjI5MzdaFw0yMTA4MDUy
|
41
|
+
MjI5MzdaMHIxCzAJBgNVBAYTAlVTMSswKQYDVQQKDCJPbmVMb2dpbiBUZXN0IChz
|
42
|
+
Z2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQLDAxPbmVMb2dpbiBJZFAxHzAdBgNV
|
43
|
+
BAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDYwggEiMA0GCSqGSIb3DQEBAQUAA4IB
|
44
|
+
DwAwggEKAoIBAQDN6iqQGcLOCglNO42I2rkzE05UXSiMXT6c8ALThMMiaDw6qqzo
|
45
|
+
3sd/tKK+NcNKWLIIC8TozWVyh5ykUiVZps+08xil7VsTU7E+wKu3kvmOsvw2wlRw
|
46
|
+
tnoKZJwYhnr+RkBa+h1r3ZYUgXm1ZPeHMKj1g18KaWz9+MxYL6BhKqrOzfW/P2xx
|
47
|
+
VRcFH7/pq+ZsDdgNzD2GD+apzY4MZyZj/N6BpBWJ0GlFsmtBegpbX3LBitJuFkk5
|
48
|
+
L4/U/jjF1AJa3boBdCUVfATqO5G03H4XS1GySjBIRQXmlUF52rLjg6xCgWJ30/+t
|
49
|
+
1X+IHLJeixiQ0vxyh6C4/usCEt94cgD1r8ADAgMBAAGjgfIwge8wDAYDVR0TAQH/
|
50
|
+
BAIwADAdBgNVHQ4EFgQUPW0DcH0G3IwynWgi74co4wZ6n7gwga8GA1UdIwSBpzCB
|
51
|
+
pIAUPW0DcH0G3IwynWgi74co4wZ6n7ihdqR0MHIxCzAJBgNVBAYTAlVTMSswKQYD
|
52
|
+
VQQKDCJPbmVMb2dpbiBUZXN0IChzZ2FyY2lhLXVzLXByZXByb2QpMRUwEwYDVQQL
|
53
|
+
DAxPbmVMb2dpbiBJZFAxHzAdBgNVBAMMFk9uZUxvZ2luIEFjY291bnQgODkxNDaC
|
54
|
+
FD8svwN22QGeJtvD8xFFKE+0SYcSMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0B
|
55
|
+
AQUFAAOCAQEAQhB4q9jrycwbHrDSoYR1X4LFFzvJ9Us75wQquRHXpdyS9D6HUBXM
|
56
|
+
GI6ahPicXCQrfLgN8vzMIiqZqfySXXv/8/dxe/X4UsWLYKYJHDJmxXD5EmWTa65c
|
57
|
+
hjkeP1oJAc8f3CKCpcP2lOBTthbnk2fEVAeLHR4xNdQO0VvGXWO9BliYPpkYqUIB
|
58
|
+
vlm+Fg9mF7AM/Uagq2503XXIE1Lq//HON68P10vNMwLSKOtYLsoTiCnuIKGJqG37
|
59
|
+
MsZVjQ1ZPRcO+LSLkq0i91gFxrOrVCrgztX4JQi5XkvEsYZGIXXjwHqxTVyt3adZ
|
60
|
+
WQO0LPxPqRiUqUzyhDhLo/xXNrHCu4VbMw==</ds:X509Certificate>
|
61
|
+
</ds:X509Data>
|
62
|
+
</ds:KeyInfo>
|
63
|
+
</md:KeyDescriptor>
|
64
|
+
<md:SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/logout" ResponseLocation="https://hello.example.com/access/saml/logout"/>
|
65
|
+
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</md:NameIDFormat>
|
66
|
+
<md:NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</md:NameIDFormat>
|
67
|
+
<md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</md:NameIDFormat>
|
68
|
+
<md:SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://hello.example.com/access/saml/login"/>
|
69
|
+
<saml:Attribute Name="AuthToken" NameFormat="urn:oasis:names:tc:SAML:2.0:att rname-format:basic" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
|
70
|
+
<saml:Attribute Name="SSOStartPage" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:uri" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"/>
|
71
|
+
</md:IDPSSODescriptor>
|
72
|
+
</md:EntityDescriptor>
|
data/test/request_test.rb
CHANGED
@@ -129,6 +129,33 @@ class RequestTest < Minitest::Test
|
|
129
129
|
assert_match /&hello=$/, auth_url
|
130
130
|
end
|
131
131
|
|
132
|
+
it "RelayState cases" do
|
133
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :RelayState => nil })
|
134
|
+
assert !auth_url.include?('RelayState')
|
135
|
+
|
136
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { :RelayState => "http://example.com" })
|
137
|
+
assert auth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
138
|
+
|
139
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { 'RelayState' => nil })
|
140
|
+
assert !auth_url.include?('RelayState')
|
141
|
+
|
142
|
+
auth_url = OneLogin::RubySaml::Authrequest.new.create(settings, { 'RelayState' => "http://example.com" })
|
143
|
+
assert auth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
144
|
+
end
|
145
|
+
|
146
|
+
describe "when the target url is not set" do
|
147
|
+
before do
|
148
|
+
settings.idp_sso_target_url = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
it "raises an error with a descriptive message" do
|
152
|
+
err = assert_raises RuntimeError do
|
153
|
+
OneLogin::RubySaml::Authrequest.new.create(settings)
|
154
|
+
end
|
155
|
+
assert_match /idp_sso_target_url is not set/, err.message
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
132
159
|
describe "when the target url doesn't contain a query string" do
|
133
160
|
it "create the SAMLRequest parameter correctly" do
|
134
161
|
|
@@ -222,7 +249,7 @@ class RequestTest < Minitest::Test
|
|
222
249
|
settings.certificate = ruby_saml_cert_text
|
223
250
|
settings.private_key = ruby_saml_key_text
|
224
251
|
end
|
225
|
-
|
252
|
+
|
226
253
|
it "create a signature parameter with RSA_SHA1 and validate it" do
|
227
254
|
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
228
255
|
|
@@ -255,7 +282,7 @@ class RequestTest < Minitest::Test
|
|
255
282
|
|
256
283
|
signature_algorithm = XMLSecurity::BaseDocument.new.algorithm(params['SigAlg'])
|
257
284
|
assert_equal signature_algorithm, OpenSSL::Digest::SHA256
|
258
|
-
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
285
|
+
assert cert.public_key.verify(signature_algorithm.new, Base64.decode64(params['Signature']), query_string)
|
259
286
|
end
|
260
287
|
end
|
261
288
|
|
data/test/response_test.rb
CHANGED
@@ -23,7 +23,9 @@ class RubySamlTest < Minitest::Test
|
|
23
23
|
let(:response_no_version) { OneLogin::RubySaml::Response.new(read_invalid_response("no_saml2.xml.base64")) }
|
24
24
|
let(:response_multi_assertion) { OneLogin::RubySaml::Response.new(read_invalid_response("multiple_assertions.xml.base64")) }
|
25
25
|
let(:response_no_conditions) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64")) }
|
26
|
+
let(:response_no_conditions_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_conditions.xml.base64"), { :skip_conditions => true }) }
|
26
27
|
let(:response_no_authnstatement) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64")) }
|
28
|
+
let(:response_no_authnstatement_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("no_authnstatement.xml.base64"), {:skip_authnstatement => true}) }
|
27
29
|
let(:response_empty_destination) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64")) }
|
28
30
|
let(:response_empty_destination_with_skip) { OneLogin::RubySaml::Response.new(read_invalid_response("empty_destination.xml.base64"), {:skip_destination => true}) }
|
29
31
|
let(:response_no_status) { OneLogin::RubySaml::Response.new(read_invalid_response("no_status.xml.base64")) }
|
@@ -54,10 +56,22 @@ class RubySamlTest < Minitest::Test
|
|
54
56
|
let(:response_invalid_signature_position) { OneLogin::RubySaml::Response.new(read_invalid_response("invalid_signature_position.xml.base64")) }
|
55
57
|
let(:response_encrypted_nameid) { OneLogin::RubySaml::Response.new(response_document_encrypted_nameid) }
|
56
58
|
|
59
|
+
def generate_audience_error(expected, actual)
|
60
|
+
s = actual.count > 1 ? 's' : '';
|
61
|
+
return "Invalid Audience#{s}. The audience#{s} #{actual.join(',')}, did not match the expected audience #{expected}"
|
62
|
+
end
|
63
|
+
|
57
64
|
it "raise an exception when response is initialized with nil" do
|
58
65
|
assert_raises(ArgumentError) { OneLogin::RubySaml::Response.new(nil) }
|
59
66
|
end
|
60
67
|
|
68
|
+
it "not filter available options only" do
|
69
|
+
options = { :skip_destination => true, :foo => :bar }
|
70
|
+
response = OneLogin::RubySaml::Response.new(response_document_valid_signed, options)
|
71
|
+
assert_includes response.options.keys, :skip_destination
|
72
|
+
assert_includes response.options.keys, :foo
|
73
|
+
end
|
74
|
+
|
61
75
|
it "be able to parse a document which contains ampersands" do
|
62
76
|
XMLSecurity::SignedDocument.any_instance.stubs(:digests_match?).returns(true)
|
63
77
|
OneLogin::RubySaml::Response.any_instance.stubs(:validate_conditions).returns(true)
|
@@ -82,7 +96,32 @@ class RubySamlTest < Minitest::Test
|
|
82
96
|
it "receives the full AttributeValue when there is an injected comment" do
|
83
97
|
assert_equal "smith", @response.attributes["surname"]
|
84
98
|
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "Another test to prevent with comment attack (VU#475445)" do
|
102
|
+
before do
|
103
|
+
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack2.xml.base64'), {:skip_recipient_check => true })
|
104
|
+
@response.settings = settings
|
105
|
+
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
106
|
+
end
|
107
|
+
|
108
|
+
it "receives the full NameID when there is an injected comment, validates the response" do
|
109
|
+
assert_equal "test@onelogin.com", @response.name_id
|
110
|
+
end
|
111
|
+
end
|
85
112
|
|
113
|
+
describe "Another test with CDATA injected" do
|
114
|
+
before do
|
115
|
+
@response = OneLogin::RubySaml::Response.new(read_response('response_node_text_attack3.xml.base64'), {:skip_recipient_check => true })
|
116
|
+
@response.settings = settings
|
117
|
+
@response.settings.idp_cert_fingerprint = ruby_saml_cert_fingerprint
|
118
|
+
end
|
119
|
+
|
120
|
+
it "it normalizes CDATA but reject SAMLResponse due signature invalidation" do
|
121
|
+
assert_equal "test@onelogin.com.evil.com", @response.name_id
|
122
|
+
assert !@response.is_valid?
|
123
|
+
assert_includes @response.errors, "Invalid Signature on SAML Response"
|
124
|
+
end
|
86
125
|
end
|
87
126
|
|
88
127
|
describe "Prevent XEE attack" do
|
@@ -223,7 +262,7 @@ class RubySamlTest < Minitest::Test
|
|
223
262
|
settings.issuer = 'invalid'
|
224
263
|
response_valid_signed.settings = settings
|
225
264
|
response_valid_signed.soft = false
|
226
|
-
error_msg =
|
265
|
+
error_msg = generate_audience_error(response_valid_signed.settings.issuer, ['https://someone.example.com/audience'])
|
227
266
|
assert_raises(OneLogin::RubySaml::ValidationError, error_msg) do
|
228
267
|
response_valid_signed.is_valid?
|
229
268
|
end
|
@@ -379,7 +418,8 @@ class RubySamlTest < Minitest::Test
|
|
379
418
|
settings.issuer = 'invalid'
|
380
419
|
response_valid_signed.settings = settings
|
381
420
|
response_valid_signed.is_valid?
|
382
|
-
|
421
|
+
|
422
|
+
assert_includes response_valid_signed.errors, generate_audience_error(response_valid_signed.settings.issuer, ['https://someone.example.com/audience'])
|
383
423
|
end
|
384
424
|
|
385
425
|
it "return false when no ID present in the SAML Response" do
|
@@ -415,7 +455,7 @@ class RubySamlTest < Minitest::Test
|
|
415
455
|
response_invalid_subjectconfirmation_recipient.settings = settings
|
416
456
|
collect_errors = true
|
417
457
|
response_invalid_subjectconfirmation_recipient.is_valid?(collect_errors)
|
418
|
-
assert_includes response_invalid_subjectconfirmation_recipient.errors,
|
458
|
+
assert_includes response_invalid_subjectconfirmation_recipient.errors, generate_audience_error('invalid', ['http://stuff.com/endpoints/metadata.php'])
|
419
459
|
assert_includes response_invalid_subjectconfirmation_recipient.errors, "Invalid Signature on SAML Response"
|
420
460
|
end
|
421
461
|
end
|
@@ -440,7 +480,7 @@ class RubySamlTest < Minitest::Test
|
|
440
480
|
response.settings = settings
|
441
481
|
response.settings.issuer = 'invalid_audience'
|
442
482
|
assert !response.send(:validate_audience)
|
443
|
-
assert_includes response.errors,
|
483
|
+
assert_includes response.errors, generate_audience_error(response.settings.issuer, ['{audience}'])
|
444
484
|
end
|
445
485
|
end
|
446
486
|
|
@@ -626,7 +666,7 @@ class RubySamlTest < Minitest::Test
|
|
626
666
|
response_invalid_audience.settings = settings
|
627
667
|
response_invalid_audience.settings.issuer = "https://invalid.example.com/audience"
|
628
668
|
assert !response_invalid_audience.send(:validate_audience)
|
629
|
-
assert_includes response_invalid_audience.errors,
|
669
|
+
assert_includes response_invalid_audience.errors, generate_audience_error(response_invalid_audience.settings.issuer, ['http://invalid.audience.com'])
|
630
670
|
end
|
631
671
|
end
|
632
672
|
|
@@ -958,6 +998,11 @@ class RubySamlTest < Minitest::Test
|
|
958
998
|
response.soft = true
|
959
999
|
assert response.send(:validate_one_conditions)
|
960
1000
|
end
|
1001
|
+
|
1002
|
+
it "return true when no conditions are present and skip_conditions is true" do
|
1003
|
+
response_no_conditions_with_skip.soft = true
|
1004
|
+
assert response_no_conditions_with_skip.send(:validate_one_conditions)
|
1005
|
+
end
|
961
1006
|
end
|
962
1007
|
|
963
1008
|
describe "#check_one_authnstatement" do
|
@@ -971,6 +1016,12 @@ class RubySamlTest < Minitest::Test
|
|
971
1016
|
response.soft = true
|
972
1017
|
assert response.send(:validate_one_authnstatement)
|
973
1018
|
end
|
1019
|
+
|
1020
|
+
it "return true when SAML Response is empty but skip_authstatement option is used" do
|
1021
|
+
response_no_authnstatement_with_skip.soft = true
|
1022
|
+
assert response_no_authnstatement_with_skip.send(:validate_one_authnstatement)
|
1023
|
+
assert_empty response_empty_destination_with_skip.errors
|
1024
|
+
end
|
974
1025
|
end
|
975
1026
|
|
976
1027
|
describe "#check_conditions" do
|
@@ -0,0 +1 @@
|
|
1
|
+
PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGJjODI2YWZkLWU5ZmUtZDNmYi1kODc0LWM0NzAwYzNlZjBjOCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2FwcC5tdWRhLm5vL3Nzby9jb25zdW1lIiBJblJlc3BvbnNlVG89Il9mYzRhMzRiMC03ZWZiLTAxMmUtY2FhZS03ODJiY2IxM2JiMzgiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbDI8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhiYzgyNmFmZC1lOWZlLWQzZmItZDg3NC1jNDcwMGMzZWYwYzgiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPkl6NFpRbHMzQUpaRGIzczh2Y1VYLzNSYytGUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+UWhLSm1vbnlzUDFxbW5hN1MrZUUxTGMycktBampDMk9HclFPZ1NqUHBUb2N1bVE2aFlIa3pUU1pyN3QvSS9LVE9TdkhDUXFEMXJoNGxTMGpEUC9FdUhOQUN0azlZN2xsMlV5Z3U3MkwrYkZ0cVoyOURuOXJMa1NkR3JpK0k3SGh4TDM2N2RmQVNTaDYrc3k3V2V2RWRrTWZ3ZURRMkFYL3NhNkJCR2d6N1RFPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDR3pDQ0FZUUNDUUNOTmNRWG9tMzJWREFOQmdrcWhraUc5dzBCQVFVRkFEQlNNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0JNQ1NVNHhGVEFUQmdOVkJBY1RERWx1WkdsaGJtRndiMnhwY3pFUk1BOEdBMVVFQ2hNSVQyNWxURzluYVc0eEREQUtCZ05WQkFzVEEwVnVaekFlRncweE5EQTBNak14T0RReE1ERmFGdzB4TlRBME1qTXhPRFF4TURGYU1GSXhDekFKQmdOVkJBWVRBbFZUTVFzd0NRWURWUVFJRXdKSlRqRVZNQk1HQTFVRUJ4TU1TVzVrYVdGdVlYQnZiR2x6TVJFd0R3WURWUVFLRXdoUGJtVk1iMmRwYmpFTU1Bb0dBMVVFQ3hNRFJXNW5NSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURvNm0rUVp2WVEveEwwRWxMZ3VwSzFRRGNZTDRmNVBja3dzTmdTOXBVdlY3ZnpUcUNIazhUaEx4VGs0Mk1RMk1jSnNPZVVKVlA3MjhLaHltakZDcXhnUDRWdXdSazlycEFsMCttaHk2TVBkeWp5QTZHMTRqckRXUzY1eXNMY2hLNHQvdndwRUR6MFNRbEVvRzFrTXpsbFNtN3paUzNYcmVnQTdEak5hVVlRcXdJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBTE0ydkdDaVEvdm0rYTZ2NDArVlgyemRxSEEyUS8xdkYxaWJReko1NE1KQ09WV3ZzK3ZRWGZaRmhkbTBPUE0ySXJEVTdvcXZLUHFQNnhPQWVKSzZIMHlQN000WUwzZmF0U3ZJWW1tZnlYQzlrdDNTdnovTnlySHpQaFVuSjB5ZS9zVVNYeG56UXh3Y20vOVB3QXFyUWFBM1FwUWtINTd5YkYvT29yeVBlKzJoPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgVmVyc2lvbj0iMi4wIiBJRD0icGZ4OTUxNmIwZjMtNDUzNi0xMGY2LWM2ZmEtOWRkNTIzZTE0OThjIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwyPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+dGVzdDwhLS0gYXR0YWNrIC0tPkBvbmVsb2dpbi5jb208L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMzAtMDYtMDRUMDI6Mjc6MDJaIiBSZWNpcGllbnQ9InJlY2lwaWVudCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDExLTA2LTA0VDAyOjE3OjAyWiIgTm90T25PckFmdGVyPSIyMDMwLTA2LTA0VDAyOjI3OjAyWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovL3NvbWVvbmUuZXhhbXBsZS5jb20vYXVkaWVuY2U8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA2LTA0VDAyOjIyOjAyWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAzMC0wNi0wNVQwMjoyMjowMloiIFNlc3Npb25JbmRleD0iXzE2ZjU3MGZiYzAzMTUwMDdhMDM1NWRmZWE2YjNjNDZjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
|
@@ -0,0 +1 @@
|
|
1
|
+
PD94bWwgdmVyc2lvbj0iMS4wIj8+DQo8c2FtbHA6UmVzcG9uc2UgeG1sbnM6c2FtbD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFzc2VydGlvbiIgeG1sbnM6c2FtbHA9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDpwcm90b2NvbCIgSUQ9InBmeGJjODI2YWZkLWU5ZmUtZDNmYi1kODc0LWM0NzAwYzNlZjBjOCIgVmVyc2lvbj0iMi4wIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIiBEZXN0aW5hdGlvbj0iaHR0cDovL2FwcC5tdWRhLm5vL3Nzby9jb25zdW1lIiBJblJlc3BvbnNlVG89Il9mYzRhMzRiMC03ZWZiLTAxMmUtY2FhZS03ODJiY2IxM2JiMzgiPjxzYW1sOklzc3Vlcj5odHRwczovL2FwcC5vbmVsb2dpbi5jb20vc2FtbDI8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPg0KICA8ZHM6U2lnbmVkSW5mbz48ZHM6Q2Fub25pY2FsaXphdGlvbk1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPg0KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjcnNhLXNoYTEiLz4NCiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNwZnhiYzgyNmFmZC1lOWZlLWQzZmItZDg3NC1jNDcwMGMzZWYwYzgiPjxkczpUcmFuc2Zvcm1zPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjZW52ZWxvcGVkLXNpZ25hdHVyZSIvPjxkczpUcmFuc2Zvcm0gQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz48L2RzOlRyYW5zZm9ybXM+PGRzOkRpZ2VzdE1ldGhvZCBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNzaGExIi8+PGRzOkRpZ2VzdFZhbHVlPkl6NFpRbHMzQUpaRGIzczh2Y1VYLzNSYytGUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+UWhLSm1vbnlzUDFxbW5hN1MrZUUxTGMycktBampDMk9HclFPZ1NqUHBUb2N1bVE2aFlIa3pUU1pyN3QvSS9LVE9TdkhDUXFEMXJoNGxTMGpEUC9FdUhOQUN0azlZN2xsMlV5Z3U3MkwrYkZ0cVoyOURuOXJMa1NkR3JpK0k3SGh4TDM2N2RmQVNTaDYrc3k3V2V2RWRrTWZ3ZURRMkFYL3NhNkJCR2d6N1RFPTwvZHM6U2lnbmF0dXJlVmFsdWU+DQo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlDR3pDQ0FZUUNDUUNOTmNRWG9tMzJWREFOQmdrcWhraUc5dzBCQVFVRkFEQlNNUXN3Q1FZRFZRUUdFd0pWVXpFTE1Ba0dBMVVFQ0JNQ1NVNHhGVEFUQmdOVkJBY1RERWx1WkdsaGJtRndiMnhwY3pFUk1BOEdBMVVFQ2hNSVQyNWxURzluYVc0eEREQUtCZ05WQkFzVEEwVnVaekFlRncweE5EQTBNak14T0RReE1ERmFGdzB4TlRBME1qTXhPRFF4TURGYU1GSXhDekFKQmdOVkJBWVRBbFZUTVFzd0NRWURWUVFJRXdKSlRqRVZNQk1HQTFVRUJ4TU1TVzVrYVdGdVlYQnZiR2x6TVJFd0R3WURWUVFLRXdoUGJtVk1iMmRwYmpFTU1Bb0dBMVVFQ3hNRFJXNW5NSUdmTUEwR0NTcUdTSWIzRFFFQkFRVUFBNEdOQURDQmlRS0JnUURvNm0rUVp2WVEveEwwRWxMZ3VwSzFRRGNZTDRmNVBja3dzTmdTOXBVdlY3ZnpUcUNIazhUaEx4VGs0Mk1RMk1jSnNPZVVKVlA3MjhLaHltakZDcXhnUDRWdXdSazlycEFsMCttaHk2TVBkeWp5QTZHMTRqckRXUzY1eXNMY2hLNHQvdndwRUR6MFNRbEVvRzFrTXpsbFNtN3paUzNYcmVnQTdEak5hVVlRcXdJREFRQUJNQTBHQ1NxR1NJYjNEUUVCQlFVQUE0R0JBTE0ydkdDaVEvdm0rYTZ2NDArVlgyemRxSEEyUS8xdkYxaWJReko1NE1KQ09WV3ZzK3ZRWGZaRmhkbTBPUE0ySXJEVTdvcXZLUHFQNnhPQWVKSzZIMHlQN000WUwzZmF0U3ZJWW1tZnlYQzlrdDNTdnovTnlySHpQaFVuSjB5ZS9zVVNYeG56UXh3Y20vOVB3QXFyUWFBM1FwUWtINTd5YkYvT29yeVBlKzJoPC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgVmVyc2lvbj0iMi4wIiBJRD0icGZ4OTUxNmIwZjMtNDUzNi0xMGY2LWM2ZmEtOWRkNTIzZTE0OThjIiBJc3N1ZUluc3RhbnQ9IjIwMTQtMDYtMDRUMDI6MjI6MDJaIj48c2FtbDpJc3N1ZXI+aHR0cHM6Ly9hcHAub25lbG9naW4uY29tL3NhbWwyPC9zYW1sOklzc3Vlcj48c2FtbDpTdWJqZWN0PjxzYW1sOk5hbWVJRCBGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjEuMTpuYW1laWQtZm9ybWF0OmVtYWlsQWRkcmVzcyI+dGVzdEBvbmVsb2dpbi5jb208IVtDREFUQVsuZXZpbC5jb21dXT48L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMzAtMDYtMDRUMDI6Mjc6MDJaIiBSZWNpcGllbnQ9InJlY2lwaWVudCIvPjwvc2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDpTdWJqZWN0PjxzYW1sOkNvbmRpdGlvbnMgTm90QmVmb3JlPSIyMDExLTA2LTA0VDAyOjE3OjAyWiIgTm90T25PckFmdGVyPSIyMDMwLTA2LTA0VDAyOjI3OjAyWiI+PHNhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48c2FtbDpBdWRpZW5jZT5odHRwczovL3NvbWVvbmUuZXhhbXBsZS5jb20vYXVkaWVuY2U8L3NhbWw6QXVkaWVuY2U+PC9zYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PC9zYW1sOkNvbmRpdGlvbnM+PHNhbWw6QXV0aG5TdGF0ZW1lbnQgQXV0aG5JbnN0YW50PSIyMDE0LTA2LTA0VDAyOjIyOjAyWiIgU2Vzc2lvbk5vdE9uT3JBZnRlcj0iMjAzMC0wNi0wNVQwMjoyMjowMloiIFNlc3Npb25JbmRleD0iXzE2ZjU3MGZiYzAzMTUwMDdhMDM1NWRmZWE2YjNjNDZjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmRQcm90ZWN0ZWRUcmFuc3BvcnQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PC9zYW1sOkFzc2VydGlvbj48L3NhbWxwOlJlc3BvbnNlPg==
|
@@ -37,6 +37,20 @@ class SloLogoutresponseTest < Minitest::Test
|
|
37
37
|
assert_match /&RelayState=http%3A%2F%2Fidp.example.com$/, unauth_url
|
38
38
|
end
|
39
39
|
|
40
|
+
it "RelayState cases" do
|
41
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :RelayState => nil })
|
42
|
+
assert !unauth_url.include?('RelayState')
|
43
|
+
|
44
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { :RelayState => "http://example.com" })
|
45
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
46
|
+
|
47
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { 'RelayState' => nil })
|
48
|
+
assert !unauth_url.include?('RelayState')
|
49
|
+
|
50
|
+
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id, nil, { 'RelayState' => "http://example.com" })
|
51
|
+
assert unauth_url.include?('&RelayState=http%3A%2F%2Fexample.com')
|
52
|
+
end
|
53
|
+
|
40
54
|
it "set InResponseTo to the ID from the logout request" do
|
41
55
|
unauth_url = OneLogin::RubySaml::SloLogoutresponse.new.create(settings, logout_request.id)
|
42
56
|
|
data/test/test_helper.rb
CHANGED
@@ -117,15 +117,15 @@ class Minitest::Test
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def signed_message_encrypted_unsigned_assertion
|
120
|
-
@signed_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_unsigned_assertion.xml.base64'))
|
120
|
+
@signed_message_encrypted_unsigned_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_unsigned_assertion.xml.base64'))
|
121
121
|
end
|
122
122
|
|
123
123
|
def signed_message_encrypted_signed_assertion
|
124
|
-
@signed_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_signed_assertion.xml.base64'))
|
124
|
+
@signed_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'signed_message_encrypted_signed_assertion.xml.base64'))
|
125
125
|
end
|
126
126
|
|
127
127
|
def unsigned_message_encrypted_signed_assertion
|
128
|
-
@unsigned_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_signed_assertion.xml.base64'))
|
128
|
+
@unsigned_message_encrypted_signed_assertion ||= File.read(File.join(File.dirname(__FILE__), 'responses', 'unsigned_message_encrypted_signed_assertion.xml.base64'))
|
129
129
|
end
|
130
130
|
|
131
131
|
def unsigned_message_encrypted_unsigned_assertion
|
@@ -145,7 +145,7 @@ class Minitest::Test
|
|
145
145
|
end
|
146
146
|
|
147
147
|
# certificate used on response_with_undefined_recipient
|
148
|
-
def signature_1
|
148
|
+
def signature_1
|
149
149
|
@signature1 ||= read_certificate("certificate1")
|
150
150
|
end
|
151
151
|
|
@@ -166,6 +166,10 @@ class Minitest::Test
|
|
166
166
|
@idp_metadata_descriptor3 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_3.xml'))
|
167
167
|
end
|
168
168
|
|
169
|
+
def idp_metadata_descriptor4
|
170
|
+
@idp_metadata_descriptor4 ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'idp_descriptor_4.xml'))
|
171
|
+
end
|
172
|
+
|
169
173
|
def no_idp_metadata_descriptor
|
170
174
|
@no_idp_metadata_descriptor ||= File.read(File.join(File.dirname(__FILE__), 'metadata', 'no_idp_descriptor.xml'))
|
171
175
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-saml
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OneLogin LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-04-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -220,6 +220,7 @@ files:
|
|
220
220
|
- test/metadata/idp_descriptor.xml
|
221
221
|
- test/metadata/idp_descriptor_2.xml
|
222
222
|
- test/metadata/idp_descriptor_3.xml
|
223
|
+
- test/metadata/idp_descriptor_4.xml
|
223
224
|
- test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml
|
224
225
|
- test/metadata/idp_metadata_multi_certs.xml
|
225
226
|
- test/metadata/idp_metadata_multi_signing_certs.xml
|
@@ -278,6 +279,8 @@ files:
|
|
278
279
|
- test/responses/response_eval.xml
|
279
280
|
- test/responses/response_no_cert_and_encrypted_attrs.xml
|
280
281
|
- test/responses/response_node_text_attack.xml.base64
|
282
|
+
- test/responses/response_node_text_attack2.xml.base64
|
283
|
+
- test/responses/response_node_text_attack3.xml.base64
|
281
284
|
- test/responses/response_unsigned_xml_base64
|
282
285
|
- test/responses/response_with_ampersands.xml
|
283
286
|
- test/responses/response_with_ampersands.xml.base64
|
@@ -377,6 +380,7 @@ test_files:
|
|
377
380
|
- test/metadata/idp_descriptor.xml
|
378
381
|
- test/metadata/idp_descriptor_2.xml
|
379
382
|
- test/metadata/idp_descriptor_3.xml
|
383
|
+
- test/metadata/idp_descriptor_4.xml
|
380
384
|
- test/metadata/idp_metadata_different_sign_and_encrypt_cert.xml
|
381
385
|
- test/metadata/idp_metadata_multi_certs.xml
|
382
386
|
- test/metadata/idp_metadata_multi_signing_certs.xml
|
@@ -435,6 +439,8 @@ test_files:
|
|
435
439
|
- test/responses/response_eval.xml
|
436
440
|
- test/responses/response_no_cert_and_encrypted_attrs.xml
|
437
441
|
- test/responses/response_node_text_attack.xml.base64
|
442
|
+
- test/responses/response_node_text_attack2.xml.base64
|
443
|
+
- test/responses/response_node_text_attack3.xml.base64
|
438
444
|
- test/responses/response_unsigned_xml_base64
|
439
445
|
- test/responses/response_with_ampersands.xml
|
440
446
|
- test/responses/response_with_ampersands.xml.base64
|