mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
0e1daf3ddd
Unlike last attempt this replaces Fog::Connection with Fog::XML::Connection which should be directly compatible. Fog::Connection is there for old PRs but should be removed real soon. Providers using JSON should be able to replace "XML" with "Core" within their code to cut down on the dependency. If I get the time I may attempt to clean up some but testing with Mock will mean that is mostly educated guesswork.
415 lines
16 KiB
Ruby
415 lines
16 KiB
Ruby
require 'fog/hp/core'
|
|
|
|
module Fog
|
|
module Storage
|
|
class HP < Fog::Service
|
|
|
|
requires :hp_secret_key, :hp_tenant_id, :hp_avl_zone
|
|
recognizes :hp_auth_uri, :hp_cdn_ssl, :hp_cdn_uri, :credentials, :hp_service_type
|
|
recognizes :persistent, :connection_options
|
|
recognizes :hp_use_upass_auth_style, :hp_auth_version, :user_agent
|
|
recognizes :hp_access_key, :hp_account_id # :hp_account_id is deprecated use hp_access_key instead
|
|
|
|
# :os_account_meta_temp_url_key is an OpenStack specific setting used to generate temporary urls.
|
|
recognizes :os_account_meta_temp_url_key
|
|
|
|
secrets :hp_secret_key, :os_account_meta_temp_url_key
|
|
|
|
model_path 'fog/hp/models/storage'
|
|
model :directory
|
|
collection :directories
|
|
model :shared_directory
|
|
collection :shared_directories
|
|
model :file
|
|
collection :files
|
|
model :shared_file
|
|
collection :shared_files
|
|
|
|
request_path 'fog/hp/requests/storage'
|
|
request :delete_container
|
|
request :delete_object
|
|
request :delete_shared_object
|
|
request :get_container
|
|
request :get_containers
|
|
request :get_object
|
|
request :get_object_temp_url
|
|
request :get_shared_container
|
|
request :get_shared_object
|
|
request :head_container
|
|
request :head_containers
|
|
request :head_object
|
|
request :head_shared_container
|
|
request :head_shared_object
|
|
request :post_container
|
|
request :post_object
|
|
request :put_container
|
|
request :put_object
|
|
request :put_shared_object
|
|
|
|
module Utils
|
|
|
|
def cdn
|
|
unless @hp_cdn_uri.nil?
|
|
@cdn ||= Fog::CDN.new(
|
|
:provider => 'HP',
|
|
:hp_access_key => @hp_access_key,
|
|
:hp_secret_key => @hp_secret_key,
|
|
:hp_auth_uri => @hp_auth_uri,
|
|
:hp_cdn_uri => @hp_cdn_uri,
|
|
:hp_tenant_id => @hp_tenant_id,
|
|
:hp_avl_zone => @hp_avl_zone,
|
|
:credentials => @credentials,
|
|
:connection_options => @connection_options
|
|
)
|
|
if @cdn.enabled?
|
|
@cdn
|
|
end
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
def url
|
|
"#{@scheme}://#{@host}:#{@port}#{@path}"
|
|
end
|
|
|
|
def public_url(container=nil, object=nil)
|
|
public_url = nil
|
|
unless container.nil?
|
|
if object.nil?
|
|
# return container public url
|
|
public_url = "#{url}/#{Fog::HP.escape(container)}"
|
|
else
|
|
# return object public url
|
|
public_url = "#{url}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
|
|
end
|
|
end
|
|
public_url
|
|
end
|
|
|
|
def perm_to_acl(perm, users=[])
|
|
read_perm_acl = []
|
|
write_perm_acl = []
|
|
valid_public_perms = ['pr', 'pw', 'prw']
|
|
valid_account_perms = ['r', 'w', 'rw']
|
|
valid_perms = valid_public_perms + valid_account_perms
|
|
unless valid_perms.include?(perm)
|
|
raise ArgumentError.new("permission must be one of [#{valid_perms.join(', ')}]")
|
|
end
|
|
# tackle the public access differently
|
|
if valid_public_perms.include?(perm)
|
|
case perm
|
|
when "pr"
|
|
read_perm_acl = [".r:*",".rlistings"]
|
|
when "pw"
|
|
write_perm_acl = ["*"]
|
|
when "prw"
|
|
read_perm_acl = [".r:*",".rlistings"]
|
|
write_perm_acl = ["*"]
|
|
end
|
|
elsif valid_account_perms.include?(perm)
|
|
# tackle the user access differently
|
|
unless (users.nil? || users.empty?)
|
|
# return the correct acls
|
|
tenant_id = "*" # this might change later
|
|
acl_array = users.map { |u| "#{tenant_id}:#{u}" }
|
|
#acl_string = acl_array.join(',')
|
|
case perm
|
|
when "r"
|
|
read_perm_acl = acl_array
|
|
when "w"
|
|
write_perm_acl = acl_array
|
|
when "rw"
|
|
read_perm_acl = acl_array
|
|
write_perm_acl = acl_array
|
|
end
|
|
end
|
|
end
|
|
return read_perm_acl, write_perm_acl
|
|
end
|
|
|
|
def perm_acl_to_header(read_perm_acl, write_perm_acl)
|
|
header = {}
|
|
if read_perm_acl.nil? && write_perm_acl.nil?
|
|
header = {'X-Container-Read' => "", 'X-Container-Write' => ""}
|
|
elsif !read_perm_acl.nil? && write_perm_acl.nil?
|
|
header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => ""}
|
|
elsif read_perm_acl.nil? && !write_perm_acl.nil?
|
|
header = {'X-Container-Read' => "", 'X-Container-Write' => "#{write_perm_acl.join(',')}"}
|
|
elsif !read_perm_acl.nil? && !write_perm_acl.nil?
|
|
header = {'X-Container-Read' => "#{read_perm_acl.join(',')}", 'X-Container-Write' => "#{write_perm_acl.join(',')}"}
|
|
end
|
|
header
|
|
end
|
|
|
|
def header_to_perm_acl(read_header=nil, write_header=nil)
|
|
read_h, write_h = nil
|
|
read_h = read_header.split(',') unless read_header.nil?
|
|
write_h = write_header.split(',') unless write_header.nil?
|
|
return read_h, write_h
|
|
end
|
|
|
|
# Get an expiring object https url
|
|
#
|
|
# ==== Parameters
|
|
# * container<~String> - Name of container containing object
|
|
# * object<~String> - Name of object to get expiring url for
|
|
# * expires<~Time> - An expiry time for this url
|
|
#
|
|
# ==== Returns
|
|
# * response<~Excon::Response>:
|
|
# * body<~String> - url for object
|
|
def get_object_https_url(container, object, expires, options = {})
|
|
create_temp_url(container, object, expires, "GET", options.merge(:scheme => "https"))
|
|
end
|
|
|
|
# Get an expiring object http url
|
|
#
|
|
# ==== Parameters
|
|
# * container<~String> - Name of container containing object
|
|
# * object<~String> - Name of object to get expiring url for
|
|
# * expires<~Time> - An expiry time for this url
|
|
#
|
|
# ==== Returns
|
|
# * response<~Excon::Response>:
|
|
# * body<~String> - url for object
|
|
def get_object_http_url(container, object, expires, options = {})
|
|
create_temp_url(container, object, expires, "GET", options.merge(:scheme => "http"))
|
|
end
|
|
|
|
# Get an object http url expiring in the given amount of seconds
|
|
#
|
|
# ==== Parameters
|
|
# * container<~String> - Name of container containing object
|
|
# * object<~String> - Name of object to get expiring url for
|
|
# * expires_secs<~Integer> - the amount of seconds from now until the url expires
|
|
#
|
|
# ==== Returns
|
|
# * response<~Excon::Response>:
|
|
# * body<~String> - url for object
|
|
def generate_object_temp_url(container, object, expires_secs, method)
|
|
expiration_time = (Time.now + expires_secs.to_i).to_i
|
|
create_temp_url(container, object, expiration_time, method, {})
|
|
end
|
|
|
|
# creates a temporary url
|
|
#
|
|
# ==== Parameters
|
|
# * container<~String> - Name of container containing object
|
|
# * object<~String> - Name of object to get expiring url for
|
|
# * expires<~Time> - An expiry time for this url
|
|
# * method<~String> - The method to use for accessing the object (GET, PUT, HEAD)
|
|
# * scheme<~String> - The scheme to use (http, https)
|
|
# * options<~Hash> - An optional options hash
|
|
#
|
|
# ==== Returns
|
|
# * response<~Excon::Response>:
|
|
# * body<~String> - url for object
|
|
def create_temp_url(container, object, expires, method, options = {})
|
|
raise ArgumentError, "Insufficient parameters specified." unless (container && object && expires && method)
|
|
raise ArgumentError, "Storage must my instantiated with the :os_account_meta_temp_url_key option" if @os_account_meta_temp_url_key.nil?
|
|
|
|
# POST not allowed
|
|
allowed_methods = %w{GET PUT HEAD}
|
|
unless allowed_methods.include?(method)
|
|
raise ArgumentError.new("Invalid method '#{method}' specified. Valid methods are: #{allowed_methods.join(', ')}")
|
|
end
|
|
|
|
expires = expires.to_i
|
|
scheme = options[:scheme] || @scheme
|
|
|
|
# do not encode before signature generation, encode after
|
|
sig_path = "#{@path}/#{container}/#{object}"
|
|
encoded_path = "#{@path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}"
|
|
|
|
string_to_sign = "#{method}\n#{expires}\n#{sig_path}"
|
|
|
|
signature = nil
|
|
|
|
# HP uses a different strategy to create the signature that is passed to swift than OpenStack.
|
|
# As the HP provider is broadly used by OpenStack users the OpenStack strategy is applied when
|
|
# the @os_account_meta_temp_url_key is given.
|
|
if @os_account_meta_temp_url_key then
|
|
hmac = OpenSSL::HMAC.new(@os_account_meta_temp_url_key, OpenSSL::Digest::SHA1.new)
|
|
signature= hmac.update(string_to_sign).hexdigest
|
|
else
|
|
# Only works with 1.9+ Not compatible with 1.8.7
|
|
#signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1)
|
|
|
|
# Compatible with 1.8.7 onwards
|
|
hmac = OpenSSL::HMAC.new(@hp_secret_key, OpenSSL::Digest::SHA1.new)
|
|
signed_string = hmac.update(string_to_sign).hexdigest
|
|
|
|
signature = @hp_tenant_id.to_s + ":" + @hp_access_key.to_s + ":" + signed_string
|
|
signature = Fog::HP.escape(signature)
|
|
end
|
|
|
|
# generate the temp url using the signature and expiry
|
|
"#{scheme}://#{@host}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
|
|
end
|
|
end
|
|
|
|
class Mock
|
|
include Utils
|
|
def self.acls(type)
|
|
type
|
|
end
|
|
|
|
def self.data
|
|
@data ||= Hash.new do |hash, key|
|
|
hash[key] = {
|
|
:acls => {
|
|
:container => {},
|
|
:object => {}
|
|
},
|
|
:containers => {}
|
|
}
|
|
end
|
|
end
|
|
|
|
def self.reset
|
|
@data = nil
|
|
end
|
|
|
|
def initialize(options={})
|
|
# deprecate hp_account_id
|
|
if options[:hp_account_id]
|
|
Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.")
|
|
@hp_access_key = options.delete(:hp_account_id)
|
|
end
|
|
@hp_access_key = options[:hp_access_key]
|
|
unless @hp_access_key
|
|
raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.")
|
|
end
|
|
@hp_secret_key = options[:hp_secret_key]
|
|
@hp_tenant_id = options[:hp_tenant_id]
|
|
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
|
|
end
|
|
|
|
def data
|
|
self.class.data[@hp_access_key]
|
|
end
|
|
|
|
def reset_data
|
|
self.class.data.delete(@hp_access_key)
|
|
end
|
|
|
|
end
|
|
|
|
class Real
|
|
include Utils
|
|
attr_reader :credentials
|
|
attr_reader :hp_cdn_ssl
|
|
|
|
def initialize(options={})
|
|
# deprecate hp_account_id
|
|
if options[:hp_account_id]
|
|
Fog::Logger.deprecation(":hp_account_id is deprecated, please use :hp_access_key instead.")
|
|
options[:hp_access_key] = options.delete(:hp_account_id)
|
|
end
|
|
@hp_access_key = options[:hp_access_key]
|
|
unless @hp_access_key
|
|
raise ArgumentError.new("Missing required arguments: hp_access_key. :hp_account_id is deprecated, please use :hp_access_key instead.")
|
|
end
|
|
@hp_secret_key = options[:hp_secret_key]
|
|
@hp_auth_uri = options[:hp_auth_uri]
|
|
@hp_cdn_ssl = options[:hp_cdn_ssl]
|
|
@connection_options = options[:connection_options] || {}
|
|
### Set an option to use the style of authentication desired; :v1 or :v2 (default)
|
|
### A symbol is required, we should ensure that the value is loaded as a symbol
|
|
auth_version = options[:hp_auth_version] || :v2
|
|
auth_version = auth_version.to_s.downcase.to_sym
|
|
|
|
### Pass the service name for object storage to the authentication call
|
|
options[:hp_service_type] ||= "Object Storage"
|
|
@hp_tenant_id = options[:hp_tenant_id]
|
|
@hp_avl_zone = options[:hp_avl_zone]
|
|
@os_account_meta_temp_url_key = options[:os_account_meta_temp_url_key]
|
|
|
|
### Make the authentication call
|
|
if (auth_version == :v2)
|
|
# Call the control services authentication
|
|
credentials = Fog::HP.authenticate_v2(options, @connection_options)
|
|
# the CS service catalog returns the cdn endpoint
|
|
@hp_storage_uri = credentials[:endpoint_url]
|
|
@hp_cdn_uri = credentials[:cdn_endpoint_url]
|
|
@credentials = credentials
|
|
else
|
|
# Call the legacy v1.0/v1.1 authentication
|
|
credentials = Fog::HP.authenticate_v1(options, @connection_options)
|
|
# the user sends in the cdn endpoint
|
|
@hp_storage_uri = options[:hp_auth_uri]
|
|
@hp_cdn_uri = options[:hp_cdn_uri]
|
|
end
|
|
|
|
@auth_token = credentials[:auth_token]
|
|
|
|
uri = URI.parse(@hp_storage_uri)
|
|
@host = uri.host
|
|
@path = uri.path
|
|
@persistent = options[:persistent] || false
|
|
@port = uri.port
|
|
@scheme = uri.scheme
|
|
|
|
@connection = Fog::XML::Connection.new("#{@scheme}://#{@host}:#{@port}", @persistent, @connection_options)
|
|
end
|
|
|
|
def reload
|
|
@connection.reset
|
|
end
|
|
|
|
def request(params, parse_json = true, &block)
|
|
begin
|
|
response = @connection.request(params.merge!({
|
|
:headers => {
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
'X-Auth-Token' => @auth_token
|
|
}.merge!(params[:headers] || {}),
|
|
:path => "#{@path}/#{params[:path]}",
|
|
}), &block)
|
|
rescue Excon::Errors::HTTPStatusError => error
|
|
raise case error
|
|
when Excon::Errors::NotFound
|
|
Fog::Storage::HP::NotFound.slurp(error)
|
|
else
|
|
error
|
|
end
|
|
end
|
|
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
|
response.body = Fog::JSON.decode(response.body)
|
|
end
|
|
response
|
|
end
|
|
|
|
# this request is used only for get_shared_container and get_shared_object calls
|
|
def shared_request(params, parse_json = true, &block)
|
|
begin
|
|
response = @connection.request(params.merge!({
|
|
:headers => {
|
|
'Content-Type' => 'application/json',
|
|
'Accept' => 'application/json',
|
|
'X-Auth-Token' => @auth_token
|
|
}.merge!(params[:headers] || {}),
|
|
:path => "#{params[:path]}",
|
|
}), &block)
|
|
rescue Excon::Errors::HTTPStatusError => error
|
|
raise case error
|
|
when Excon::Errors::NotFound
|
|
Fog::Storage::HP::NotFound.slurp(error)
|
|
when Excon::Errors::Forbidden
|
|
Fog::HP::Errors::Forbidden.slurp(error)
|
|
else
|
|
error
|
|
end
|
|
end
|
|
if !response.body.empty? && parse_json && response.headers['Content-Type'] =~ %r{application/json}
|
|
response.body = Fog::JSON.decode(response.body)
|
|
end
|
|
response
|
|
end
|
|
|
|
end
|
|
end
|
|
end
|
|
end
|