ruby-saml 0.9.1 → 0.9.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 472b9871eb65dc7e7290cd0eeefbe32299062339
4
- data.tar.gz: 6275d19b96066aa9f17502b4e521b2c3d30626a3
3
+ metadata.gz: 87259e0d311dfd9ad0d9e4a9aa440e10b637f977
4
+ data.tar.gz: 82d80b9439b54b29d2da85b7454b4225070991d5
5
5
  SHA512:
6
- metadata.gz: b2f4796bb5f8ee71f40fdf18f28552038c0c9b6ac85f626452a2614a898f60775fdf482eab044f8643c178da126ea3d640eb3b72427c86e59b935418649ae5b8
7
- data.tar.gz: 96050fa0bfb1451640fe4e1fa799b60fffee4f33622832e9f2a3c0bee20b085c8f99d087b1541d60277de4c4a88c01890206dca8b16c29f4d2dbbe5106b4666f
6
+ metadata.gz: 500dfa98a3746237a3e5c4425e57f7231fd6a9b15efaac4aeaf0ab694d0f3dbf9b8535a4f3c858ecbe87c55e4318f777ce1ce94b31efb8af8238d8097c534b58
7
+ data.tar.gz: 5007fc0c661da41c87ee5132bf96d4e483cfd925fb88afe795c8fde5252fab818daa35ae6a76edc2d096a7a114132c39c6aae3300f335714c0441c0dab81ce27
@@ -4,4 +4,14 @@ rvm:
4
4
  - 1.9.3
5
5
  - 2.0.0
6
6
  - 2.1.5
7
+ - 2.2.0
7
8
  - ree
9
+ gemfile:
10
+ - Gemfile
11
+ - gemfiles/nokogiri-1.5.gemfile
12
+ matrix:
13
+ exclude:
14
+ - rvm: 1.8.7
15
+ gemfile: Gemfile
16
+ - rvm: ree
17
+ gemfile: Gemfile
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # Please keep this file alphabetized and organized
3
3
  #
4
- source 'http://rubygems.org'
4
+ source 'https://rubygems.org'
5
5
 
6
6
  gemspec
data/README.md CHANGED
@@ -18,7 +18,7 @@ We created a demo project for Rails4 that uses the latest version of this librar
18
18
  * 1.8.7
19
19
  * 1.9.x
20
20
  * 2.1.x
21
- * 2.2 (not yet officially supported)
21
+ * 2.2.0
22
22
 
23
23
  ## Adding Features, Pull Requests
24
24
  * Fork the repository
@@ -41,7 +41,7 @@ gem 'ruby-saml', '~> 0.9.1'
41
41
  gem 'ruby-saml', :github => 'onelogin/ruby-saml'
42
42
  ```
43
43
 
44
- Using Bundler
44
+ Using RubyGems
45
45
 
46
46
  ```sh
47
47
  gem install ruby-saml
@@ -58,6 +58,22 @@ or just the required components individually:
58
58
  require 'onelogin/ruby-saml/authrequest'
59
59
  ```
60
60
 
61
+ ### Installation on Ruby 1.8.7
62
+
63
+ This gem has a dependency on Nokogiri, which dropped support for Ruby 1.8.x in Nokogiri 1.6. When installing this gem on Ruby 1.8.7, you will need to make sure a version of Nokogiri prior to 1.6 is installed or specified if it hasn't been already.
64
+
65
+ Using `Gemfile`
66
+
67
+ ```ruby
68
+ gem 'nokogiri', '~> 1.5.10'
69
+ ```
70
+
71
+ Using RubyGems
72
+
73
+ ```sh
74
+ gem install nokogiri --version '~> 1.5.10'
75
+ ````
76
+
61
77
  ## The Initialization Phase
62
78
 
63
79
  This is the first request you will get from the identity provider. It will hit your application at a specific URL (that you've announced as being your SAML initialization point). The response to this initialization, is a redirect back to the identity provider, which can look something like this (ignore the saml_settings method call for now):
@@ -100,6 +116,7 @@ def saml_settings
100
116
  settings.idp_sso_target_url = "https://app.onelogin.com/trust/saml2/http-post/sso/#{OneLoginAppId}"
101
117
  settings.idp_slo_target_url = "https://app.onelogin.com/trust/saml2/http-redirect/slo/#{OneLoginAppId}"
102
118
  settings.idp_cert_fingerprint = OneLoginAppCertFingerPrint
119
+ settings.idp_cert_fingerprint_algorithm = "http://www.w3.org/2000/09/xmldsig#sha1"
103
120
  settings.name_identifier_format = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"
104
121
 
105
122
  # Optional for most SAML IdPs
@@ -192,7 +209,7 @@ The following attributes are set:
192
209
  * idp_slo_target_url
193
210
  * id_cert_fingerpint
194
211
 
195
- If are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It contains all the saml:AttributeStatement with its 'Name' as a indifferent key the one/more saml:AttributeValue as value. The value returned depends on the value of the
212
+ If you are using saml:AttributeStatement to transfer metadata, like the user name, you can access all the attributes through response.attributes. It contains all the saml:AttributeStatement with its 'Name' as a indifferent key the one/more saml:AttributeValue as value. The value returned depends on the value of the
196
213
  `single_value_compatibility` (when activate, only one value returned, the first one)
197
214
 
198
215
  ```ruby
@@ -320,16 +337,23 @@ In order to be able to sign we need first to define the private key and the publ
320
337
  The settings related to sign are stored in the `security` attribute of the settings:
321
338
 
322
339
  ```ruby
323
- settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
324
- settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
340
+ settings.security[:authn_requests_signed] = true # Enable or not signature on AuthNRequest
341
+ settings.security[:logout_requests_signed] = true # Enable or not signature on Logout Request
325
342
  settings.security[:logout_responses_signed] = true # Enable or not signature on Logout Response
343
+ settings.security[:metadata_signed] = true # Enable or not signature on Metadata
326
344
 
327
345
  settings.security[:digest_method] = XMLSecurity::Document::SHA1
328
- settings.security[:signature_method] = XMLSecurity::Document::SHA1
346
+ settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
329
347
 
330
- settings.security[:embed_sign] = false # Embeded signature or HTTP GET parameter Signature
348
+ # Embeded signature or HTTP GET parameter signature
349
+ # Note that metadata signature is always embedded regardless of this value.
350
+ settings.security[:embed_sign] = false
331
351
  ```
332
352
 
353
+ Notice that the RelayState parameter is used when creating the Signature on the HTTP-Redirect Binding,
354
+ remember to provide it to the Signature builder if you are sending a GET RelayState parameter or
355
+ Signature validation process will fail at the Identity Provider.
356
+
333
357
 
334
358
  ## Single Log Out
335
359
 
@@ -1,4 +1,22 @@
1
1
  # RubySaml Changelog
2
+ ### 0.9.2 (Apr 28, 2015)
3
+ * [#216](https://github.com/onelogin/ruby-saml/pull/216) Add fingerprint algorithm support
4
+ * [#218](https://github.com/onelogin/ruby-saml/pull/218) Update README.md
5
+ * [#214](https://github.com/onelogin/ruby-saml/pull/214) Cleanup `SamlMessage` class
6
+ * [#213](https://github.com/onelogin/ruby-saml/pull/213) Add ability to sign metadata. (Improved)
7
+ * [#212](https://github.com/onelogin/ruby-saml/pull/212) Rename library entry point
8
+ * [#210](https://github.com/onelogin/ruby-saml/pull/210) Call assert in tests
9
+ * [#208](https://github.com/onelogin/ruby-saml/pull/208) Update tests and CI for Ruby 2.2.0
10
+ * [#205](https://github.com/onelogin/ruby-saml/pull/205) Allow requirement of single files
11
+ * [#204](https://github.com/onelogin/ruby-saml/pull/204) Require ‘net/http’ library
12
+ * [#201](https://github.com/onelogin/ruby-saml/pull/201) Freeze and duplicate default security settings hash so that it doesn't get modified.
13
+ * [#200](https://github.com/onelogin/ruby-saml/pull/200) Set default SSL certificate store in Ruby 1.8.
14
+ * [#199](https://github.com/onelogin/ruby-saml/pull/199) Change Nokogiri's runtime dependency to fix support for Ruby 1.8.7.
15
+ * [#179](https://github.com/onelogin/ruby-saml/pull/179) Add support for setting the entity ID and name ID format when parsing metadata
16
+ * [#175](https://github.com/onelogin/ruby-saml/pull/175) Introduce thread safety to SAML schema validation
17
+ * [#171](https://github.com/onelogin/ruby-saml/pull/171) Fix inconsistent results with using regex matches in decode_raw_saml
18
+ *
19
+
2
20
  ### 0.9.1 (Feb 10, 2015)
3
21
  * [#194](https://github.com/onelogin/ruby-saml/pull/194) Relax nokogiri gem requirements
4
22
  * [#191](https://github.com/onelogin/ruby-saml/pull/191) Use Minitest instead of Test::Unit
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem "nokogiri", "~> 1.5.10"
4
+
5
+ gemspec :path => "../"
@@ -0,0 +1,16 @@
1
+ require 'onelogin/ruby-saml/logging'
2
+ require 'onelogin/ruby-saml/saml_message'
3
+ require 'onelogin/ruby-saml/authrequest'
4
+ require 'onelogin/ruby-saml/logoutrequest'
5
+ require 'onelogin/ruby-saml/logoutresponse'
6
+ require 'onelogin/ruby-saml/attributes'
7
+ require 'onelogin/ruby-saml/slo_logoutrequest'
8
+ require 'onelogin/ruby-saml/slo_logoutresponse'
9
+ require 'onelogin/ruby-saml/response'
10
+ require 'onelogin/ruby-saml/settings'
11
+ require 'onelogin/ruby-saml/attribute_service'
12
+ require 'onelogin/ruby-saml/validation_error'
13
+ require 'onelogin/ruby-saml/metadata'
14
+ require 'onelogin/ruby-saml/idp_metadata_parser'
15
+ require 'onelogin/ruby-saml/utils'
16
+ require 'onelogin/ruby-saml/version'
@@ -1,6 +1,8 @@
1
1
  require "uuid"
2
+ require "rexml/document"
2
3
 
3
4
  require "onelogin/ruby-saml/logging"
5
+ require "onelogin/ruby-saml/saml_message"
4
6
 
5
7
  module OneLogin
6
8
  module RubySaml
@@ -25,7 +27,10 @@ module OneLogin
25
27
  end
26
28
 
27
29
  def create_params(settings, params={})
28
- params = {} if params.nil?
30
+ # The method expects :RelayState but sometimes we get 'RelayState' instead.
31
+ # Based on the HashWithIndifferentAccess value in Rails we could experience
32
+ # conflicts so this line will solve them.
33
+ relay_state = params[:RelayState] || params['RelayState']
29
34
 
30
35
  request_doc = create_authentication_xml_doc(settings)
31
36
  request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
@@ -40,10 +45,10 @@ module OneLogin
40
45
  request_params = {"SAMLRequest" => base64_request}
41
46
 
42
47
  if settings.security[:authn_requests_signed] && !settings.security[:embed_sign] && settings.private_key
43
- params['SigAlg'] = XMLSecurity::Document::SHA1
48
+ params['SigAlg'] = settings.security[:signature_method]
44
49
  url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
45
- url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState']
46
- url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}"
50
+ url_string << "&RelayState=#{CGI.escape(relay_state)}" if relay_state
51
+ url_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
47
52
  private_key = settings.get_sp_key()
48
53
  signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string)
49
54
  params['Signature'] = encode(signature)
@@ -111,7 +116,7 @@ module OneLogin
111
116
  end
112
117
  end
113
118
 
114
- # embebed sign
119
+ # embed signature
115
120
  if settings.security[:authn_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
116
121
  private_key = settings.get_sp_key()
117
122
  cert = settings.get_sp_cert()
@@ -2,6 +2,8 @@ require "base64"
2
2
  require "uuid"
3
3
  require "zlib"
4
4
  require "cgi"
5
+ require "net/http"
6
+ require "net/https"
5
7
  require "rexml/document"
6
8
  require "rexml/xpath"
7
9
 
@@ -25,7 +27,8 @@ module OneLogin
25
27
  @document = REXML::Document.new(idp_metadata)
26
28
 
27
29
  OneLogin::RubySaml::Settings.new.tap do |settings|
28
-
30
+ settings.idp_entity_id = idp_entity_id
31
+ settings.name_identifier_format = idp_name_id_format
29
32
  settings.idp_sso_target_url = single_signon_service_url
30
33
  settings.idp_slo_target_url = single_logout_service_url
31
34
  settings.idp_cert_fingerprint = fingerprint
@@ -47,6 +50,12 @@ module OneLogin
47
50
  # Most IdPs will probably use self signed certs
48
51
  if validate_cert
49
52
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
53
+
54
+ # Net::HTTP in Ruby 1.8 did not set the default certificate store
55
+ # automatically when VERIFY_PEER was specified.
56
+ if RUBY_VERSION < '1.9' && !http.ca_file && !http.ca_path && !http.cert_store
57
+ http.cert_store = OpenSSL::SSL::SSLContext::DEFAULT_CERT_STORE
58
+ end
50
59
  else
51
60
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
52
61
  end
@@ -57,6 +66,16 @@ module OneLogin
57
66
  meta_text
58
67
  end
59
68
 
69
+ def idp_entity_id
70
+ node = REXML::XPath.first(document, "/md:EntityDescriptor/@entityID", { "md" => METADATA })
71
+ node.value if node
72
+ end
73
+
74
+ def idp_name_id_format
75
+ node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:NameIDFormat", { "md" => METADATA })
76
+ node.text if node
77
+ end
78
+
60
79
  def single_signon_service_url
61
80
  node = REXML::XPath.first(document, "/md:EntityDescriptor/md:IDPSSODescriptor/md:SingleSignOnService/@Location", { "md" => METADATA })
62
81
  node.value if node
@@ -1,6 +1,7 @@
1
1
  require "uuid"
2
2
 
3
3
  require "onelogin/ruby-saml/logging"
4
+ require "onelogin/ruby-saml/saml_message"
4
5
 
5
6
  module OneLogin
6
7
  module RubySaml
@@ -24,7 +25,10 @@ module OneLogin
24
25
  end
25
26
 
26
27
  def create_params(settings, params={})
27
- params = {} if params.nil?
28
+ # The method expects :RelayState but sometimes we get 'RelayState' instead.
29
+ # Based on the HashWithIndifferentAccess value in Rails we could experience
30
+ # conflicts so this line will solve them.
31
+ relay_state = params[:RelayState] || params['RelayState']
28
32
 
29
33
  request_doc = create_logout_request_xml_doc(settings)
30
34
  request_doc.context[:attribute_quote] = :quote if settings.double_quote_xml_attribute_values
@@ -39,10 +43,10 @@ module OneLogin
39
43
  request_params = {"SAMLRequest" => base64_request}
40
44
 
41
45
  if settings.security[:logout_requests_signed] && !settings.security[:embed_sign] && settings.private_key
42
- params['SigAlg'] = XMLSecurity::Document::SHA1
46
+ params['SigAlg'] = settings.security[:signature_method]
43
47
  url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
44
- url_string += "&RelayState=#{CGI.escape(params['RelayState'])}" if params['RelayState']
45
- url_string += "&SigAlg=#{CGI.escape(params['SigAlg'])}"
48
+ url_string << "&RelayState=#{CGI.escape(relay_state)}" if relay_state
49
+ url_string << "&SigAlg=#{CGI.escape(params['SigAlg'])}"
46
50
  private_key = settings.get_sp_key()
47
51
  signature = private_key.sign(XMLSecurity::BaseDocument.new.algorithm(settings.security[:signature_method]).new, url_string)
48
52
  params['Signature'] = encode(signature)
@@ -88,7 +92,7 @@ module OneLogin
88
92
  sessionindex.text = settings.sessionindex
89
93
  end
90
94
 
91
- # embebed sign
95
+ # embed signature
92
96
  if settings.security[:logout_requests_signed] && settings.private_key && settings.certificate && settings.security[:embed_sign]
93
97
  private_key = settings.get_sp_key()
94
98
  cert = settings.get_sp_cert()
@@ -1,4 +1,6 @@
1
1
  require "xml_security"
2
+ require "onelogin/ruby-saml/saml_message"
3
+
2
4
  require "time"
3
5
 
4
6
  module OneLogin
@@ -1,6 +1,5 @@
1
- require "rexml/document"
2
- require "rexml/xpath"
3
1
  require "uri"
2
+ require "uuid"
4
3
 
5
4
  require "onelogin/ruby-saml/logging"
6
5
 
@@ -10,10 +9,9 @@ require "onelogin/ruby-saml/logging"
10
9
  # will be updated automatically
11
10
  module OneLogin
12
11
  module RubySaml
13
- include REXML
14
12
  class Metadata
15
- def generate(settings)
16
- meta_doc = REXML::Document.new
13
+ def generate(settings, pretty_print=true)
14
+ meta_doc = XMLSecurity::Document.new
17
15
  root = meta_doc.add_element "md:EntityDescriptor", {
18
16
  "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata"
19
17
  }
@@ -23,6 +21,7 @@ module OneLogin
23
21
  # However we would like assertions signed if idp_cert_fingerprint or idp_cert is set
24
22
  "WantAssertionsSigned" => !!(settings.idp_cert_fingerprint || settings.idp_cert)
25
23
  }
24
+ root.attributes["ID"] = "_" + UUID.new.generate
26
25
  if settings.issuer
27
26
  root.attributes["entityID"] = settings.issuer
28
27
  end
@@ -85,9 +84,20 @@ module OneLogin
85
84
  # <md:XACMLAuthzDecisionQueryDescriptor WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"/>
86
85
 
87
86
  meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
87
+
88
+ # embed signature
89
+ if settings.security[:metadata_signed] && settings.private_key && settings.certificate
90
+ private_key = settings.get_sp_key()
91
+ meta_doc.sign_document(private_key, cert, settings.security[:signature_method], settings.security[:digest_method])
92
+ end
93
+
88
94
  ret = ""
89
95
  # pretty print the XML so IdP administrators can easily see what the SP supports
90
- meta_doc.write(ret, 1)
96
+ if pretty_print
97
+ meta_doc.write(ret, 1)
98
+ else
99
+ ret = meta_doc.to_s
100
+ end
91
101
 
92
102
  return ret
93
103
  end
@@ -1,4 +1,6 @@
1
1
  require "xml_security"
2
+ require "onelogin/ruby-saml/attributes"
3
+
2
4
  require "time"
3
5
  require "nokogiri"
4
6
 
@@ -138,7 +140,7 @@ module OneLogin
138
140
  validate_response_state(soft) &&
139
141
  validate_conditions(soft) &&
140
142
  validate_issuer(soft) &&
141
- document.validate_document(get_fingerprint, soft) &&
143
+ document.validate_document(get_fingerprint, soft, :fingerprint_alg => settings.idp_cert_fingerprint_algorithm) &&
142
144
  validate_success_status(soft)
143
145
  end
144
146
 
@@ -151,19 +153,18 @@ module OneLogin
151
153
  end
152
154
 
153
155
  def validate_structure(soft = true)
154
- Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
155
- @schema = Nokogiri::XML::Schema(IO.read('saml-schema-protocol-2.0.xsd'))
156
- @xml = Nokogiri::XML(self.document.to_s)
157
- end
158
- if soft
159
- @schema.validate(@xml).map{
160
- @errors << "Schema validation failed";
161
- return false
162
- }
163
- else
164
- @schema.validate(@xml).map{ |error| @errors << "#{error.message}\n\n#{@xml.to_s}";
165
- validation_error("#{error.message}\n\n#{@xml.to_s}")
166
- }
156
+ xml = Nokogiri::XML(self.document.to_s)
157
+
158
+ SamlMessage.schema.validate(xml).map do |error|
159
+ if soft
160
+ @errors << "Schema validation failed"
161
+ break false
162
+ else
163
+ error_message = [error.message, xml.to_s].join("\n\n")
164
+
165
+ @errors << error_message
166
+ validation_error(error_message)
167
+ end
167
168
  end
168
169
  end
169
170
 
@@ -202,7 +203,8 @@ module OneLogin
202
203
  def get_fingerprint
203
204
  if settings.idp_cert
204
205
  cert = OpenSSL::X509::Certificate.new(settings.idp_cert)
205
- Digest::SHA1.hexdigest(cert.to_der).upcase.scan(/../).join(":")
206
+ fingerprint_alg = XMLSecurity::BaseDocument.new.algorithm(settings.idp_cert_fingerprint_algorithm).new
207
+ fingerprint_alg.hexdigest(cert.to_der).upcase.scan(/../).join(":")
206
208
  else
207
209
  settings.idp_cert_fingerprint
208
210
  end
@@ -1,8 +1,10 @@
1
1
  require 'cgi'
2
2
  require 'zlib'
3
3
  require 'base64'
4
- require "rexml/document"
5
- require "rexml/xpath"
4
+ require 'nokogiri'
5
+ require 'rexml/document'
6
+ require 'rexml/xpath'
7
+ require 'thread'
6
8
 
7
9
  module OneLogin
8
10
  module RubySaml
@@ -12,15 +14,22 @@ module OneLogin
12
14
  ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
13
15
  PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
14
16
 
15
- def valid_saml?(document, soft = true)
16
- Dir.chdir(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'schemas'))) do
17
- @schema = Nokogiri::XML::Schema(IO.read('saml-schema-protocol-2.0.xsd'))
18
- @xml = Nokogiri::XML(document.to_s)
17
+ BASE64_FORMAT = %r(\A[A-Za-z0-9+/]{4}*[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=?\Z)
18
+
19
+ def self.schema
20
+ @schema ||= Mutex.new.synchronize do
21
+ Dir.chdir(File.expand_path("../../../schemas", __FILE__)) do
22
+ ::Nokogiri::XML::Schema(File.read("saml-schema-protocol-2.0.xsd"))
23
+ end
19
24
  end
20
- if soft
21
- @schema.validate(@xml).map{ return false }
22
- else
23
- @schema.validate(@xml).map{ |error| validation_error("#{error.message}\n\n#{@xml.to_s}") }
25
+ end
26
+
27
+ def valid_saml?(document, soft = true)
28
+ xml = Nokogiri::XML(document.to_s)
29
+
30
+ SamlMessage.schema.validate(xml).map do |error|
31
+ break false if soft
32
+ validation_error("#{error.message}\n\n#{xml.to_s}")
24
33
  end
25
34
  end
26
35
 
@@ -30,22 +39,29 @@ module OneLogin
30
39
 
31
40
  private
32
41
 
42
+ ##
43
+ # Take a SAML object provided by +saml+, determine its status and return
44
+ # a decoded XML as a String.
45
+ #
46
+ # Since SAML decided to use the RFC1951 and therefor has no zlib markers,
47
+ # the only reliable method of deciding whether we have a zlib stream or not
48
+ # is to try and inflate it and fall back to the base64 decoded string if
49
+ # the stream contains errors.
33
50
  def decode_raw_saml(saml)
34
- if saml =~ /^</
35
- return saml
36
- elsif (decoded = decode(saml)) =~ /^</
37
- return decoded
38
- elsif (inflated = inflate(decoded)) =~ /^</
39
- return inflated
40
- end
51
+ return saml unless base64_encoded?(saml)
41
52
 
42
- return nil
53
+ decoded = decode(saml)
54
+ begin
55
+ inflate(decoded)
56
+ rescue
57
+ decoded
58
+ end
43
59
  end
44
60
 
45
61
  def encode_raw_saml(saml, settings)
46
- saml = Zlib::Deflate.deflate(saml, 9)[2..-5] if settings.compress_request
47
- base64_saml = Base64.encode64(saml)
48
- return CGI.escape(base64_saml)
62
+ saml = deflate(saml) if settings.compress_request
63
+
64
+ CGI.escape(Base64.encode64(saml))
49
65
  end
50
66
 
51
67
  def decode(encoded)
@@ -56,23 +72,21 @@ module OneLogin
56
72
  Base64.encode64(encoded).gsub(/\n/, "")
57
73
  end
58
74
 
59
- def escape(unescaped)
60
- CGI.escape(unescaped)
61
- end
62
-
63
- def unescape(escaped)
64
- CGI.unescape(escaped)
75
+ # Check if a string is base64 encoded
76
+ #
77
+ # @param string [String] string to check the encoding of
78
+ # @return [true, false] whether or not the string is base64 encoded
79
+ def base64_encoded?(string)
80
+ !!string.gsub(/[\r\n]|\\r|\\n/, "").match(BASE64_FORMAT)
65
81
  end
66
82
 
67
83
  def inflate(deflated)
68
- zlib = Zlib::Inflate.new(-Zlib::MAX_WBITS)
69
- zlib.inflate(deflated)
84
+ Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(deflated)
70
85
  end
71
86
 
72
87
  def deflate(inflated)
73
88
  Zlib::Deflate.deflate(inflated, 9)[2..-5]
74
89
  end
75
-
76
90
  end
77
91
  end
78
92
  end