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 +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
|