ruby-saml 0.9.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.

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