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 +4 -4
- data/.travis.yml +10 -0
- data/Gemfile +1 -1
- data/README.md +31 -7
- data/changelog.md +18 -0
- data/gemfiles/nokogiri-1.5.gemfile +5 -0
- data/lib/onelogin/ruby-saml.rb +16 -0
- data/lib/onelogin/ruby-saml/authrequest.rb +10 -5
- data/lib/onelogin/ruby-saml/idp_metadata_parser.rb +20 -1
- data/lib/onelogin/ruby-saml/logoutrequest.rb +9 -5
- data/lib/onelogin/ruby-saml/logoutresponse.rb +2 -0
- data/lib/onelogin/ruby-saml/metadata.rb +16 -6
- data/lib/onelogin/ruby-saml/response.rb +17 -15
- data/lib/onelogin/ruby-saml/saml_message.rb +44 -30
- data/lib/onelogin/ruby-saml/settings.rb +17 -7
- data/lib/onelogin/ruby-saml/slo_logoutrequest.rb +2 -0
- data/lib/onelogin/ruby-saml/slo_logoutresponse.rb +9 -5
- data/lib/onelogin/ruby-saml/version.rb +1 -1
- data/lib/ruby-saml.rb +1 -16
- data/lib/xml_security.rb +39 -17
- data/ruby-saml.gemspec +5 -6
- data/test/idp_metadata_parser_test.rb +6 -2
- data/test/logoutrequest_test.rb +50 -22
- data/test/logoutresponse_test.rb +2 -1
- data/test/metadata_test.rb +100 -55
- data/test/request_test.rb +49 -24
- data/test/response_test.rb +41 -8
- data/test/responses/test_sign.xml +43 -0
- data/test/settings_test.rb +16 -0
- data/test/slo_logoutrequest_test.rb +2 -0
- data/test/slo_logoutresponse_test.rb +51 -21
- data/test/test_helper.rb +0 -1
- data/test/xml_security_test.rb +41 -4
- metadata +10 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 87259e0d311dfd9ad0d9e4a9aa440e10b637f977
|
4
|
+
data.tar.gz: 82d80b9439b54b29d2da85b7454b4225070991d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 500dfa98a3746237a3e5c4425e57f7231fd6a9b15efaac4aeaf0ab694d0f3dbf9b8535a4f3c858ecbe87c55e4318f777ce1ce94b31efb8af8238d8097c534b58
|
7
|
+
data.tar.gz: 5007fc0c661da41c87ee5132bf96d4e483cfd925fb88afe795c8fde5252fab818daa35ae6a76edc2d096a7a114132c39c6aae3300f335714c0441c0dab81ce27
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
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
|
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
|
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]
|
324
|
-
settings.security[:logout_requests_signed]
|
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::
|
346
|
+
settings.security[:signature_method] = XMLSecurity::Document::RSA_SHA1
|
329
347
|
|
330
|
-
|
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
|
|
data/changelog.md
CHANGED
@@ -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,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
|
-
|
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'] =
|
48
|
+
params['SigAlg'] = settings.security[:signature_method]
|
44
49
|
url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
|
45
|
-
url_string
|
46
|
-
url_string
|
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
|
-
#
|
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
|
-
|
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'] =
|
46
|
+
params['SigAlg'] = settings.security[:signature_method]
|
43
47
|
url_string = "SAMLRequest=#{CGI.escape(base64_request)}"
|
44
|
-
url_string
|
45
|
-
url_string
|
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
|
-
#
|
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,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 =
|
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
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
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
|
-
|
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
|
5
|
-
require
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
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
|
-
|
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
|
47
|
-
|
48
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
def
|
64
|
-
|
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
|
-
|
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
|