From 6cfcfd1edf049b9ef681727fcd9c6ab08a45dd3d Mon Sep 17 00:00:00 2001 From: Wesley Beary Date: Sat, 3 Oct 2009 15:43:19 -0700 Subject: [PATCH] first pass at query param signed s3 requests --- lib/fog/aws/models/s3/objects.rb | 4 +++ lib/fog/aws/requests/s3/get_object.rb | 15 +++++++++ lib/fog/aws/s3.rb | 41 ++++++++++++++++--------- spec/aws/models/s3/objects_spec.rb | 12 ++++++++ spec/aws/requests/s3/get_object_spec.rb | 6 ++++ spec/spec_helper.rb | 1 + 6 files changed, 64 insertions(+), 15 deletions(-) diff --git a/lib/fog/aws/models/s3/objects.rb b/lib/fog/aws/models/s3/objects.rb index 22501aaef..35168b358 100644 --- a/lib/fog/aws/models/s3/objects.rb +++ b/lib/fog/aws/models/s3/objects.rb @@ -42,6 +42,10 @@ module Fog nil end + def get_url(key, expires) + connection.get_object_url(bucket.name, key, expires) + end + def head(key, options = {}) data = connection.head_object(bucket.name, key, options) object_data = { diff --git a/lib/fog/aws/requests/s3/get_object.rb b/lib/fog/aws/requests/s3/get_object.rb index 497dc1294..d91bd35a4 100644 --- a/lib/fog/aws/requests/s3/get_object.rb +++ b/lib/fog/aws/requests/s3/get_object.rb @@ -44,6 +44,21 @@ unless Fog.mocking? }) end + def get_object_url(bucket_name, object_name, expires) + unless bucket_name + raise ArgumentError.new('bucket_name is required') + end + unless object_name + raise ArgumentError.new('object_name is required') + end + url({ + :headers => {}, + :host => "#{bucket_name}.#{@host}", + :method => 'GET', + :path => object_name + }, expires) + end + end end end diff --git a/lib/fog/aws/s3.rb b/lib/fog/aws/s3.rb index 34d01d04f..daf58abe3 100644 --- a/lib/fog/aws/s3.rb +++ b/lib/fog/aws/s3.rb @@ -100,7 +100,33 @@ module Fog def request(params) params[:headers]['Date'] = Time.now.utc.strftime("%a, %d %b %Y %H:%M:%S +0000") + params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature(params)}" + response = @connection.request({ + :block => params[:block], + :body => params[:body], + :expects => params[:expects], + :headers => params[:headers], + :host => params[:host], + :method => params[:method], + :parser => params[:parser], + :path => params[:path], + :query => params[:query] + }) + + response + end + + def url(params, expires) + params[:headers]['Date'] = expires.to_i + query = [params[:query]].compact + query << "AWSAccessKeyId=#{@aws_access_key_id}" + query << "Signature=#{CGI.escape(signature(params))}" + query << "Expires=#{params[:headers]['Date']}" + "http://#{params[:host]}/#{params[:path]}?#{query.join('&')}" + end + + def signature(params) string_to_sign = <<-DATA #{params[:method]} @@ -145,21 +171,6 @@ DATA hmac = @hmac.update(string_to_sign) signature = Base64.encode64(hmac.digest).chomp! - params[:headers]['Authorization'] = "AWS #{@aws_access_key_id}:#{signature}" - - response = @connection.request({ - :block => params[:block], - :body => params[:body], - :expects => params[:expects], - :headers => params[:headers], - :host => params[:host], - :method => params[:method], - :parser => params[:parser], - :path => params[:path], - :query => params[:query] - }) - - response end end diff --git a/spec/aws/models/s3/objects_spec.rb b/spec/aws/models/s3/objects_spec.rb index 0546663c5..dc2aa8882 100644 --- a/spec/aws/models/s3/objects_spec.rb +++ b/spec/aws/models/s3/objects_spec.rb @@ -90,6 +90,18 @@ describe 'Fog::AWS::S3::Objects' do end + describe "#get_url" do + + it "should return a signed expiring url" do + file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r') + object = @bucket.objects.create(:key => 'fogobjectname', :body => file) + url = @bucket.objects.get_url('fogobjectname', Time.now + 60 * 10) + open(url).read.should == File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r').read + object.destroy + end + + end + describe "#head" do it "should return a Fog::AWS::S3::Object with metadata" do diff --git a/spec/aws/requests/s3/get_object_spec.rb b/spec/aws/requests/s3/get_object_spec.rb index 2c208930f..b9aeffe5a 100644 --- a/spec/aws/requests/s3/get_object_spec.rb +++ b/spec/aws/requests/s3/get_object_spec.rb @@ -35,6 +35,12 @@ describe 'S3.get_object' do data.should == file.read end + it 'should return a signed expiring url' do + url = s3.get_object_url('foggetobject', 'fog_get_object', Time.now + 60 * 10) + file = File.open(File.dirname(__FILE__) + '/../../../lorem.txt', 'r') + open(url).read.should == file.read + end + end describe 'failure' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ddb955ec4..6d1fc9cbe 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,4 +1,5 @@ require 'spec' +require 'open-uri' current_directory = File.dirname(__FILE__) require "#{current_directory}/../lib/fog"