diff --git a/Gemfile.lock b/Gemfile.lock index ff8a1f6..09d753c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -14,6 +14,7 @@ GEM amq-protocol (>= 0.9.4) eventmachine antlr3 (1.8.12) + bouncy-castle-java (1.5.0146.1) builder (3.1.4) crack (0.3.1) cucumber (1.2.1) @@ -23,20 +24,26 @@ GEM json (>= 1.4.6) diff-lcs (1.1.3) eventmachine (1.0.0) + eventmachine (1.0.0-java) gherkin (2.11.5) json (>= 1.4.6) + gherkin (2.11.5-java) + json (>= 1.4.6) hashie (1.2.0) highline (1.6.15) httparty (0.9.0) multi_json (~> 1.0) multi_xml i18n (0.6.1) + jruby-openssl (0.7.7) + bouncy-castle-java (>= 1.5.0146.1) json (1.7.5) + json (1.7.5-java) multi_json (1.3.7) multi_xml (0.5.1) nokogiri (1.5.5) + nokogiri (1.5.5-java) rake (0.9.2.2) - redcarpet (2.2.2) rspec (2.11.0) rspec-core (~> 2.11.0) rspec-expectations (~> 2.11.0) @@ -67,6 +74,7 @@ GEM yard (~> 0.7) PLATFORMS + java ruby DEPENDENCIES @@ -78,10 +86,10 @@ DEPENDENCIES highline httparty i18n + jruby-openssl json nokogiri rake - redcarpet rspec rspec-http simplecov diff --git a/lib/occi/api/client/client_http.rb b/lib/occi/api/client/client_http.rb index 31174a7..08c0532 100644 --- a/lib/occi/api/client/client_http.rb +++ b/lib/occi/api/client/client_http.rb @@ -1,5 +1,7 @@ require 'rubygems' require 'httparty' +require 'occi/api/client/http/net_http_fix' +require 'occi/api/client/http/httparty_fix' module Occi module Api @@ -9,6 +11,7 @@ class ClientHttp # HTTParty for raw HTTP requests include HTTParty + # TODO: uncomment the following line as JSON is properly implemented in OpenStack # headers 'Accept' => 'application/occi+json,text/plain;q=0.8,text/occi;q=0.2' headers 'Accept' => 'text/plain,text/occi;q=0.2' @@ -586,7 +589,9 @@ def change_auth(auth_options) raise ArgumentError, "The file specified in 'user_cert' does not exist!" unless File.exists? @auth_options[:user_cert] self.class.pem File.read(@auth_options[:user_cert]), @auth_options[:user_cert_password] - self.class.ssl_ca_path @auth_options[:ca_path] unless @auth_options[:ca_path].nil? or @auth_options[:ca_path].empty? + self.class.ssl_ca_path @auth_options[:ca_path] unless @auth_options[:ca_path].nil? + self.class.ssl_ca_file @auth_options[:ca_file] unless @auth_options[:ca_file].nil? + self.class.ssl_extra_chain_cert certs_to_file_ary(@auth_options[:proxy_ca]) unless @auth_options[:proxy_ca].nil? when "none", nil # do nothing else @@ -594,6 +599,19 @@ def change_auth(auth_options) end end + # Reads X.509 certificates from a file to an array. + # + # @example + # certs_to_file_ary "~/.globus/usercert.pem" + # # => [#, #, ...] + # + # @param [String] Path to a PEM file containing certificates + # @return [Array] An array of read certificates + def certs_to_file_ary(ca_file) + # TODO: read and separate multiple certificates + [] << File.read(ca_file) + end + # Performs GET request and parses the responses to collections. # # @example diff --git a/lib/occi/api/client/http/httparty_fix.rb b/lib/occi/api/client/http/httparty_fix.rb new file mode 100644 index 0000000..f4a47fd --- /dev/null +++ b/lib/occi/api/client/http/httparty_fix.rb @@ -0,0 +1,53 @@ +module HTTParty + class ConnectionAdapter + + private + + def attach_ssl_certificates(http, options) + if http.use_ssl? + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + + # Client certificate authentication + if options[:pem] + http.cert = OpenSSL::X509::Certificate.new(options[:pem]) + http.key = OpenSSL::PKey::RSA.new(options[:pem], options[:pem_password]) + http.verify_mode = OpenSSL::SSL::VERIFY_PEER + end + + # Set chain of client certificates + if options[:ssl_extra_chain_cert] + http.extra_chain_cert = [] + + options[:ssl_extra_chain_cert].each do |p_ca| + http.extra_chain_cert << OpenSSL::X509::Certificate.new(p_ca) + end + end + + # SSL certificate authority file and/or directory + if options[:ssl_ca_file] + http.ca_file = options[:ssl_ca_file] + http.verify_mode = OpenSSL::SSL::VERIFY_PEER + end + + if options[:ssl_ca_path] + http.ca_path = options[:ssl_ca_path] + http.verify_mode = OpenSSL::SSL::VERIFY_PEER + end + + # This is only Ruby 1.9+ + if options[:ssl_version] && http.respond_to?(:ssl_version=) + http.ssl_version = options[:ssl_version] + end + end + end + + end + + module ClassMethods + + def ssl_extra_chain_cert(ary_of_certs) + default_options[:ssl_extra_chain_cert] = ary_of_certs + end + + end +end diff --git a/lib/occi/api/client/http/net_http_fix.rb b/lib/occi/api/client/http/net_http_fix.rb new file mode 100644 index 0000000..882e89f --- /dev/null +++ b/lib/occi/api/client/http/net_http_fix.rb @@ -0,0 +1,46 @@ +############################################################################## +## Net::HTTP hack allowing the use of X.509 proxy certificates. +############################################################################## + +# When running Ruby 1.8.x, RUBY_ENGINE is not defined +RUBY_ENGINE = "ruby" unless defined? RUBY_ENGINE + +# detect jRuby +if RUBY_ENGINE == 'jruby' + #TODO: add jRuby support, this doesn't work + module Net + class HTTP + + SSL_ATTRIBUTES = SSL_ATTRIBUTES.concat %w(extra_chain_cert) + + # An Array of certificates that will be sent to the client. + attr_accessor :extra_chain_cert + + end + end +else + net_http_fix_rby_ver = RUBY_VERSION.split('.') + + # detect old Rubies, tested with 1.8.7-p371 + if net_http_fix_rby_ver[1] == "8" + module Net + class HTTP + + # An Array of certificates that will be sent to the client. + ssl_context_accessor :extra_chain_cert + + end + end + else + module Net + class HTTP + + SSL_ATTRIBUTES = SSL_ATTRIBUTES.concat %w(extra_chain_cert) + + # An Array of certificates that will be sent to the client. + attr_accessor :extra_chain_cert + + end + end + end +end diff --git a/lib/occi/bin/occi_opts.rb b/lib/occi/bin/occi_opts.rb index f8bc72f..a9df473 100644 --- a/lib/occi/bin/occi_opts.rb +++ b/lib/occi/bin/occi_opts.rb @@ -32,6 +32,7 @@ def self.parse(args) options.auth[:user_cert] = ENV['HOME'] + "/.globus/usercred.pem" options.auth[:ca_path] = "/etc/grid-security/certificates" options.auth[:username] = "anonymous" + options.auth[:ca_file] = nil options.output_format = :plain @@ -81,17 +82,27 @@ def self.parse(args) end opts.on("-c", - "--ca-path PATH", String, "Path to CA certificates, defaults to '#{options.auth[:ca_path]}'") do |ca_path| + "--ca-path PATH", String, "Path to CA certificates directory, defaults to '#{options.auth[:ca_path]}'") do |ca_path| options.auth[:ca_path] = ca_path end + opts.on("-f", + "--ca-file PATH", String, "Path to CA certificates in a file") do |ca_file| + options.auth[:ca_file] = ca_file + end + opts.on("-x", - "--user-cred X509_CREDENTIALS", + "--user-cred PATH", String, "Path to user's x509 credentials, defaults to '#{options.auth[:user_cert]}'") do |user_cred| options.auth[:user_cert] = user_cred end + opts.on("-X", + "--proxy-ca PATH", String, "Path to a file with GSI proxy's CA certificate(s)") do |proxy_ca| + options.auth[:proxy_ca] = proxy_ca + end + opts.on("-y", "--media-type MEDIA_TYPE", MEDIA_TYPES,