From 271bc985fa057e3297249061af5ac91463d349ba Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Wed, 26 Sep 2012 13:01:32 -0400 Subject: [PATCH 1/7] Add object temp url generation capability with mock support. --- .../requests/storage/get_object_temp_url.rb | 34 +++++++++++++++++++ lib/fog/hp/storage.rb | 27 +++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 lib/fog/hp/requests/storage/get_object_temp_url.rb diff --git a/lib/fog/hp/requests/storage/get_object_temp_url.rb b/lib/fog/hp/requests/storage/get_object_temp_url.rb new file mode 100644 index 000000000..e803ec625 --- /dev/null +++ b/lib/fog/hp/requests/storage/get_object_temp_url.rb @@ -0,0 +1,34 @@ +module Fog + module Storage + class HP + + class Real + + # Generate a temporary url for an object + # + # ==== Parameters + # * container<~String> - Name of container + # * object<~String> - Name of object + # * expires<~Integer> - Time the temporary url expire in secs. + # * method<~String> - Allowed HTTP method GET, PUT, HEAD only + def get_object_temp_url(container, object, expires, method) + generate_object_temp_url(container, object, expires, method) + end + + end + + class Mock # :nodoc:all + + def get_object_temp_url(container, object, expires, method) + @scheme = "https" + @host = "swift-cluster.example.com" + @port = "443" + @path = "/v1/account" + + generate_object_temp_url(container, object, expires, method) + end + end + + end + end +end \ No newline at end of file diff --git a/lib/fog/hp/storage.rb b/lib/fog/hp/storage.rb index 21fd3e8e6..7e1b81caa 100644 --- a/lib/fog/hp/storage.rb +++ b/lib/fog/hp/storage.rb @@ -20,6 +20,7 @@ module Fog request :get_container request :get_containers request :get_object + request :get_object_temp_url request :head_container request :head_containers request :head_object @@ -81,6 +82,32 @@ module Fog acl = "public-read-write" end end + + def generate_object_temp_url(container, object, expires_secs, method) + return unless (container && object && expires_secs && method) + + # 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 = (Time.now + expires_secs.to_i).to_i + + # do not encode before signature generation, encode after + path = "#{@path}/#{container}/#{object}" + encoded_path = "#{@path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + + string_to_sign = "#{method}\n#{expires}\n#{path}" + signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1) + + signature = @hp_account_id.to_s + ":" + signed_string + signature = Fog::HP.escape(signature) + + # generate the temp url using the signature and expiry + temp_url = "#{@scheme}://#{@host}:#{@port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}" + end + end class Mock From 42a91a65d4b8319f8ffdbd447f5586b5de4c63bb Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Wed, 26 Sep 2012 13:16:38 -0400 Subject: [PATCH 2/7] Add object temp url functionality to file model layer. --- lib/fog/hp/models/storage/file.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/fog/hp/models/storage/file.rb b/lib/fog/hp/models/storage/file.rb index 573a8b48c..5b08f472f 100644 --- a/lib/fog/hp/models/storage/file.rb +++ b/lib/fog/hp/models/storage/file.rb @@ -70,6 +70,11 @@ module Fog self.collection.get_cdn_ssl_url(self.key) end + def temp_signed_url(expires_secs, method) + requires :directory, :key + connection.generate_object_temp_url(directory.key, key, expires_secs, method) + end + def save(options = {}) requires :body, :directory, :key options['Content-Type'] = content_type if content_type From 2459a6b457252058b1a96a590fc25dd229e8130b Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Wed, 26 Sep 2012 14:12:57 -0400 Subject: [PATCH 3/7] Add request layer tests for object_temp_url functionality. --- tests/hp/requests/storage/object_tests.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/hp/requests/storage/object_tests.rb b/tests/hp/requests/storage/object_tests.rb index c228f9180..dca0098c6 100644 --- a/tests/hp/requests/storage/object_tests.rb +++ b/tests/hp/requests/storage/object_tests.rb @@ -1,4 +1,4 @@ -Shindo.tests('Fog::Storage[:hp] | object requests', [:hp]) do +Shindo.tests('Fog::Storage[:hp] | object requests', ['hp', 'storage']) do @directory = Fog::Storage[:hp].directories.create(:key => 'fogobjecttests') @dir_name = @directory.identity @@ -25,6 +25,10 @@ Shindo.tests('Fog::Storage[:hp] | object requests', [:hp]) do Fog::Storage[:hp].head_object(@dir_name, 'fog_object') end + tests("#get_object_temp_url('#{@dir_name}', 'fog_object', 60, 'GET')").succeeds do + Fog::Storage[:hp].get_object_temp_url(@dir_name, 'fog_object', 60, 'GET') + end + # copy a file within the same container tests("#put_object('#{@dir_name}', 'fog_other_object', nil, {'X-Copy-From' => '/#{@dir_name}/fog_object'})" ).succeeds do Fog::Storage[:hp].put_object(@dir_name, 'fog_other_object', nil, {'X-Copy-From' => "/#{@dir_name}/fog_object"}) @@ -58,6 +62,10 @@ Shindo.tests('Fog::Storage[:hp] | object requests', [:hp]) do Fog::Storage[:hp].get_object('fognoncontainer', 'fog_non_object') end + tests("#get_object_temp_url('#{@dir_name}', 'fog_object', 60, 'POST')").raises(ArgumentError) do + Fog::Storage[:hp].get_object_temp_url(@dir_name, 'fog_object', 60, 'POST') + end + tests("#head_object('#{@dir_name}', 'fog_non_object')").raises(Fog::Storage::HP::NotFound) do Fog::Storage[:hp].head_object(@dir_name, 'fog_non_object') end From afc3fd9ad6fb73e02fac946cc554d439129672e7 Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Thu, 27 Sep 2012 15:55:57 -0400 Subject: [PATCH 4/7] Add tenant_id to the mix to tighten security for the temp_url generation. --- lib/fog/hp/storage.rb | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/fog/hp/storage.rb b/lib/fog/hp/storage.rb index 7e1b81caa..8ba015aad 100644 --- a/lib/fog/hp/storage.rb +++ b/lib/fog/hp/storage.rb @@ -94,18 +94,25 @@ module Fog expires = (Time.now + expires_secs.to_i).to_i - # do not encode before signature generation, encode after - path = "#{@path}/#{container}/#{object}" - encoded_path = "#{@path}/#{Fog::HP.escape(container)}/#{Fog::HP.escape(object)}" + # split up the storage uri + uri = URI.parse(@hp_storage_uri) + host = uri.host + path = uri.path + port = uri.port + scheme = uri.scheme - string_to_sign = "#{method}\n#{expires}\n#{path}" + # 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}" signed_string = Digest::HMAC.hexdigest(string_to_sign, @hp_secret_key, Digest::SHA1) - signature = @hp_account_id.to_s + ":" + signed_string + signature = @hp_tenant_id.to_s + ":" + @hp_account_id.to_s + ":" + signed_string signature = Fog::HP.escape(signature) # generate the temp url using the signature and expiry - temp_url = "#{@scheme}://#{@host}:#{@port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}" + temp_url = "#{scheme}://#{host}:#{port}#{encoded_path}?temp_url_sig=#{signature}&temp_url_expires=#{expires}" end end @@ -136,6 +143,7 @@ module Fog require 'mime/types' @hp_secret_key = options[:hp_secret_key] @hp_account_id = options[:hp_account_id] + @hp_tenant_id = options[:hp_tenant_id] end def data From c7349ee526926c21376314375afe2f0d5a211de1 Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Thu, 27 Sep 2012 15:56:40 -0400 Subject: [PATCH 5/7] Update mock for tenp_url. --- lib/fog/hp/requests/storage/get_object_temp_url.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/fog/hp/requests/storage/get_object_temp_url.rb b/lib/fog/hp/requests/storage/get_object_temp_url.rb index e803ec625..019f6c298 100644 --- a/lib/fog/hp/requests/storage/get_object_temp_url.rb +++ b/lib/fog/hp/requests/storage/get_object_temp_url.rb @@ -20,10 +20,7 @@ module Fog class Mock # :nodoc:all def get_object_temp_url(container, object, expires, method) - @scheme = "https" - @host = "swift-cluster.example.com" - @port = "443" - @path = "/v1/account" + @hp_storage_uri = "https://swift-cluster.example.com:443/v1/account" generate_object_temp_url(container, object, expires, method) end From fa3343fd6786aa0ac485cde1867c62a5d7bb9a21 Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Mon, 1 Oct 2012 15:01:37 -0400 Subject: [PATCH 6/7] Call the request layer method instead of the generic util method. --- lib/fog/hp/models/storage/file.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fog/hp/models/storage/file.rb b/lib/fog/hp/models/storage/file.rb index 5b08f472f..b1f4067d9 100644 --- a/lib/fog/hp/models/storage/file.rb +++ b/lib/fog/hp/models/storage/file.rb @@ -72,7 +72,7 @@ module Fog def temp_signed_url(expires_secs, method) requires :directory, :key - connection.generate_object_temp_url(directory.key, key, expires_secs, method) + connection.get_object_temp_url(directory.key, key, expires_secs, method) end def save(options = {}) From 3cb5dee0f9efedba88737267d81e6c79cf5990ff Mon Sep 17 00:00:00 2001 From: Rupak Ganguly Date: Tue, 2 Oct 2012 01:16:26 -0400 Subject: [PATCH 7/7] Add model tests for storage service. --- tests/hp/models/storage/directories_tests.rb | 17 +++++++ tests/hp/models/storage/directory_tests.rb | 50 ++++++++++++++++++++ tests/hp/models/storage/file_tests.rb | 44 +++++++++++++++++ tests/hp/models/storage/files_tests.rb | 42 ++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 tests/hp/models/storage/directories_tests.rb create mode 100644 tests/hp/models/storage/directory_tests.rb create mode 100644 tests/hp/models/storage/file_tests.rb create mode 100644 tests/hp/models/storage/files_tests.rb diff --git a/tests/hp/models/storage/directories_tests.rb b/tests/hp/models/storage/directories_tests.rb new file mode 100644 index 000000000..f92c97766 --- /dev/null +++ b/tests/hp/models/storage/directories_tests.rb @@ -0,0 +1,17 @@ +Shindo.tests('Fog::Storage[:hp] | directories', ['hp', 'storage']) do + + collection_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true) + + tests('success') do + + tests("#head('fogdirtests')").succeeds do + Fog::Storage[:hp].directories.head('fogdirtests') + end + + tests("#get('fogdirtests')").succeeds do + Fog::Storage[:hp].directories.get('fogdirtests') + end + + end + +end \ No newline at end of file diff --git a/tests/hp/models/storage/directory_tests.rb b/tests/hp/models/storage/directory_tests.rb new file mode 100644 index 000000000..3389db1ea --- /dev/null +++ b/tests/hp/models/storage/directory_tests.rb @@ -0,0 +1,50 @@ +Shindo.tests('Fog::Storage[:hp] | directory', ['hp', 'storage']) do + + model_tests(Fog::Storage[:hp].directories, {:key => "fogdirtests"}, true) do + + tests('success') do + + tests("#acl='public-read'").succeeds do + @instance.acl = 'public-read' + tests("public?").returns(true) do + @instance.public? + end + end + + @instance.files.create(:key => 'sample.txt', :body => lorem_file) + tests("#files").succeeds do + @instance.files + end + @instance.files.get('sample.txt').destroy + + tests("#cdn_enable=(true)").succeeds do + @instance.cdn_enable=(true) + tests("cdn_enabled?").returns(true) do + pending if Fog.mocking? + @instance.cdn_enable? + end + end + + tests("#cdn_public_url").succeeds do + pending if Fog.mocking? + @instance.cdn_public_url + end + + tests("#cdn_public_ssl_url").succeeds do + pending if Fog.mocking? + @instance.cdn_public_ssl_url + end + + end + + tests('failure') do + + tests("#acl='invalid-acl'").raises(ArgumentError) do + @instance.acl = 'invalid-acl' + end + + end + + end + +end diff --git a/tests/hp/models/storage/file_tests.rb b/tests/hp/models/storage/file_tests.rb new file mode 100644 index 000000000..47a2211da --- /dev/null +++ b/tests/hp/models/storage/file_tests.rb @@ -0,0 +1,44 @@ +Shindo.tests('Fog::Storage[:hp] | directory', ['hp', 'storage']) do + + file_attributes = { + :key => 'fog_file_tests', + :body => lorem_file, + :public => true + } + + directory_attributes = { + :key => 'fogfilestests' + } + + @directory = Fog::Storage[:hp].directories.create(directory_attributes) + + model_tests(@directory.files, file_attributes, true) do + + @file = @directory.files.get('fog_file_tests') + + tests('success') do + + tests("#directory").returns(@directory.key) do + @file.directory.key + end + + tests("#cdn_public_url").succeeds do + pending if Fog.mocking? + @file.cdn_public_url + end + + tests("#cdn_public_ssl_url").succeeds do + pending if Fog.mocking? + @file.cdn_public_ssl_url + end + + tests("#temp_signed_url(60, 'GET')").succeeds do + @file.temp_signed_url(60, 'GET') + end + + end + end + + @directory.destroy + +end \ No newline at end of file diff --git a/tests/hp/models/storage/files_tests.rb b/tests/hp/models/storage/files_tests.rb new file mode 100644 index 000000000..4a12d1cc5 --- /dev/null +++ b/tests/hp/models/storage/files_tests.rb @@ -0,0 +1,42 @@ +Shindo.tests('Fog::Storage[:hp] | files', ['hp', 'storage']) do + + file_attributes = { + :key => 'fog_files_tests', + :body => lorem_file + } + + directory_attributes = { + :key => 'fogfilestests' + } + + @directory = Fog::Storage[:hp].directories.create(directory_attributes) + + collection_tests(@directory.files, file_attributes, true) do + + tests('success') do + + tests("#get_url('#{@directory.key}')").succeeds do + @directory.files.get_url("#{@directory.key}") + end + + tests("#get_cdn_url('#{@directory.key}')").succeeds do + pending if Fog.mocking? + @directory.files.get_cdn_url("#{@directory.key}") + end + + tests("#get_cdn_ssl_url('#{@directory.key}')").succeeds do + pending if Fog.mocking? + @directory.files.get_cdn_ssl_url("#{@directory.key}") + end + + tests("#head('#{@directory.key}')").succeeds do + @directory.files.head("#{@directory.key}") + end + + end + + end + + @directory.destroy + +end \ No newline at end of file