cie-es 0.0.1

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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +19 -0
  5. data/README.md +126 -0
  6. data/Rakefile +41 -0
  7. data/cie-es.gemspec +22 -0
  8. data/lib/cie/ruby-saml/authrequest.rb +205 -0
  9. data/lib/cie/ruby-saml/coding.rb +34 -0
  10. data/lib/cie/ruby-saml/error_handling.rb +27 -0
  11. data/lib/cie/ruby-saml/logging.rb +26 -0
  12. data/lib/cie/ruby-saml/logout_request.rb +126 -0
  13. data/lib/cie/ruby-saml/logout_response.rb +132 -0
  14. data/lib/cie/ruby-saml/metadata.rb +489 -0
  15. data/lib/cie/ruby-saml/request.rb +81 -0
  16. data/lib/cie/ruby-saml/response.rb +678 -0
  17. data/lib/cie/ruby-saml/settings.rb +89 -0
  18. data/lib/cie/ruby-saml/utils.rb +225 -0
  19. data/lib/cie/ruby-saml/validation_error.rb +7 -0
  20. data/lib/cie/ruby-saml/version.rb +5 -0
  21. data/lib/cie/xml_security.rb +166 -0
  22. data/lib/cie/xml_security_new.rb +373 -0
  23. data/lib/cie-es.rb +14 -0
  24. data/lib/schemas/saml20assertion_schema.xsd +283 -0
  25. data/lib/schemas/saml20protocol_schema.xsd +302 -0
  26. data/lib/schemas/xenc_schema.xsd +146 -0
  27. data/lib/schemas/xmldsig_schema.xsd +318 -0
  28. data/test/certificates/certificate1 +12 -0
  29. data/test/logoutrequest_test.rb +98 -0
  30. data/test/request_test.rb +53 -0
  31. data/test/response_test.rb +219 -0
  32. data/test/responses/adfs_response_sha1.xml +46 -0
  33. data/test/responses/adfs_response_sha256.xml +46 -0
  34. data/test/responses/adfs_response_sha384.xml +46 -0
  35. data/test/responses/adfs_response_sha512.xml +46 -0
  36. data/test/responses/no_signature_ns.xml +48 -0
  37. data/test/responses/open_saml_response.xml +56 -0
  38. data/test/responses/response1.xml.base64 +1 -0
  39. data/test/responses/response2.xml.base64 +79 -0
  40. data/test/responses/response3.xml.base64 +66 -0
  41. data/test/responses/response4.xml.base64 +93 -0
  42. data/test/responses/response5.xml.base64 +102 -0
  43. data/test/responses/response_with_ampersands.xml +139 -0
  44. data/test/responses/response_with_ampersands.xml.base64 +93 -0
  45. data/test/responses/simple_saml_php.xml +71 -0
  46. data/test/responses/wrapped_response_2.xml.base64 +150 -0
  47. data/test/settings_test.rb +43 -0
  48. data/test/test_helper.rb +65 -0
  49. data/test/xml_security_test.rb +123 -0
  50. metadata +119 -0
@@ -0,0 +1,132 @@
1
+ #encoding: utf-8
2
+
3
+ require "rexml/document"
4
+
5
+ module Cie
6
+ module Saml
7
+ class LogoutResponse
8
+ include Coding
9
+ include Request
10
+ ASSERTION = "urn:oasis:names:tc:SAML:2.0:assertion"
11
+ PROTOCOL = "urn:oasis:names:tc:SAML:2.0:protocol"
12
+ DSIG = "http://www.w3.org/2000/09/xmldsig#"
13
+
14
+ def initialize( options = { } )
15
+ opt = { :response => nil, :settings => nil }.merge(options)
16
+ # We've recieved a LogoutResponse from the IdP
17
+ if opt[:response]
18
+ begin
19
+ @response = Cie::XMLSecurity::SignedDocument.new(decode( opt[:response] ))
20
+ # Check to see if we have a root tag using the "protocol" namespace.
21
+ # If not, it means this is deflated text and we need to raise to
22
+ # the rescue below
23
+ raise if @response.nil?
24
+ raise if @response.root.nil?
25
+ raise if @response.root.namespace != PROTOCOL
26
+ document
27
+ rescue
28
+ @response = Cie::XMLSecurity::SignedDocument.new( inflate(decode( opt[:response] ) ) )
29
+ end
30
+ end
31
+ # We plan to create() a new LogoutResponse
32
+ if opt[:settings]
33
+ @settings = opt[:settings]
34
+ end
35
+ end
36
+
37
+ # Create a LogoutResponse to to the IdP's LogoutRequest
38
+ # (For IdP initiated SLO)
39
+ def create( options )
40
+ opt = { :transaction_id => nil,
41
+ :in_response_to => nil,
42
+ :status => "urn:oasis:names:tc:SAML:2.0:status:Success",
43
+ :extra_parameters => nil }.merge(options)
44
+ return nil if opt[:transaction_id].nil?
45
+ @response = REXML::Document.new
46
+ @response.context[:attribute_quote] = :quote
47
+ uuid = "_" + UUID.new.generate
48
+ time = Time.now.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
49
+ root = @response.add_element "saml2p:LogoutResponse", { "xmlns:saml2p" => PROTOCOL }
50
+ root.attributes['ID'] = uuid
51
+ root.attributes['IssueInstant'] = time
52
+ root.attributes['Version'] = "2.0"
53
+ # Just convenient naming to accept both names as InResponseTo
54
+ if opt[:transaction_id]
55
+ root.attributes['InResponseTo'] = opt[:transaction_id]
56
+ elsif opt[:in_response_to]
57
+ root.attributes['InResponseTo'] = opt[:in_response_to]
58
+ end
59
+ if opt[:status]
60
+ status = root.add_element "saml2p:Status"
61
+ status_code = status.add_element "saml2p:StatusCode", {
62
+ "Value" => opt[:status]
63
+ }
64
+ end
65
+ if @settings && @settings.issuer
66
+ issuer = root.add_element "saml:Issuer", {
67
+ "xmlns:saml" => "urn:oasis:names:tc:SAML:2.0:assertion"
68
+ }
69
+ issuer.text = @settings.issuer
70
+ end
71
+ meta = Metadata.new( @settings )
72
+ Logging.debug "Created LogoutResponse:\n#{@response}"
73
+ return meta.create_slo_response( to_s, opt[:extra_parameters] )
74
+
75
+ #root.attributes['Destination'] = action
76
+
77
+ end
78
+ # function to return the created request as an XML document
79
+ def to_xml
80
+ text = ""
81
+ @response.write(text, 1)
82
+ return text
83
+ end
84
+ def to_s
85
+ @response.to_s
86
+ end
87
+
88
+ def issuer
89
+ element = REXML::XPath.first(@response, "/p:LogoutResponse/a:Issuer", {
90
+ "p" => PROTOCOL, "a" => ASSERTION} )
91
+ return nil if element.nil?
92
+ element.text
93
+ end
94
+
95
+ def in_response_to
96
+ element = REXML::XPath.first(@response, "/p:LogoutResponse", {
97
+ "p" => PROTOCOL })
98
+ return nil if element.nil?
99
+ element.attributes["InResponseTo"]
100
+ end
101
+
102
+ def success?
103
+ element = REXML::XPath.first(@response, "/p:LogoutResponse/p:Status/p:StatusCode", {
104
+ "p" => PROTOCOL })
105
+ return false if element.nil?
106
+ element.attributes["Value"] == "urn:oasis:names:tc:SAML:2.0:status:Success"
107
+
108
+ end
109
+ def is_valid?
110
+ validate(soft = true)
111
+ end
112
+
113
+ def validate!
114
+ validate( soft = false )
115
+ end
116
+ def validate( soft = true )
117
+ return false if @response.nil?
118
+ # Skip validation with a failed response if we don't have settings
119
+ return false if @settings.nil?
120
+ return false if @response.validate(@settings, soft) == false
121
+
122
+ return true
123
+
124
+ end
125
+
126
+ protected
127
+ def document
128
+ REXML::Document.new(@response)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,489 @@
1
+ require "rexml/document"
2
+ require "rexml/xpath"
3
+ require "net/https"
4
+ require "uri"
5
+ require "digest/md5"
6
+ require "nokogiri"
7
+ require_relative "../xml_security_new" #fa il require della nokogiri
8
+ require "uuid"
9
+
10
+ # Class to return SP metadata based on the settings requested.
11
+ # Return this XML in a controller, then give that URL to the the
12
+ # IdP administrator. The IdP will poll the URL and your settings
13
+ # will be updated automatically
14
+ module Cie
15
+ module Saml
16
+ class Metadata
17
+ include REXML
18
+ include Coding
19
+ # a few symbols for SAML class names
20
+ HTTP_POST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
21
+ HTTP_GET = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
22
+
23
+ attr_accessor :uuid
24
+
25
+ def initialize(settings=nil)
26
+ if settings
27
+ @settings = settings
28
+ end
29
+ end
30
+
31
+ def generate(settings)
32
+ #meta_doc = REXML::Document.new
33
+ meta_doc = Cie::XMLSecurityNew::Document.new
34
+ root = meta_doc.add_element "md:EntityDescriptor", {
35
+ "xmlns:md" => "urn:oasis:names:tc:SAML:2.0:metadata",
36
+ "xmlns:xml" => "http://www.w3.org/XML/1998/namespace"
37
+ }
38
+ if settings.issuer != nil
39
+ root.attributes["entityID"] = settings.issuer
40
+ end
41
+ uuid = "_" + UUID.new.generate
42
+ self.uuid = uuid
43
+ root.attributes["ID"] = uuid
44
+
45
+ sp_sso = root.add_element "md:SPSSODescriptor", {
46
+ "protocolSupportEnumeration" => "urn:oasis:names:tc:SAML:2.0:protocol",
47
+ "WantAssertionsSigned" => "true",
48
+ "AuthnRequestsSigned" => "true"
49
+
50
+ }
51
+
52
+
53
+ # if settings.sp_cert != nil
54
+ # keyDescriptor = sp_sso.add_element "md:KeyDescriptor", {
55
+ # "use" => "signing"
56
+ # }
57
+ # keyInfo = keyDescriptor.add_element "ds:KeyInfo", {
58
+ # "xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"
59
+ # }
60
+ # x509Data = keyInfo.add_element "ds:X509Data"
61
+ # x509Certificate = x509Data.add_element "ds:X509Certificate"
62
+ # file = ""
63
+ # File.foreach(settings.sp_cert){ |line|
64
+ # file += line unless (line.include?("RSA PUBLIC KEY") || line.include?("CERTIFICATE"))
65
+ # }
66
+ # x509Certificate.text = file
67
+ # end
68
+
69
+ # Add KeyDescriptor if messages will be signed / encrypted
70
+ #cert = settings.get_sp_cert
71
+ cert = settings.get_cert(settings.sp_cert)
72
+ if cert
73
+
74
+ if cert.is_a?(String)
75
+ cert = OpenSSL::X509::Certificate.new(cert)
76
+ end
77
+
78
+ cert_text = Base64.encode64(cert.to_der).to_s.gsub(/\n/, "").gsub(/\t/, "")
79
+ kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
80
+ ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
81
+ xd = ki.add_element "ds:X509Data"
82
+ xc = xd.add_element "ds:X509Certificate"
83
+ xc.text = cert_text
84
+
85
+ # kd2 = sp_sso.add_element "md:KeyDescriptor", { "use" => "encryption" }
86
+ # ki2 = kd2.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
87
+ # xd2 = ki2.add_element "ds:X509Data"
88
+ # xc2 = xd2.add_element "ds:X509Certificate"
89
+ # xc2.text = cert_text
90
+ end
91
+
92
+ if !settings.sp_external_consumer_cert.nil? && settings.sp_external_consumer_cert.length > 0
93
+ settings.sp_external_consumer_cert.each{ |cert_cons_external|
94
+ cert_ex = settings.get_cert(cert_cons_external)
95
+ if cert_ex
96
+
97
+ if cert_ex.is_a?(String)
98
+ cert_ex = OpenSSL::X509::Certificate.new(cert_ex)
99
+ end
100
+
101
+ cert_text = Base64.encode64(cert_ex.to_der).to_s.gsub(/\n/, "").gsub(/\t/, "")
102
+ kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
103
+ ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
104
+ xd = ki.add_element "ds:X509Data"
105
+ xc = xd.add_element "ds:X509Certificate"
106
+ xc.text = cert_text
107
+ end
108
+ }
109
+ end
110
+
111
+ if settings.single_logout_service_url != nil
112
+ sp_sso.add_element "md:SingleLogoutService", {
113
+ "Binding" => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
114
+ "Location" => settings.single_logout_service_url
115
+ }
116
+ sp_sso.add_element "md:SingleLogoutService", {
117
+ "Binding" => "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
118
+ "Location" => settings.single_logout_service_url
119
+ }
120
+ end
121
+
122
+ #Logout dei servizi esterni
123
+ unless settings.hash_assertion_consumer.blank?
124
+ settings.hash_assertion_consumer.each_pair{ |index, hash_service|
125
+ unless hash_service['logout'].blank?
126
+ sp_sso.add_element "md:SingleLogoutService", {
127
+ "Binding" => hash_service['logout']['binding'] || "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
128
+ "Location" => hash_service['logout']['location']
129
+ }
130
+ end
131
+ }
132
+ end
133
+
134
+ name_identifier_formats = settings.name_identifier_format
135
+ if name_identifier_formats != nil
136
+ name_id = []
137
+ name_identifier_formats.each_with_index{ |format, index|
138
+ name_id[index] = sp_sso.add_element "md:NameIDFormat"
139
+ name_id[index].text = format
140
+ }
141
+
142
+ end
143
+
144
+ if settings.assertion_consumer_service_url
145
+
146
+ #ciclo e creo i vari tag AssertionConsumerService
147
+ settings.hash_assertion_consumer.each_pair{ |index, hash_service|
148
+
149
+ sp_sso.add_element "md:AssertionConsumerService", {
150
+ "Binding" => settings.assertion_consumer_service_binding,
151
+ "Location" => (hash_service['external'] ? hash_service['url_consumer'] : settings.assertion_consumer_service_url ),
152
+ "isDefault" => hash_service['default'],
153
+ "index" => index
154
+ }
155
+ }
156
+
157
+ # #Caso con eidas
158
+ # sp_sso.add_element "md:AssertionConsumerService", {
159
+ # "Binding" => settings.assertion_consumer_service_binding,
160
+ # "Location" => settings.assertion_consumer_service_url,
161
+ # "index" => 99
162
+ # }
163
+
164
+ # sp_sso.add_element "md:AssertionConsumerService", {
165
+ # "Binding" => settings.assertion_consumer_service_binding,
166
+ # "Location" => settings.assertion_consumer_service_url,
167
+ # "index" => 100
168
+ # }
169
+
170
+ settings.hash_assertion_consumer.each_pair{ |index, hash_service|
171
+
172
+ #AttributeConsumingService
173
+ attr_cons_service = sp_sso.add_element "md:AttributeConsumingService", {
174
+ "index" => index,
175
+ }
176
+ service_name = attr_cons_service.add_element "md:ServiceName", {
177
+ "xml:lang" => "it"
178
+ }
179
+ service_name.text = hash_service['testo']
180
+ unless hash_service['description'].blank?
181
+ service_description = attr_cons_service.add_element "md:ServiceDescription", {
182
+ "xml:lang" => "it"
183
+ }
184
+ service_description.text = hash_service['description']
185
+ end
186
+
187
+ if hash_service['array_campi'].is_a?(Array)
188
+ hash_service['array_campi'].each_with_index{ |attribute, index|
189
+ attr_cons_service.add_element "md:RequestedAttribute", {
190
+ "Name" => attribute
191
+ }
192
+ }
193
+ else #hash
194
+ hash_service['array_campi'].each_pair{ |attribute, name_format|
195
+ attr_cons_service.add_element "md:RequestedAttribute", {
196
+ "Name" => attribute,
197
+ "NameFormat" => name_format
198
+ }
199
+ }
200
+ end
201
+ }
202
+
203
+
204
+
205
+
206
+ #Per EIDAS
207
+ # #AttributeConsumingService
208
+ # attr_cons_service = sp_sso.add_element "md:AttributeConsumingService", {
209
+ # "index" => "99",
210
+ # }
211
+ # service_name
212
+ # = attr_cons_service.add_element "md:ServiceName", {
213
+ # "xml:lang" => "it"
214
+ # }
215
+ # service_name.text = "eIDAS Natural Person Minimum Attribute Set"
216
+ # settings.requested_attribute.each_with_index{ |attribute, index|
217
+ # attr_cons_service.add_element "md:RequestedAttribute", {
218
+ # "Name" => attribute
219
+ # }
220
+ # }
221
+
222
+ # #AttributeConsumingService
223
+ # attr_cons_service = sp_sso.add_element "md:AttributeConsumingService", {
224
+ # "index" => "100",
225
+ # }
226
+ # service_name = attr_cons_service.add_element "md:ServiceName", {
227
+ # "xml:lang" => "it"
228
+ # }
229
+ # service_name.text = "eIDAS Natural Person Full Attribute Set"
230
+ # settings.requested_attribute.each_with_index{ |attribute, index|
231
+ # attr_cons_service.add_element "md:RequestedAttribute", {
232
+ # "Name" => attribute
233
+ # }
234
+ # }
235
+
236
+
237
+ end
238
+ #organization
239
+ organization = root.add_element "md:Organization"
240
+ org_name = organization.add_element "md:OrganizationName", {
241
+ "xml:lang" => "it"
242
+ }
243
+ org_name.text = settings.organization['org_name']
244
+ org_display_name = organization.add_element "md:OrganizationDisplayName", {
245
+ "xml:lang" => "it"
246
+ }
247
+ org_display_name.text = settings.organization['org_display_name']
248
+ org_url = organization.add_element "md:OrganizationURL", {
249
+ "xml:lang" => "it"
250
+ }
251
+ org_url.text = settings.organization['org_url']
252
+
253
+ #meta_doc << REXML::XMLDecl.new(version='1.0', encoding='UTF-8')
254
+ meta_doc << REXML::XMLDecl.new("1.0", "UTF-8")
255
+
256
+
257
+ #SE SERVE ANCHE ENCRYPTION
258
+ # # Add KeyDescriptor if messages will be signed / encrypted
259
+ #
260
+ # if cert
261
+ # cert_text = Base64.encode64(cert.to_der).gsub("\n", '')
262
+ # kd = sp_sso.add_element "md:KeyDescriptor", { "use" => "signing" }
263
+ # ki = kd.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
264
+ # xd = ki.add_element "ds:X509Data"
265
+ # xc = xd.add_element "ds:X509Certificate"
266
+ # xc.text = cert_text
267
+
268
+ # kd2 = sp_sso.add_element "md:KeyDescriptor", { "use" => "encryption" }
269
+ # ki2 = kd2.add_element "ds:KeyInfo", {"xmlns:ds" => "http://www.w3.org/2000/09/xmldsig#"}
270
+ # xd2 = ki2.add_element "ds:X509Data"
271
+ # xc2 = xd2.add_element "ds:X509Certificate"
272
+ # xc2.text = cert_text
273
+ # end
274
+
275
+ #cert = settings.get_sp_cert
276
+ cert = settings.get_cert(settings.sp_cert) #inserisco il certificato principale
277
+ # embed signature
278
+ if settings.metadata_signed && settings.sp_private_key && settings.sp_cert
279
+ private_key = settings.get_sp_key
280
+ meta_doc.sign_document(private_key, cert)
281
+ end
282
+ ret = ""
283
+ # stampo come stringa semplice i metadata per non avere problemi con validazione firma
284
+ ret = meta_doc.to_s
285
+ #Logging.debug "Generated metadata:\n#{ret}"
286
+
287
+ return ret
288
+
289
+ end
290
+
291
+ def create_sso_request(message, extra_parameters = {} )
292
+ build_message( :type => "SAMLRequest",
293
+ :service => "SingleSignOnService",
294
+ :message => message, :extra_parameters => extra_parameters)
295
+ end
296
+ def create_sso_response(message, extra_parameters = {} )
297
+ build_message( :type => "SAMLResponse",
298
+ :service => "SingleSignOnService",
299
+ :message => message, :extra_parameters => extra_parameters)
300
+ end
301
+ def create_slo_request(message, extra_parameters = {} )
302
+ build_message( :type => "SAMLRequest",
303
+ :service => "SingleLogoutService",
304
+ :message => message, :extra_parameters => extra_parameters)
305
+ end
306
+ def create_slo_response(message, extra_parameters = {} )
307
+ build_message( :type => "SAMLResponse",
308
+ :service => "SingleLogoutService",
309
+ :message => message, :extra_parameters => extra_parameters)
310
+ end
311
+
312
+ # Construct a SAML message using information in the IdP metadata.
313
+ # :type can be either "SAMLRequest" or "SAMLResponse"
314
+ # :service refers to the Binding method,
315
+ # either "SingleLogoutService" or "SingleSignOnService"
316
+ # :message is the SAML message itself (XML)
317
+ # I've provided easy to use wrapper functions above
318
+ def build_message( options = {} )
319
+ opt = { :type => nil, :service => nil, :message => nil, :extra_parameters => nil }.merge(options)
320
+ url = binding_select( opt[:service] )
321
+ return message_get( opt[:type], url, opt[:message], opt[:extra_parameters] )
322
+ end
323
+
324
+ # get the IdP metadata, and select the appropriate SSO binding
325
+ # that we can support. Currently this is HTTP-Redirect and HTTP-POST
326
+ # but more could be added in the future
327
+ def binding_select(service)
328
+ # first check if we're still using the old hard coded method for
329
+ # backwards compatability
330
+ if service == "SingleSignOnService" && @settings.idp_sso_target_url != nil
331
+ return @settings.idp_sso_target_url
332
+ end
333
+ if service == "SingleLogoutService" && @settings.idp_slo_target_url != nil
334
+ return @settings.idp_slo_target_url
335
+ end
336
+
337
+ meta_doc = get_idp_metadata
338
+
339
+ return nil unless meta_doc
340
+ # first try GET (REDIRECT)
341
+ sso_element = REXML::XPath.first(meta_doc, "/EntityDescriptor/IDPSSODescriptor/#{service}[@Binding='#{HTTP_GET}']")
342
+ if !sso_element.nil?
343
+ @URL = sso_element.attributes["Location"]
344
+ Logging.debug "binding_select: GET from #{@URL}"
345
+ return @URL
346
+ end
347
+
348
+ # next try post
349
+ sso_element = REXML::XPath.first(meta_doc, "/EntityDescriptor/IDPSSODescriptor/#{service}[@Binding='#{HTTP_POST}']")
350
+ if !sso_element.nil?
351
+ @URL = sso_element.attributes["Location"]
352
+ #Logging.debug "binding_select: POST to #{@URL}"
353
+ return @URL
354
+ end
355
+
356
+ # other types we might want to add in the future: SOAP, Artifact
357
+ end
358
+
359
+
360
+ def fetch(uri_str, limit = 10)
361
+ # You should choose a better exception.
362
+ raise ArgumentError, 'too many HTTP redirects' if limit == 0
363
+
364
+ uri = URI.parse(uri_str)
365
+ if uri.scheme == "http"
366
+ response = Net::HTTP.get_response(uri)
367
+ elsif uri.scheme == "https"
368
+ http = Net::HTTP.new(uri.host, uri.port)
369
+ http.use_ssl = true
370
+ # Most IdPs will probably use self signed certs
371
+ #http.verify_mode = OpenSSL::SSL::VERIFY_PEER
372
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
373
+ get = Net::HTTP::Get.new(uri.request_uri)
374
+ response = http.request(get)
375
+ end
376
+
377
+ case response
378
+ when Net::HTTPSuccess then
379
+ response
380
+ when Net::HTTPRedirection then
381
+ location = response['location']
382
+ warn "redirected to #{location}"
383
+ fetch(location, limit - 1)
384
+ else
385
+ response.value
386
+ end
387
+ end
388
+
389
+
390
+
391
+ # Retrieve the remote IdP metadata from the URL or a cached copy
392
+ # returns a REXML document of the metadata
393
+ def get_idp_metadata
394
+ return false if @settings.idp_metadata.nil?
395
+
396
+ # Look up the metdata in cache first
397
+ id = Digest::MD5.hexdigest(@settings.idp_metadata)
398
+ response = fetch(@settings.idp_metadata)
399
+ #meta_text = response.body
400
+ #testo_response = meta_text.sub!(' xmlns:xml="http://www.w3.org/XML/1998/namespace"', '') da errori
401
+ #uso nokogiri per cercare il certificato, uso la funzione che rimuove tutti i namespace
402
+ doc_noko = Nokogiri::XML(response.body.gsub(/\n/, "").gsub(/\t/, "")) #modifica per poste
403
+ doc_noko.remove_namespaces!
404
+ extract_certificate(doc_noko)
405
+ doc_rexml = REXML::Document.new(doc_noko.to_xml)
406
+
407
+ return doc_rexml
408
+
409
+ # USE OF CACHE WITH CERTIFICATE
410
+ # lookup = @cache.read(id)
411
+ # if lookup != nil
412
+ # Logging.debug "IdP metadata cached lookup for #{@settings.idp_metadata}"
413
+ # doc = REXML::Document.new( lookup )
414
+ # extract_certificate( doc )
415
+ # return doc
416
+ # end
417
+
418
+ # Logging.debug "IdP metadata cache miss on #{@settings.idp_metadata}"
419
+ # # cache miss
420
+ # if File.exists?(@settings.idp_metadata)
421
+ # fp = File.open( @settings.idp_metadata, "r")
422
+ # meta_text = fp.read
423
+ # else
424
+ # uri = URI.parse(@settings.idp_metadata)
425
+ # if uri.scheme == "http"
426
+ # response = Net::HTTP.get_response(uri)
427
+ # meta_text = response.body
428
+ # elsif uri.scheme == "https"
429
+ # http = Net::HTTP.new(uri.host, uri.port)
430
+ # http.use_ssl = true
431
+ # # Most IdPs will probably use self signed certs
432
+ # #http.verify_mode = OpenSSL::SSL::VERIFY_PEER
433
+ # http.verify_mode = OpenSSL::SSL::VERIFY_NONE
434
+ # get = Net::HTTP::Get.new(uri.request_uri)
435
+ # response = http.request(get)
436
+ # meta_text = response.body
437
+ # end
438
+ # end
439
+ # # Add it to the cache
440
+ # @cache.write(id, meta_text, @settings.idp_metadata_ttl )
441
+ # doc = REXML::Document.new( meta_text )
442
+ # extract_certificate(doc)
443
+ # return doc
444
+ end
445
+
446
+ def extract_certificate(meta_doc)
447
+ #ricerco il certificato con nokogiri
448
+ # pull out the x509 tag
449
+ x509 = meta_doc.xpath("//EntityDescriptor//IDPSSODescriptor//KeyDescriptor//KeyInfo//X509Data//X509Certificate")
450
+
451
+ #x509 = REXML::XPath.first(meta_doc, "/md:EntityDescriptor/md:IDPSSODescriptor"+"/md:KeyDescriptor"+"/ds:KeyInfo/ds:X509Data/ds:X509Certificate")
452
+ # If the IdP didn't specify the use attribute
453
+ if x509.nil?
454
+ x509 = meta_doc.xpath("//EntityDescriptor//IDPSSODescriptor//KeyDescriptor//KeyInfo//X509Data//X509Certificate")
455
+ # x509 = REXML::XPath.first(meta_doc,
456
+ # "/EntityDescriptor/IDPSSODescriptor" +
457
+ # "/KeyDescriptor" +
458
+ # "/ds:KeyInfo/ds:X509Data/ds:X509Certificate"
459
+ # )
460
+ end
461
+ @settings.idp_cert = x509.children.to_s.gsub(/\n/, "").gsub(/\t/, "")
462
+ end
463
+
464
+ # construct the parameter list on the URL and return
465
+ def message_get( type, url, message, extra_parameters = {} )
466
+ params = Hash.new
467
+ if extra_parameters
468
+ params.merge!(extra_parameters)
469
+ end
470
+ # compress GET requests to try and stay under that 8KB request limit
471
+ #deflate of samlrequest
472
+ params[type] = encode( deflate( message ) )
473
+ #Logging.debug "#{type}=#{params[type]}"
474
+
475
+ uri = Addressable::URI.parse(url)
476
+ if uri.query_values == nil
477
+ uri.query_values = params
478
+ else
479
+ # solution to stevenwilkin's parameter merge
480
+ uri.query_values = params.merge(uri.query_values)
481
+ end
482
+ url = uri.to_s
483
+ #Logging.debug "Sending to URL #{url}"
484
+ return url
485
+ end
486
+
487
+ end
488
+ end
489
+ end