ruby-saml-uppercase 0.5.3.4

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.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +12 -0
  5. data/LICENSE +19 -0
  6. data/README.md +126 -0
  7. data/Rakefile +41 -0
  8. data/lib/onelogin/ruby-saml/authrequest.rb +79 -0
  9. data/lib/onelogin/ruby-saml/logging.rb +26 -0
  10. data/lib/onelogin/ruby-saml/logoutrequest.rb +82 -0
  11. data/lib/onelogin/ruby-saml/logoutresponse.rb +160 -0
  12. data/lib/onelogin/ruby-saml/metadata.rb +47 -0
  13. data/lib/onelogin/ruby-saml/response.rb +195 -0
  14. data/lib/onelogin/ruby-saml/settings.rb +19 -0
  15. data/lib/onelogin/ruby-saml/validation_error.rb +7 -0
  16. data/lib/onelogin/ruby-saml/version.rb +5 -0
  17. data/lib/ruby-saml.rb +9 -0
  18. data/lib/schemas/saml20assertion_schema.xsd +283 -0
  19. data/lib/schemas/saml20protocol_schema.xsd +302 -0
  20. data/lib/schemas/xenc_schema.xsd +146 -0
  21. data/lib/schemas/xmldsig_schema.xsd +318 -0
  22. data/lib/xml_security.rb +168 -0
  23. data/ruby-saml.gemspec +29 -0
  24. data/test/certificates/certificate1 +12 -0
  25. data/test/logoutrequest_test.rb +111 -0
  26. data/test/logoutresponse_test.rb +116 -0
  27. data/test/request_test.rb +53 -0
  28. data/test/response_test.rb +219 -0
  29. data/test/responses/adfs_response_sha1.xml +46 -0
  30. data/test/responses/adfs_response_sha256.xml +46 -0
  31. data/test/responses/adfs_response_sha384.xml +46 -0
  32. data/test/responses/adfs_response_sha512.xml +46 -0
  33. data/test/responses/logoutresponse_fixtures.rb +67 -0
  34. data/test/responses/no_signature_ns.xml +48 -0
  35. data/test/responses/open_saml_response.xml +56 -0
  36. data/test/responses/response1.xml.base64 +1 -0
  37. data/test/responses/response2.xml.base64 +79 -0
  38. data/test/responses/response3.xml.base64 +66 -0
  39. data/test/responses/response4.xml.base64 +93 -0
  40. data/test/responses/response5.xml.base64 +102 -0
  41. data/test/responses/response_with_ampersands.xml +139 -0
  42. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  43. data/test/responses/simple_saml_php.xml +71 -0
  44. data/test/responses/wrapped_response_2.xml.base64 +150 -0
  45. data/test/settings_test.rb +44 -0
  46. data/test/test_helper.rb +66 -0
  47. data/test/xml_security_test.rb +123 -0
  48. metadata +166 -0
@@ -0,0 +1,168 @@
1
+ # The contents of this file are subject to the terms
2
+ # of the Common Development and Distribution License
3
+ # (the License). You may not use this file except in
4
+ # compliance with the License.
5
+ #
6
+ # You can obtain a copy of the License at
7
+ # https://opensso.dev.java.net/public/CDDLv1.0.html or
8
+ # opensso/legal/CDDLv1.0.txt
9
+ # See the License for the specific language governing
10
+ # permission and limitations under the License.
11
+ #
12
+ # When distributing Covered Code, include this CDDL
13
+ # Header Notice in each file and include the License file
14
+ # at opensso/legal/CDDLv1.0.txt.
15
+ # If applicable, add the following below the CDDL Header,
16
+ # with the fields enclosed by brackets [] replaced by
17
+ # your own identifying information:
18
+ # "Portions Copyrighted [year] [name of copyright owner]"
19
+ #
20
+ # $Id: xml_sec.rb,v 1.6 2007/10/24 00:28:41 todddd Exp $
21
+ #
22
+ # Copyright 2007 Sun Microsystems Inc. All Rights Reserved
23
+ # Portions Copyrighted 2007 Todd W Saxton.
24
+
25
+ require 'rubygems'
26
+ require "rexml/document"
27
+ require "rexml/xpath"
28
+ require "openssl"
29
+ require 'nokogiri'
30
+ require 'xmlcanonicalizer'
31
+ require "digest/sha1"
32
+ require "digest/sha2"
33
+ require "onelogin/ruby-saml/validation_error"
34
+
35
+ module XMLSecurity
36
+
37
+ class SignedDocument < REXML::Document
38
+ C14N = "http://www.w3.org/2001/10/xml-exc-c14n#"
39
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
40
+
41
+ attr_accessor :signed_element_id, :sig_element, :noko_sig_element
42
+
43
+ def initialize(response)
44
+ super(response)
45
+ extract_signed_element_id
46
+ end
47
+
48
+ def validate(idp_cert_fingerprint, soft = true)
49
+ # get cert from response
50
+ cert_element = REXML::XPath.first(self, "//ds:X509Certificate", { "ds"=>DSIG })
51
+ base64_cert = cert_element.text
52
+ cert_text = Base64.decode64(base64_cert)
53
+ cert = OpenSSL::X509::Certificate.new(cert_text)
54
+
55
+ # check cert matches registered idp cert
56
+ fingerprint = Digest::SHA1.hexdigest(cert.to_der)
57
+
58
+ if fingerprint != idp_cert_fingerprint.gsub(/[^a-zA-Z0-9]/,"").downcase
59
+ return soft ? false : (raise Onelogin::Saml::ValidationError.new("Fingerprint mismatch"))
60
+ end
61
+
62
+ validate_doc(base64_cert, soft)
63
+ end
64
+
65
+ def validate_doc(base64_cert, soft = true)
66
+ # validate references
67
+
68
+ # check for inclusive namespaces
69
+ inclusive_namespaces = extract_inclusive_namespaces
70
+
71
+ document = Nokogiri.parse(self.to_s)
72
+
73
+ # store and remove signature node
74
+ self.sig_element ||= begin
75
+ element = REXML::XPath.first(self, "//ds:Signature", {"ds"=>DSIG})
76
+ element.remove
77
+ end
78
+
79
+
80
+ # verify signature
81
+ canoner = XML::Util::XmlCanonicalizer.new(false, true)
82
+ signed_info_element = REXML::XPath.first(sig_element, "//ds:SignedInfo", {"ds"=>DSIG})
83
+ canon_string = canoner.canonicalize(signed_info_element)
84
+ self.noko_sig_element ||= document.at_xpath('//ds:Signature', 'ds' => DSIG)
85
+ noko_signed_info_element = noko_sig_element.at_xpath('./ds:SignedInfo', 'ds' => DSIG)
86
+ canon_algorithm = canon_algorithm REXML::XPath.first(sig_element, '//ds:CanonicalizationMethod')
87
+ canon_string = noko_signed_info_element.canonicalize(canon_algorithm)
88
+ noko_sig_element.remove
89
+
90
+ # check digests
91
+ REXML::XPath.each(sig_element, "//ds:Reference", {"ds"=>DSIG}) do |ref|
92
+ uri = ref.attributes.get_attribute("URI").value
93
+
94
+ hashed_element = document.at_xpath("//*[@ID='#{uri[1..-1]}']")
95
+ canon_algorithm = canon_algorithm REXML::XPath.first(ref, '//ds:CanonicalizationMethod')
96
+ canon_hashed_element = hashed_element.canonicalize(canon_algorithm, inclusive_namespaces).gsub('&','&amp;')
97
+
98
+ digest_algorithm = algorithm(REXML::XPath.first(ref, "//ds:DigestMethod"))
99
+
100
+ hash = digest_algorithm.digest(canon_hashed_element)
101
+ digest_value = Base64.decode64(REXML::XPath.first(ref, "//ds:DigestValue", {"ds"=>DSIG}).text)
102
+
103
+ unless digests_match?(hash, digest_value)
104
+ return soft ? false : (raise Onelogin::Saml::ValidationError.new("Digest mismatch"))
105
+ end
106
+ end
107
+
108
+ base64_signature = REXML::XPath.first(sig_element, "//ds:SignatureValue", {"ds"=>DSIG}).text
109
+ signature = Base64.decode64(base64_signature)
110
+
111
+ # get certificate object
112
+ cert_text = Base64.decode64(base64_cert)
113
+ cert = OpenSSL::X509::Certificate.new(cert_text)
114
+
115
+ # signature method
116
+ signature_algorithm = algorithm(REXML::XPath.first(signed_info_element, "//ds:SignatureMethod", {"ds"=>DSIG}))
117
+
118
+ unless cert.public_key.verify(signature_algorithm.new, signature, canon_string)
119
+ return soft ? false : (raise Onelogin::Saml::ValidationError.new("Key validation error"))
120
+ end
121
+
122
+ return true
123
+ end
124
+
125
+ private
126
+
127
+ def digests_match?(hash, digest_value)
128
+ hash == digest_value
129
+ end
130
+
131
+ def extract_signed_element_id
132
+ reference_element = REXML::XPath.first(self, "//ds:Signature/ds:SignedInfo/ds:Reference", {"ds"=>DSIG})
133
+ self.signed_element_id = reference_element.attribute("URI").value[1..-1] unless reference_element.nil?
134
+ end
135
+
136
+ def canon_algorithm(element)
137
+ algorithm = element.attribute('Algorithm').value if element
138
+ case algorithm
139
+ when "http://www.w3.org/2001/10/xml-exc-c14n#" then Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
140
+ when "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" then Nokogiri::XML::XML_C14N_1_0
141
+ when "http://www.w3.org/2006/12/xml-c14n11" then Nokogiri::XML::XML_C14N_1_1
142
+ else Nokogiri::XML::XML_C14N_EXCLUSIVE_1_0
143
+ end
144
+ end
145
+
146
+ def algorithm(element)
147
+ algorithm = element.attribute("Algorithm").value if element
148
+ algorithm = algorithm && algorithm =~ /sha(.*?)$/i && $1.to_i
149
+ case algorithm
150
+ when 256 then OpenSSL::Digest::SHA256
151
+ when 384 then OpenSSL::Digest::SHA384
152
+ when 512 then OpenSSL::Digest::SHA512
153
+ else
154
+ OpenSSL::Digest::SHA1
155
+ end
156
+ end
157
+
158
+ def extract_inclusive_namespaces
159
+ if element = REXML::XPath.first(self, "//ec:InclusiveNamespaces", { "ec" => C14N })
160
+ prefix_list = element.attributes.get_attribute("PrefixList").value
161
+ prefix_list.split(" ")
162
+ else
163
+ []
164
+ end
165
+ end
166
+
167
+ end
168
+ end
data/ruby-saml.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
2
+ require 'onelogin/ruby-saml/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'ruby-saml-uppercase'
6
+ s.version = Onelogin::Saml::VERSION
7
+
8
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
9
+ s.authors = ["OneLogin LLC"]
10
+ s.date = Time.now.strftime("%Y-%m-%d")
11
+ s.description = %q{SAML toolkit for Ruby on Rails}
12
+ s.email = %q{support@onelogin.com}
13
+ s.extra_rdoc_files = [
14
+ "LICENSE",
15
+ "README.md"
16
+ ]
17
+ s.files = `git ls-files`.split("\n")
18
+ s.homepage = %q{http://github.com/onelogin/ruby-saml}
19
+ s.rubyforge_project = %q{http://www.rubygems.org/gems/ruby-saml}
20
+ s.rdoc_options = ["--charset=UTF-8"]
21
+ s.require_paths = ["lib"]
22
+ s.rubygems_version = %q{1.3.7}
23
+ s.summary = %q{SAML Ruby Tookit}
24
+ s.test_files = `git ls-files test/*`.split("\n")
25
+
26
+ s.add_runtime_dependency("canonix", ["0.1.1"])
27
+ s.add_runtime_dependency("uuid", ["~> 2.3"])
28
+ s.add_runtime_dependency("nokogiri")
29
+ end
@@ -0,0 +1,12 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIBrTCCAaGgAwIBAgIBATADBgEAMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD
3
+ YWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxv
4
+ Z2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMTAxMTIxMTUxMloX
5
+ DTE1MTAxMTIxMTUxMlowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju
6
+ aWExFTATBgNVBAcMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAX
7
+ BgNVBAMMEGFwcC5vbmVsb2dpbi5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ
8
+ AoGBAMPmjfjy7L35oDpeBXBoRVCgktPkLno9DOEWB7MgYMMVKs2B6ymWQLEWrDug
9
+ MK1hkzWFhIb5fqWLGbWy0J0veGR9/gHOQG+rD/I36xAXnkdiXXhzoiAG/zQxM0ed
10
+ MOUf40n314FC8moErcUg6QabttzesO59HFz6shPuxcWaVAgxAgMBAAEwAwYBAAMB
11
+ AA==
12
+ -----END CERTIFICATE-----
@@ -0,0 +1,111 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ class RequestTest < Test::Unit::TestCase
4
+
5
+ context "Logoutrequest" do
6
+ settings = Onelogin::Saml::Settings.new
7
+
8
+ should "create the deflated SAMLRequest URL parameter" do
9
+ settings.idp_slo_target_url = "http://unauth.com/logout"
10
+ settings.name_identifier_value = "f00f00"
11
+
12
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
13
+ assert unauth_url =~ /^http:\/\/unauth\.com\/logout\?SAMLRequest=/
14
+
15
+ inflated = decode_saml_request_payload(unauth_url)
16
+
17
+ assert_match /^<samlp:LogoutRequest/, inflated
18
+ end
19
+
20
+ should "support additional params" do
21
+
22
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :hello => nil })
23
+ assert unauth_url =~ /&hello=$/
24
+
25
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :foo => "bar" })
26
+ assert unauth_url =~ /&foo=bar$/
27
+ end
28
+
29
+ should "set sessionindex" do
30
+ settings.idp_slo_target_url = "http://example.com"
31
+ sessionidx = UUID.new.generate
32
+ settings.sessionindex = sessionidx
33
+
34
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :name_id => "there" })
35
+ inflated = decode_saml_request_payload(unauth_url)
36
+
37
+ assert_match /<samlp:SessionIndex/, inflated
38
+ assert_match %r(#{sessionidx}</samlp:SessionIndex>), inflated
39
+ end
40
+
41
+ should "set name_identifier_value" do
42
+ settings = Onelogin::Saml::Settings.new
43
+ settings.idp_slo_target_url = "http://example.com"
44
+ settings.name_identifier_format = "transient"
45
+ name_identifier_value = "abc123"
46
+ settings.name_identifier_value = name_identifier_value
47
+
48
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings, { :name_id => "there" })
49
+ inflated = decode_saml_request_payload(unauth_url)
50
+
51
+ assert_match /<saml:NameID/, inflated
52
+ assert_match %r(#{name_identifier_value}</saml:NameID>), inflated
53
+ end
54
+
55
+ should "require name_identifier_value" do
56
+ settings = Onelogin::Saml::Settings.new
57
+ settings.idp_slo_target_url = "http://example.com"
58
+ settings.name_identifier_format = nil
59
+
60
+ assert_raises(Onelogin::Saml::ValidationError) { Onelogin::Saml::Logoutrequest.new.create(settings) }
61
+ end
62
+
63
+ context "when the target url doesn't contain a query string" do
64
+ should "create the SAMLRequest parameter correctly" do
65
+ settings = Onelogin::Saml::Settings.new
66
+ settings.idp_slo_target_url = "http://example.com"
67
+ settings.name_identifier_value = "f00f00"
68
+
69
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
70
+ assert unauth_url =~ /^http:\/\/example.com\?SAMLRequest/
71
+ end
72
+ end
73
+
74
+ context "when the target url contains a query string" do
75
+ should "create the SAMLRequest parameter correctly" do
76
+ settings = Onelogin::Saml::Settings.new
77
+ settings.idp_slo_target_url = "http://example.com?field=value"
78
+ settings.name_identifier_value = "f00f00"
79
+
80
+ unauth_url = Onelogin::Saml::Logoutrequest.new.create(settings)
81
+ assert unauth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
82
+ end
83
+ end
84
+
85
+ context "consumation of logout may need to track the transaction" do
86
+ should "have access to the request uuid" do
87
+ settings = Onelogin::Saml::Settings.new
88
+ settings.idp_slo_target_url = "http://example.com?field=value"
89
+ settings.name_identifier_value = "f00f00"
90
+
91
+ unauth_req = Onelogin::Saml::Logoutrequest.new
92
+ unauth_url = unauth_req.create(settings)
93
+
94
+ inflated = decode_saml_request_payload(unauth_url)
95
+ assert_match %r[ID='#{unauth_req.uuid}'], inflated
96
+ end
97
+ end
98
+ end
99
+
100
+ def decode_saml_request_payload(unauth_url)
101
+ payload = CGI.unescape(unauth_url.split("SAMLRequest=").last)
102
+ decoded = Base64.decode64(payload)
103
+
104
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
105
+ inflated = zstream.inflate(decoded)
106
+ zstream.finish
107
+ zstream.close
108
+ inflated
109
+ end
110
+
111
+ end
@@ -0,0 +1,116 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+ require 'rexml/document'
3
+ require 'responses/logoutresponse_fixtures'
4
+ class RubySamlTest < Test::Unit::TestCase
5
+
6
+ context "Logoutresponse" do
7
+ context "#new" do
8
+ should "raise an exception when response is initialized with nil" do
9
+ assert_raises(ArgumentError) { Onelogin::Saml::Logoutresponse.new(nil) }
10
+ end
11
+ should "default to empty settings" do
12
+ logoutresponse = Onelogin::Saml::Logoutresponse.new( valid_response)
13
+ assert logoutresponse.settings.nil?
14
+ end
15
+ should "accept constructor-injected settings" do
16
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, settings)
17
+ assert !logoutresponse.settings.nil?
18
+ end
19
+ should "accept constructor-injected options" do
20
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, nil, { :foo => :bar} )
21
+ assert !logoutresponse.options.empty?
22
+ end
23
+ should "support base64 encoded responses" do
24
+ expected_response = valid_response
25
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(Base64.encode64(expected_response), settings)
26
+
27
+ assert_equal expected_response, logoutresponse.response
28
+ end
29
+ end
30
+
31
+ context "#validate" do
32
+ should "validate the response" do
33
+ in_relation_to_request_id = random_id
34
+
35
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
36
+
37
+ assert logoutresponse.validate
38
+
39
+ assert_equal settings.issuer, logoutresponse.issuer
40
+ assert_equal in_relation_to_request_id, logoutresponse.in_response_to
41
+
42
+ assert logoutresponse.success?
43
+ end
44
+
45
+ should "invalidate responses with wrong id when given option :matches_uuid" do
46
+
47
+ expected_request_id = "_some_other_expected_uuid"
48
+ opts = { :matches_request_id => expected_request_id}
49
+
50
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, settings, opts)
51
+
52
+ assert !logoutresponse.validate
53
+ assert_not_equal expected_request_id, logoutresponse.in_response_to
54
+ end
55
+
56
+ should "invalidate responses with wrong request status" do
57
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, settings)
58
+
59
+ assert !logoutresponse.validate
60
+ assert !logoutresponse.success?
61
+ end
62
+ end
63
+
64
+ context "#validate!" do
65
+ should "validates good responses" do
66
+ in_relation_to_request_id = random_id
67
+
68
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:uuid => in_relation_to_request_id}), settings)
69
+
70
+ logoutresponse.validate!
71
+ end
72
+
73
+ should "raises validation error when matching for wrong request id" do
74
+
75
+ expected_request_id = "_some_other_expected_id"
76
+ opts = { :matches_request_id => expected_request_id}
77
+
78
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response, settings, opts)
79
+
80
+ assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! }
81
+ end
82
+
83
+ should "raise validation error for wrong request status" do
84
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, settings)
85
+
86
+ assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! }
87
+ end
88
+
89
+ should "raise validation error when in bad state" do
90
+ # no settings
91
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response)
92
+ assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! }
93
+ end
94
+
95
+ should "raise validation error when in lack of issuer setting" do
96
+ bad_settings = settings
97
+ bad_settings.issuer = nil
98
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(unsuccessful_response, bad_settings)
99
+ assert_raises(Onelogin::Saml::ValidationError) { logoutresponse.validate! }
100
+ end
101
+
102
+ should "raise error for invalid xml" do
103
+ logoutresponse = Onelogin::Saml::Logoutresponse.new(invalid_xml_response, settings)
104
+
105
+ assert_raises(Exception) { logoutresponse.validate! }
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ # logoutresponse fixtures
112
+ def random_id
113
+ "_#{UUID.new.generate}"
114
+ end
115
+
116
+ end
@@ -0,0 +1,53 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), "test_helper"))
2
+
3
+ class RequestTest < Test::Unit::TestCase
4
+
5
+ context "Authrequest" do
6
+ should "create the deflated SAMLRequest URL parameter" do
7
+ settings = Onelogin::Saml::Settings.new
8
+ settings.idp_sso_target_url = "http://example.com"
9
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings)
10
+ assert auth_url =~ /^http:\/\/example\.com\?SAMLRequest=/
11
+ payload = CGI.unescape(auth_url.split("=").last)
12
+ decoded = Base64.decode64(payload)
13
+
14
+ zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS)
15
+ inflated = zstream.inflate(decoded)
16
+ zstream.finish
17
+ zstream.close
18
+
19
+ assert_match /^<samlp:AuthnRequest/, inflated
20
+ end
21
+
22
+ should "accept extra parameters" do
23
+ settings = Onelogin::Saml::Settings.new
24
+ settings.idp_sso_target_url = "http://example.com"
25
+
26
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => "there" })
27
+ assert auth_url =~ /&hello=there$/
28
+
29
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings, { :hello => nil })
30
+ assert auth_url =~ /&hello=$/
31
+ end
32
+
33
+ context "when the target url doesn't contain a query string" do
34
+ should "create the SAMLRequest parameter correctly" do
35
+ settings = Onelogin::Saml::Settings.new
36
+ settings.idp_sso_target_url = "http://example.com"
37
+
38
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings)
39
+ assert auth_url =~ /^http:\/\/example.com\?SAMLRequest/
40
+ end
41
+ end
42
+
43
+ context "when the target url contains a query string" do
44
+ should "create the SAMLRequest parameter correctly" do
45
+ settings = Onelogin::Saml::Settings.new
46
+ settings.idp_sso_target_url = "http://example.com?field=value"
47
+
48
+ auth_url = Onelogin::Saml::Authrequest.new.create(settings)
49
+ assert auth_url =~ /^http:\/\/example.com\?field=value&SAMLRequest/
50
+ end
51
+ end
52
+ end
53
+ end