ruby-saml-mod 0.1.22 → 0.1.23
Sign up to get free protection for your applications and to get access to all the features.
- 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
|