ruby-saml-mod 0.1.22 → 0.1.23
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.
- data/lib/onelogin/saml/response.rb +72 -32
- data/lib/xml_sec.rb +23 -0
- data/ruby-saml-mod.gemspec +2 -2
- metadata +2 -2
@@ -2,7 +2,7 @@ module Onelogin::Saml
|
|
2
2
|
class Response
|
3
3
|
|
4
4
|
attr_accessor :settings
|
5
|
-
attr_reader :document, :
|
5
|
+
attr_reader :document, :xml, :response
|
6
6
|
attr_reader :name_id, :name_qualifier, :session_index, :saml_attributes
|
7
7
|
attr_reader :status_code, :status_message
|
8
8
|
attr_reader :in_response_to, :destination, :issuer
|
@@ -14,14 +14,14 @@ module Onelogin::Saml
|
|
14
14
|
@xml = Base64.decode64(@response)
|
15
15
|
@document = LibXML::XML::Document.string(@xml)
|
16
16
|
@document.extend(XMLSecurity::SignedDocument)
|
17
|
-
rescue
|
17
|
+
rescue
|
18
18
|
# could not parse document, everything is invalid
|
19
19
|
@response = nil
|
20
20
|
return
|
21
21
|
end
|
22
22
|
|
23
|
-
@issuer =
|
24
|
-
@status_code =
|
23
|
+
@issuer = document.find_first("/samlp:Response/saml:Issuer", Onelogin::NAMESPACES).content rescue nil
|
24
|
+
@status_code = document.find_first("/samlp:Response/samlp:Status/samlp:StatusCode", Onelogin::NAMESPACES)["Value"] rescue nil
|
25
25
|
|
26
26
|
process(settings) if settings
|
27
27
|
end
|
@@ -30,79 +30,119 @@ module Onelogin::Saml
|
|
30
30
|
@settings = settings
|
31
31
|
return unless @response
|
32
32
|
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@
|
33
|
+
@in_response_to = untrusted_find_first("/samlp:Response")['InResponseTo'] rescue nil
|
34
|
+
@destination = untrusted_find_first("/samlp:Response")['Destination'] rescue nil
|
35
|
+
@status_message = untrusted_find_first("/samlp:Response/samlp:Status/samlp:StatusCode").content rescue nil
|
36
|
+
|
37
|
+
@name_id = trusted_find_first("saml:Assertion/saml:Subject/saml:NameID").content rescue nil
|
38
|
+
@name_qualifier = trusted_find_first("saml:Assertion/saml:Subject/saml:NameID")["NameQualifier"] rescue nil
|
39
|
+
@session_index = trusted_find_first("saml:Assertion/saml:AuthnStatement")["SessionIndex"] rescue nil
|
36
40
|
|
37
|
-
@in_response_to = @decrypted_document.find_first("/samlp:Response", Onelogin::NAMESPACES)['InResponseTo'] rescue nil
|
38
|
-
@destination = @decrypted_document.find_first("/samlp:Response", Onelogin::NAMESPACES)['Destination'] rescue nil
|
39
|
-
@name_id = @decrypted_document.find_first("/samlp:Response/saml:Assertion/saml:Subject/saml:NameID", Onelogin::NAMESPACES).content rescue nil
|
40
41
|
@saml_attributes = {}
|
41
|
-
|
42
|
+
trusted_find("saml:Attribute").each do |attr|
|
42
43
|
attrname = attr['FriendlyName'] || Onelogin::ATTRIBUTES[attr['Name']] || attr['Name']
|
43
44
|
@saml_attributes[attrname] = attr.content.strip rescue nil
|
44
45
|
end
|
45
|
-
|
46
|
-
|
47
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
def disable_signature_validation!(settings)
|
49
|
+
@settings = settings
|
50
|
+
@is_valid = true
|
51
|
+
@trusted_roots = [decrypted_document.root]
|
52
|
+
end
|
53
|
+
|
54
|
+
def decrypted_document
|
55
|
+
@decrypted_document ||= LibXML::XML::Document.document(document).tap do |doc|
|
56
|
+
doc.extend(XMLSecurity::SignedDocument)
|
57
|
+
doc.decrypt!(settings)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def untrusted_find_first(xpath)
|
62
|
+
decrypted_document.find(xpath, Onelogin::NAMESPACES).first
|
63
|
+
end
|
64
|
+
|
65
|
+
def trusted_find_first(xpath)
|
66
|
+
trusted_find(xpath).first
|
67
|
+
end
|
68
|
+
|
69
|
+
def trusted_find(xpath)
|
70
|
+
trusted_roots.map do |trusted_root|
|
71
|
+
trusted_root.find("descendant-or-self::#{xpath}", Onelogin::NAMESPACES).to_a
|
72
|
+
end.flatten.compact
|
48
73
|
end
|
49
74
|
|
50
75
|
def logger=(val)
|
51
76
|
@logger = val
|
52
77
|
end
|
53
|
-
|
78
|
+
|
54
79
|
def is_valid?
|
55
|
-
|
80
|
+
@is_valid ||= validate
|
81
|
+
end
|
82
|
+
|
83
|
+
def validate
|
84
|
+
if response.nil? || response == ""
|
56
85
|
@validation_error = "No response to validate"
|
57
86
|
return false
|
58
87
|
end
|
59
|
-
|
60
|
-
if
|
88
|
+
|
89
|
+
if !settings.idp_cert_fingerprint
|
61
90
|
@validation_error = "No fingerprint configured in SAML settings"
|
62
91
|
return false
|
63
92
|
end
|
64
|
-
|
93
|
+
|
65
94
|
# Verify the original document if it has a signature, otherwise verify the signature
|
66
95
|
# in the encrypted portion. If there is no signature, then we can't verify.
|
67
96
|
verified = false
|
68
|
-
|
69
|
-
|
97
|
+
|
98
|
+
if document.has_signature?
|
99
|
+
verified = document.validate(settings.idp_cert_fingerprint, @logger)
|
70
100
|
if !verified
|
71
|
-
@validation_error =
|
101
|
+
@validation_error = document.validation_error
|
72
102
|
return false
|
73
103
|
end
|
74
104
|
end
|
75
105
|
|
76
|
-
if !verified &&
|
77
|
-
verified =
|
106
|
+
if !verified && decrypted_document.has_signature?
|
107
|
+
verified = decrypted_document.validate(settings.idp_cert_fingerprint, @logger)
|
78
108
|
if !verified
|
79
|
-
@validation_error =
|
109
|
+
@validation_error = decrypted_document.validation_error
|
80
110
|
return false
|
81
111
|
end
|
82
112
|
end
|
83
|
-
|
113
|
+
|
84
114
|
if !verified
|
85
115
|
@validation_error = "No signature found in the response"
|
86
116
|
return false
|
87
117
|
end
|
88
|
-
|
118
|
+
|
119
|
+
# If we get here, validation has succeeded, and we can trust all
|
120
|
+
# <ds:Signature> elements. Each of those has a <ds:Reference> which
|
121
|
+
# points to the root of the root of the NodeSet it signs.
|
122
|
+
@trusted_roots = decrypted_document.signed_roots
|
123
|
+
|
89
124
|
true
|
90
125
|
end
|
91
|
-
|
126
|
+
|
127
|
+
# triggers validation
|
128
|
+
def trusted_roots
|
129
|
+
is_valid? ? @trusted_roots : []
|
130
|
+
end
|
131
|
+
|
92
132
|
def success_status?
|
93
133
|
@status_code == Onelogin::Saml::StatusCodes::SUCCESS_URI
|
94
134
|
end
|
95
|
-
|
135
|
+
|
96
136
|
def auth_failure?
|
97
137
|
@status_code == Onelogin::Saml::StatusCodes::AUTHN_FAILED_URI
|
98
138
|
end
|
99
|
-
|
139
|
+
|
100
140
|
def no_authn_context?
|
101
141
|
@status_code == Onelogin::Saml::StatusCodes::NO_AUTHN_CONTEXT_URI
|
102
142
|
end
|
103
|
-
|
143
|
+
|
104
144
|
def fingerprint_from_idp
|
105
|
-
if base64_cert =
|
145
|
+
if base64_cert = decrypted_document.find_first("//ds:X509Certificate", Onelogin::NAMESPACES)
|
106
146
|
cert_text = Base64.decode64(base64_cert.content)
|
107
147
|
cert = OpenSSL::X509::Certificate.new(cert_text)
|
108
148
|
Digest::SHA1.hexdigest(cert.to_der)
|
data/lib/xml_sec.rb
CHANGED
@@ -267,6 +267,29 @@ module XMLSecurity
|
|
267
267
|
"-----BEGIN PUBLIC KEY-----\n#{base64}-----END PUBLIC KEY-----"
|
268
268
|
end
|
269
269
|
|
270
|
+
def has_signature?
|
271
|
+
signatures.any?
|
272
|
+
end
|
273
|
+
|
274
|
+
def signed_roots
|
275
|
+
signatures.map do |sig|
|
276
|
+
ref = sig.find('.//ds:Reference', Onelogin::NAMESPACES).first
|
277
|
+
signed_element_id = ref['URI'].sub(/^#/, '')
|
278
|
+
|
279
|
+
if signed_element_id.empty?
|
280
|
+
self.root
|
281
|
+
else
|
282
|
+
xpath_id_query = %Q(ancestor::*[@ID = "#{signed_element_id}"])
|
283
|
+
|
284
|
+
ref.find(xpath_id_query, Onelogin::NAMESPACES).first
|
285
|
+
end
|
286
|
+
end.compact
|
287
|
+
end
|
288
|
+
|
289
|
+
def signatures
|
290
|
+
@signatures ||= self.find("//ds:Signature", Onelogin::NAMESPACES)
|
291
|
+
end
|
292
|
+
|
270
293
|
def validate(idp_cert_fingerprint, logger = nil)
|
271
294
|
# get cert from response
|
272
295
|
base64_cert = self.find_first("//ds:X509Certificate", Onelogin::NAMESPACES).content
|
data/ruby-saml-mod.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = %q{ruby-saml-mod}
|
3
|
-
s.version = "0.1.
|
3
|
+
s.version = "0.1.23"
|
4
4
|
|
5
5
|
s.authors = ["OneLogin LLC", "Bracken", "Zach", "Cody", "Jeremy", "Paul"]
|
6
|
-
s.date = %q{
|
6
|
+
s.date = %q{2014-01-25}
|
7
7
|
s.extra_rdoc_files = [
|
8
8
|
"LICENSE"
|
9
9
|
]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-saml-mod
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.23
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date:
|
17
|
+
date: 2014-01-25 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: libxml-ruby
|