2010-09-08 17:40:02 -04:00
module Fog
module AWS
class Storage < Fog :: Service
requires :aws_access_key_id , :aws_secret_access_key
model_path 'fog/aws/models/storage'
collection :directories
model :directory
collection :files
model :file
request_path 'fog/aws/requests/storage'
2010-11-15 20:17:37 -05:00
request :abort_multipart_upload
request :complete_multipart_upload
2010-09-08 17:40:02 -04:00
request :copy_object
request :delete_bucket
request :delete_object
request :get_bucket
request :get_bucket_acl
request :get_bucket_location
request :get_bucket_logging
request :get_bucket_object_versions
request :get_bucket_versioning
request :get_object
request :get_object_acl
request :get_object_torrent
request :get_object_url
request :get_request_payment
request :get_service
request :head_object
2010-11-15 20:17:37 -05:00
request :initiate_multipart_upload
request :list_multipart_uploads
request :list_parts
2010-10-18 20:56:01 -04:00
request :post_object_hidden_fields
2010-09-08 17:40:02 -04:00
request :put_bucket
request :put_bucket_acl
request :put_bucket_logging
request :put_bucket_versioning
request :put_object
2010-11-22 02:44:44 -05:00
request :put_object_acl
2010-09-08 17:40:02 -04:00
request :put_object_url
request :put_request_payment
2010-11-15 20:17:37 -05:00
request :upload_part
2010-09-08 17:40:02 -04:00
module Utils
2010-11-05 14:37:12 -04:00
def cdn
@cdn || = Fog :: AWS :: CDN . new (
:aws_access_key_id = > @aws_access_key_id ,
:aws_secret_access_key = > @aws_secret_access_key
)
end
2010-09-08 17:40:02 -04:00
def parse_data ( data )
metadata = {
:body = > nil ,
:headers = > { }
}
if data . is_a? ( String )
metadata [ :body ] = data
metadata [ :headers ] [ 'Content-Length' ] = metadata [ :body ] . size . to_s
else
filename = :: File . basename ( data . path )
unless ( mime_types = MIME :: Types . of ( filename ) ) . empty?
metadata [ :headers ] [ 'Content-Type' ] = mime_types . first . content_type
end
metadata [ :body ] = data
metadata [ :headers ] [ 'Content-Length' ] = :: File . size ( data . path ) . to_s
end
# metadata[:headers]['Content-MD5'] = Base64.encode64(Digest::MD5.digest(metadata[:body])).strip
metadata
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' ] } "
2010-09-24 14:32:48 -04:00
bucket = params [ :host ] . split ( '.' ) . first
" https:// #{ @host } / #{ params [ :path ] } ? #{ query . join ( '&' ) } "
2010-09-08 17:40:02 -04:00
end
end
class Mock
include Utils
2010-11-18 17:17:11 -05:00
def self . acls ( type )
case type
when 'private'
@private || = {
" AccessControlList " = > [
{
" Permission " = > " FULL_CONTROL " ,
" Grantee " = > { " DisplayName " = > " me " , " ID " = > " 2744ccd10c7533bd736ad890f9dd5cab2adb27b07d500b9493f29cdc420cb2e0 " }
}
] ,
" Owner " = > { " DisplayName " = > " me " , " ID " = > " 2744ccd10c7533bd736ad890f9dd5cab2adb27b07d500b9493f29cdc420cb2e0 " }
}
when 'public-read'
@public_read || = begin
public_read = self . acls ( 'private' ) . dup
public_read [ 'AccessControlList' ] << {
" Permission " = > " READ " ,
" Grantee " = > { " URI " = > " http://acs.amazonaws.com/groups/global/AllUsers " }
}
public_read
end
when 'public-read-write'
@public_read_write || = begin
public_read_write = self . acls ( 'public-read' ) . dup
public_read_write [ 'AccessControlList' ] << {
" Permission " = > " WRITE " ,
" Grantee " = > { " URI " = > " http://acs.amazonaws.com/groups/global/AllUsers " }
}
public_read_write
end
when 'authenticated-read'
@authenticated_read || = begin
authenticated_read = self . acls ( 'private' ) . dup
authenticated_read [ 'AccessControlList' ] << {
" Permission " = > " READ " ,
" Grantee " = > { " URI " = > " http://acs.amazonaws.com/groups/global/AuthenticatedUsers " }
}
authenticated_read
end
end
end
2010-09-08 17:40:02 -04:00
def self . data
2010-09-24 16:50:44 -04:00
@data || = Hash . new do | hash , region |
2010-10-13 16:20:18 -04:00
hash [ region ] = Hash . new do | region_hash , key |
region_hash [ key ] = {
2010-11-18 17:17:11 -05:00
:acls = > {
:bucket = > { } ,
:object = > { }
} ,
2010-09-24 16:50:44 -04:00
:buckets = > { }
}
end
2010-09-08 17:40:02 -04:00
end
end
def self . reset_data ( keys = data . keys )
for key in [ * keys ]
data . delete ( key )
end
end
def initialize ( options = { } )
2010-10-29 17:58:28 -04:00
require 'mime/types'
2010-09-08 17:40:02 -04:00
@aws_access_key_id = options [ :aws_access_key_id ]
2010-11-05 14:37:12 -04:00
@aws_secret_access_key = options [ :aws_secret_access_key ]
2010-09-24 17:43:29 -04:00
options [ :region ] || = 'us-east-1'
2010-09-24 17:36:42 -04:00
@host = options [ :host ] || case options [ :region ]
when 'eu-west-1'
's3-eu-west-1.amazonaws.com'
when 'us-east-1'
's3.amazonaws.com'
when 'ap-southeast-1'
's3-ap-southeast-1.amazonaws.com'
when 'us-west-1'
's3-us-west-1.amazonaws.com'
else
raise ArgumentError , " Unknown region: #{ options [ :region ] . inspect } "
end
2010-09-24 17:43:29 -04:00
@data = self . class . data [ options [ :region ] ] [ @aws_access_key_id ]
2010-09-08 17:40:02 -04:00
end
def signature ( params )
" foo "
end
end
class Real
include Utils
extend Fog :: Deprecation
deprecate ( :reset , :reload )
# Initialize connection to S3
#
# ==== Notes
2010-09-27 19:08:04 -04:00
# options parameter must include values for :aws_access_key_id and
2010-09-08 17:40:02 -04:00
# :aws_secret_access_key in order to create a connection
#
# ==== Examples
# s3 = S3.new(
# :aws_access_key_id => your_aws_access_key_id,
# :aws_secret_access_key => your_aws_secret_access_key
# )
#
# ==== Parameters
# * options<~Hash> - config arguments for connection. Defaults to {}.
#
# ==== Returns
# * S3 object with connection to aws.
def initialize ( options = { } )
2010-10-29 17:58:28 -04:00
require 'mime/types'
2010-09-08 17:40:02 -04:00
@aws_access_key_id = options [ :aws_access_key_id ]
@aws_secret_access_key = options [ :aws_secret_access_key ]
@hmac = Fog :: HMAC . new ( 'sha1' , @aws_secret_access_key )
2010-09-29 20:53:32 -04:00
if @endpoint = options [ :endpoint ]
endpoint = URI . parse ( @endpoint )
@host = endpoint . host
@path = endpoint . path
@port = endpoint . port
@scheme = endpoint . scheme
else
options [ :region ] || = 'us-east-1'
@host = options [ :host ] || case options [ :region ]
2010-09-23 13:48:52 -04:00
when 'eu-west-1'
's3-eu-west-1.amazonaws.com'
2010-09-08 17:40:02 -04:00
when 'us-east-1'
's3.amazonaws.com'
when 'ap-southeast-1'
's3-ap-southeast-1.amazonaws.com'
when 'us-west-1'
's3-us-west-1.amazonaws.com'
else
raise ArgumentError , " Unknown region: #{ options [ :region ] . inspect } "
end
2010-09-29 20:53:32 -04:00
@path = options [ :path ] || '/'
@port = options [ :port ] || 443
@scheme = options [ :scheme ] || 'https'
end
@connection = Fog :: Connection . new ( " #{ @scheme } :// #{ @host } : #{ @port } #{ @path } " , options [ :persistent ] || true )
2010-09-08 17:40:02 -04:00
end
def reload
@connection . reset
end
private
def request ( params , & block )
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 ) } "
2010-09-27 19:08:04 -04:00
params [ :expects ] = [ 307 , * params [ :expects ] ] . flatten
# FIXME: ToHashParser should make this not needed
original_params = params . dup
2010-09-08 17:40:02 -04:00
response = @connection . request ( params , & block )
2010-09-27 19:08:04 -04:00
if response . status == 307
uri = URI . parse ( response . headers [ 'Location' ] )
2010-09-27 23:44:46 -04:00
Formatador . display_line ( " [yellow][WARN] fog: followed redirect to #{ uri . host } , connecting to the matching region will be more performant[/] " )
2010-09-27 19:08:04 -04:00
response = Fog :: Connection . new ( " #{ @scheme } :// #{ uri . host } : #{ @port } " , false ) . request ( original_params , & block )
2010-09-27 16:23:21 -04:00
end
2010-09-08 17:40:02 -04:00
response
end
def signature ( params )
string_to_sign =
<<-DATA
#{params[:method]}
#{params[:headers]['Content-MD5']}
#{params[:headers]['Content-Type']}
#{params[:headers]['Date']}
DATA
amz_headers , canonical_amz_headers = { } , ''
for key , value in params [ :headers ]
if key [ 0 .. 5 ] == 'x-amz-'
amz_headers [ key ] = value
end
end
amz_headers = amz_headers . sort { | x , y | x [ 0 ] < = > y [ 0 ] }
for key , value in amz_headers
canonical_amz_headers << " #{ key } : #{ value } \n "
end
2010-11-15 20:17:37 -05:00
string_to_sign << canonical_amz_headers
2010-09-08 17:40:02 -04:00
subdomain = params [ :host ] . split ( " . #{ @host } " ) . first
unless subdomain =~ / ^(?:[a-z]| \ d(?! \ d{0,2}(?: \ . \ d{1,3}){3}$))(?:[a-z0-9]| \ .(?![ \ . \ -])| \ -(?![ \ .])){1,61}[a-z0-9]$ /
2010-09-22 17:29:35 -04:00
Formatador . display_line ( " [yellow][WARN] fog: the specified s3 bucket name( #{ subdomain } ) is not a valid dns name, which will negatively impact performance. For details see: http://docs.amazonwebservices.com/AmazonS3/latest/dev/BucketRestrictions.html[/] " )
2010-09-08 17:40:02 -04:00
params [ :host ] = params [ :host ] . split ( " #{ subdomain } . " ) [ - 1 ]
if params [ :path ]
params [ :path ] = " #{ subdomain } / #{ params [ :path ] } "
else
2010-11-15 20:17:37 -05:00
params [ :path ] = subdomain
2010-09-08 17:40:02 -04:00
end
subdomain = nil
end
2010-10-01 13:38:15 -04:00
canonical_resource = @path . dup
2010-09-08 17:40:02 -04:00
unless subdomain . nil? || subdomain == @host
canonical_resource << " #{ CGI . escape ( subdomain ) . downcase } / "
end
2010-11-15 20:17:37 -05:00
canonical_resource << params [ :path ] . to_s
2010-09-08 17:40:02 -04:00
canonical_resource << '?'
for key in ( params [ :query ] || { } ) . keys
2010-11-15 20:17:37 -05:00
if %w{ acl location logging notification partNumber policy requestPayment torrent uploadId uploads versionId versioning versions } . include? ( key )
canonical_resource << " #{ key } #{ " = #{ params [ :query ] [ key ] } " unless params [ :query ] [ key ] . nil? } & "
2010-09-08 17:40:02 -04:00
end
end
canonical_resource . chop!
2010-11-15 20:17:37 -05:00
string_to_sign << canonical_resource
2010-09-08 17:40:02 -04:00
signed_string = @hmac . sign ( string_to_sign )
signature = Base64 . encode64 ( signed_string ) . chomp!
end
end
end
end
end