waz-storage 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +1 -1
- data/README.rdoc +13 -9
- data/lib/waz/storage/core_service.rb +122 -122
- data/waz-storage.gemspec +1 -1
- metadata +4 -4
data/Gemfile.lock
CHANGED
data/README.rdoc
CHANGED
@@ -1,11 +1,3 @@
|
|
1
|
-
= Why this fork ?
|
2
|
-
|
3
|
-
The main difference with the original library is the adding of the method "railsetag".
|
4
|
-
This method add to any blob object an "x_ms_meta_railsetag" header, its value is calculated as the rails etag way (Digest::MD5.hexdigest(file))
|
5
|
-
|
6
|
-
By doing so we can easily know if a locale copy of a file is up-to-date on Azure CDN.
|
7
|
-
This is very much used in the "waz-sync" gem which purpose is to sync file between local storage and Azure.
|
8
|
-
|
9
1
|
= Windows Azure Storage library — simple gem for accessing WAZ‘s Storage REST API
|
10
2
|
A simple implementation of Windows Azure Storage API for Ruby, inspired by the S3 gems and self experience of dealing with queues. The major
|
11
3
|
goal of the whole gem is to enable ruby developers [like me =)] to leverage Windows Azure Storage features and have another option
|
@@ -23,6 +15,18 @@ Full documentation for the gem is available at http://waz-storage.heroku.com
|
|
23
15
|
Well, this is a sum up of the whole experience of writing those gems and getting them to work together to simplify
|
24
16
|
end user experience. Although there're some breaking changes, it's pretty backward compatible with existing gems.
|
25
17
|
|
18
|
+
=== What's new on v1.1.3?
|
19
|
+
|
20
|
+
- Fixed an issue with URL enconding (thanks @survili)
|
21
|
+
|
22
|
+
=== What's new on v1.1.2?
|
23
|
+
|
24
|
+
The main difference with the original library is the adding of the method "railsetag".
|
25
|
+
This method add to any blob object an "x_ms_meta_railsetag" header, its value is calculated as the rails etag way (Digest::MD5.hexdigest(file))
|
26
|
+
|
27
|
+
By doing so we can easily know if a locale copy of a file is up-to-date on Azure CDN.
|
28
|
+
This is very much used in the "waz-sync" gem which purpose is to sync file between local storage and Azure.
|
29
|
+
|
26
30
|
=== What's new on the v1.1 version? [thanks to smarx]
|
27
31
|
- Upload from stream, plus XML/URI escaping in various places, and a few other minor fixes
|
28
32
|
- Add upload method to container
|
@@ -298,7 +302,7 @@ The things listed above do not represent any sort of priority, or the order they
|
|
298
302
|
|
299
303
|
Written by Johnny G. Halife (johnny.halife at me dot com)
|
300
304
|
|
301
|
-
contributed by: Ezequiel Morito (http://twitter.com/ezequielm), Juan Pablo Garcia (http://twitter.com/jpgd), Steve Marx (http://twitter.com/smarx)
|
305
|
+
contributed by: Ezequiel Morito (http://twitter.com/ezequielm), Juan Pablo Garcia (http://twitter.com/jpgd), Steve Marx (http://twitter.com/smarx), G. Montard (http://github.com/gmontard)
|
302
306
|
|
303
307
|
Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
|
304
308
|
|
@@ -1,122 +1,122 @@
|
|
1
|
-
module WAZ
|
2
|
-
module Storage
|
3
|
-
# This module is imported by the specific services that use Shared Key authentication profile. On the current implementation
|
4
|
-
# this module is imported from WAZ::Queues::Service and WAZ::Blobs::Service.
|
5
|
-
module SharedKeyCoreService
|
6
|
-
attr_accessor :account_name, :access_key, :use_ssl, :base_url, :type_of_service, :use_devenv, :use_sas_auth_only, :sharedaccesssignature
|
7
|
-
|
8
|
-
# Creates an instance of the implementor service (internally used by the API).
|
9
|
-
def initialize(options = {})
|
10
|
-
# Flag to define the use of shared access signature only
|
11
|
-
self.use_sas_auth_only = options[:use_sas_auth_only] or false
|
12
|
-
self.sharedaccesssignature = options[:sharedaccesssignature]
|
13
|
-
|
14
|
-
self.account_name = options[:account_name]
|
15
|
-
self.access_key = options[:access_key]
|
16
|
-
self.type_of_service = options[:type_of_service]
|
17
|
-
self.use_ssl = options[:use_ssl] or false
|
18
|
-
self.use_devenv = !!options[:use_devenv]
|
19
|
-
self.base_url = "#{options[:type_of_service] or "blobs"}.#{options[:base_url] or "core.windows.net"}" unless self.use_devenv
|
20
|
-
self.base_url ||= (options[:base_url] or "core.windows.net")
|
21
|
-
end
|
22
|
-
|
23
|
-
# Generates a request based on Adam Wiggings' rest-client, including all the required headers
|
24
|
-
# for interacting with Windows Azure Storage API (except for Tables). This methods embeds the
|
25
|
-
# authorization key signature on the request based on the given access_key.
|
26
|
-
def generate_request(verb, url, headers = {}, payload = nil)
|
27
|
-
http_headers = {}
|
28
|
-
headers.each{ |k, v| http_headers[k.to_s.gsub(/_/, '-')] = v} unless headers.nil?
|
29
|
-
http_headers.merge!("x-ms-Date" => Time.new.httpdate)
|
30
|
-
http_headers.merge!("Content-Length" => (payload or "").size)
|
31
|
-
request = {:headers => http_headers, :method => verb.to_s.downcase.to_sym, :url => url, :payload => payload}
|
32
|
-
request[:headers].merge!("Authorization" => "SharedKey #{account_name}:#{generate_signature(request)}") unless self.use_sas_auth_only
|
33
|
-
return RestClient::Request.new(request)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Generates the request uri based on the resource path, the protocol, the account name and the parameters passed
|
37
|
-
# on the options hash.
|
38
|
-
def generate_request_uri(path = nil, options = {})
|
39
|
-
protocol = use_ssl ? "https" : "http"
|
40
|
-
query_params = options.keys.sort{ |a, b| a.to_s <=> b.to_s}.map{ |k| "#{k.to_s.gsub(/_/, '')}=#{CGI.escape(options[k].to_s)}"}.join("&") unless options.nil? or options.empty?
|
41
|
-
uri = "#{protocol}://#{base_url}/#{path.start_with?(account_name) ? "" : account_name }#{((path or "").start_with?("/") or path.start_with?(account_name)) ? "" : "/"}#{(path or "")}" if !self.use_devenv.nil? and self.use_devenv
|
42
|
-
uri ||= "#{protocol}://#{account_name}.#{base_url}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}"
|
43
|
-
if self.use_sas_auth_only
|
44
|
-
uri << "?#{self.sharedaccesssignature.gsub(/\?/,'')}"
|
45
|
-
else
|
46
|
-
uri << "?#{query_params}" if query_params
|
47
|
-
end
|
48
|
-
return uri
|
49
|
-
end
|
50
|
-
|
51
|
-
# Canonicalizes the request headers by following Microsoft's specification on how those headers have to be sorted
|
52
|
-
# and which of the given headers apply to be canonicalized.
|
53
|
-
def canonicalize_headers(headers)
|
54
|
-
cannonicalized_headers = headers.keys.select {|h| h.to_s.start_with? 'x-ms'}.map{ |h| "#{h.downcase.strip}:#{headers[h].strip}" }.sort{ |a, b| a <=> b }.join("\x0A")
|
55
|
-
return cannonicalized_headers
|
56
|
-
end
|
57
|
-
|
58
|
-
# Creates a canonical representation of the message by combining account_name/resource_path.
|
59
|
-
def canonicalize_message(url)
|
60
|
-
uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
|
61
|
-
comp_component = url.scan(/comp=[^&]+/i).first()
|
62
|
-
uri_component << "?#{comp_component}" if comp_component
|
63
|
-
canonicalized_message = "/#{self.account_name}/#{uri_component}"
|
64
|
-
return canonicalized_message
|
65
|
-
end
|
66
|
-
|
67
|
-
# Generates the signature based on Micosoft specs for the REST API. It includes some special headers,
|
68
|
-
# the canonicalized header line and the canonical form of the message, all of the joined by \n character. Encoded with
|
69
|
-
# Base64 and encrypted with SHA256 using the access_key as the seed.
|
70
|
-
def generate_signature(options = {})
|
71
|
-
return generate_signature20090919(options) if options[:headers]["x-ms-version"] == "2011-08-18"
|
72
|
-
|
73
|
-
signature = options[:method].to_s.upcase + "\x0A" +
|
74
|
-
(options[:headers]["Content-MD5"] or "") + "\x0A" +
|
75
|
-
(options[:headers]["Content-Type"] or "") + "\x0A" +
|
76
|
-
(options[:headers]["Date"] or "")+ "\x0A"
|
77
|
-
|
78
|
-
signature += canonicalize_headers(options[:headers]) + "\x0A" unless self.type_of_service == 'table'
|
79
|
-
signature += canonicalize_message(options[:url])
|
80
|
-
signature = signature.toutf8 if(signature.respond_to? :toutf8)
|
81
|
-
Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature).digest)
|
82
|
-
end
|
83
|
-
|
84
|
-
def generate_signature20090919(options = {})
|
85
|
-
signature = options[:method].to_s.upcase + "\x0A" +
|
86
|
-
(options[:headers]["Content-Encoding"] or "") + "\x0A" +
|
87
|
-
(options[:headers]["Content-Language"] or "") + "\x0A" +
|
88
|
-
(options[:headers]["Content-Length"] or "").to_s + "\x0A" +
|
89
|
-
(options[:headers]["Content-MD5"] or "") + "\x0A" +
|
90
|
-
(options[:headers]["Content-Type"] or "") + "\x0A" +
|
91
|
-
(options[:headers]["Date"] or "")+ "\x0A" +
|
92
|
-
(options[:headers]["If-Modified-Since"] or "")+ "\x0A" +
|
93
|
-
(options[:headers]["If-Match"] or "")+ "\x0A" +
|
94
|
-
(options[:headers]["If-None-Match"] or "")+ "\x0A" +
|
95
|
-
(options[:headers]["If-Unmodified-Since"] or "")+ "\x0A" +
|
96
|
-
(options[:headers]["Range"] or "")+ "\x0A" +
|
97
|
-
canonicalize_headers(options[:headers]) + "\x0A" +
|
98
|
-
canonicalize_message20090919(options[:url])
|
99
|
-
|
100
|
-
signature = signature.toutf8 if(signature.respond_to? :toutf8)
|
101
|
-
Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature).digest)
|
102
|
-
end
|
103
|
-
|
104
|
-
def canonicalize_message20090919(url)
|
105
|
-
uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
|
106
|
-
query_component = (url.scan(/\?(.*)/i).first() or []).first()
|
107
|
-
query_component = query_component.split('&').sort{|a, b| a <=> b}.map{ |p| CGI::unescape(p.split('=').join(':')) }.join("\n") if query_component
|
108
|
-
canonicalized_message = "/#{self.account_name}/#{uri_component}"
|
109
|
-
canonicalized_message << "\n#{query_component}" if query_component
|
110
|
-
return canonicalized_message
|
111
|
-
end
|
112
|
-
|
113
|
-
# Generates a Windows Azure Storage call, it internally calls url generation method
|
114
|
-
# and the request generation message.
|
115
|
-
def execute(verb, path, query = {}, headers = {}, payload = nil)
|
116
|
-
url = generate_request_uri(path, query)
|
117
|
-
request = generate_request(verb, url, headers, payload)
|
118
|
-
request.execute()
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
1
|
+
module WAZ
|
2
|
+
module Storage
|
3
|
+
# This module is imported by the specific services that use Shared Key authentication profile. On the current implementation
|
4
|
+
# this module is imported from WAZ::Queues::Service and WAZ::Blobs::Service.
|
5
|
+
module SharedKeyCoreService
|
6
|
+
attr_accessor :account_name, :access_key, :use_ssl, :base_url, :type_of_service, :use_devenv, :use_sas_auth_only, :sharedaccesssignature
|
7
|
+
|
8
|
+
# Creates an instance of the implementor service (internally used by the API).
|
9
|
+
def initialize(options = {})
|
10
|
+
# Flag to define the use of shared access signature only
|
11
|
+
self.use_sas_auth_only = options[:use_sas_auth_only] or false
|
12
|
+
self.sharedaccesssignature = options[:sharedaccesssignature]
|
13
|
+
|
14
|
+
self.account_name = options[:account_name]
|
15
|
+
self.access_key = options[:access_key]
|
16
|
+
self.type_of_service = options[:type_of_service]
|
17
|
+
self.use_ssl = options[:use_ssl] or false
|
18
|
+
self.use_devenv = !!options[:use_devenv]
|
19
|
+
self.base_url = "#{options[:type_of_service] or "blobs"}.#{options[:base_url] or "core.windows.net"}" unless self.use_devenv
|
20
|
+
self.base_url ||= (options[:base_url] or "core.windows.net")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Generates a request based on Adam Wiggings' rest-client, including all the required headers
|
24
|
+
# for interacting with Windows Azure Storage API (except for Tables). This methods embeds the
|
25
|
+
# authorization key signature on the request based on the given access_key.
|
26
|
+
def generate_request(verb, url, headers = {}, payload = nil)
|
27
|
+
http_headers = {}
|
28
|
+
headers.each{ |k, v| http_headers[k.to_s.gsub(/_/, '-')] = v} unless headers.nil?
|
29
|
+
http_headers.merge!("x-ms-Date" => Time.new.httpdate)
|
30
|
+
http_headers.merge!("Content-Length" => (payload or "").size)
|
31
|
+
request = {:headers => http_headers, :method => verb.to_s.downcase.to_sym, :url => url, :payload => payload}
|
32
|
+
request[:headers].merge!("Authorization" => "SharedKey #{account_name}:#{generate_signature(request)}") unless self.use_sas_auth_only
|
33
|
+
return RestClient::Request.new(request)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generates the request uri based on the resource path, the protocol, the account name and the parameters passed
|
37
|
+
# on the options hash.
|
38
|
+
def generate_request_uri(path = nil, options = {})
|
39
|
+
protocol = use_ssl ? "https" : "http"
|
40
|
+
query_params = options.keys.sort{ |a, b| a.to_s <=> b.to_s}.map{ |k| "#{k.to_s.gsub(/_/, '')}=#{CGI.escape(options[k].to_s)}"}.join("&") unless options.nil? or options.empty?
|
41
|
+
uri = "#{protocol}://#{base_url}/#{path.start_with?(account_name) ? "" : account_name }#{((path or "").start_with?("/") or path.start_with?(account_name)) ? "" : "/"}#{(path or "")}" if !self.use_devenv.nil? and self.use_devenv
|
42
|
+
uri ||= "#{protocol}://#{account_name}.#{base_url}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}"
|
43
|
+
if self.use_sas_auth_only
|
44
|
+
uri << "?#{self.sharedaccesssignature.gsub(/\?/,'')}"
|
45
|
+
else
|
46
|
+
uri << "?#{query_params}" if query_params
|
47
|
+
end
|
48
|
+
return uri
|
49
|
+
end
|
50
|
+
|
51
|
+
# Canonicalizes the request headers by following Microsoft's specification on how those headers have to be sorted
|
52
|
+
# and which of the given headers apply to be canonicalized.
|
53
|
+
def canonicalize_headers(headers)
|
54
|
+
cannonicalized_headers = headers.keys.select {|h| h.to_s.start_with? 'x-ms'}.map{ |h| "#{h.downcase.strip}:#{headers[h].strip}" }.sort{ |a, b| a <=> b }.join("\x0A")
|
55
|
+
return cannonicalized_headers
|
56
|
+
end
|
57
|
+
|
58
|
+
# Creates a canonical representation of the message by combining account_name/resource_path.
|
59
|
+
def canonicalize_message(url)
|
60
|
+
uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
|
61
|
+
comp_component = url.scan(/comp=[^&]+/i).first()
|
62
|
+
uri_component << "?#{comp_component}" if comp_component
|
63
|
+
canonicalized_message = "/#{self.account_name}/#{uri_component}"
|
64
|
+
return canonicalized_message
|
65
|
+
end
|
66
|
+
|
67
|
+
# Generates the signature based on Micosoft specs for the REST API. It includes some special headers,
|
68
|
+
# the canonicalized header line and the canonical form of the message, all of the joined by \n character. Encoded with
|
69
|
+
# Base64 and encrypted with SHA256 using the access_key as the seed.
|
70
|
+
def generate_signature(options = {})
|
71
|
+
return generate_signature20090919(options) if options[:headers]["x-ms-version"] == "2011-08-18"
|
72
|
+
|
73
|
+
signature = options[:method].to_s.upcase + "\x0A" +
|
74
|
+
(options[:headers]["Content-MD5"] or "") + "\x0A" +
|
75
|
+
(options[:headers]["Content-Type"] or "") + "\x0A" +
|
76
|
+
(options[:headers]["Date"] or "")+ "\x0A"
|
77
|
+
|
78
|
+
signature += canonicalize_headers(options[:headers]) + "\x0A" unless self.type_of_service == 'table'
|
79
|
+
signature += canonicalize_message(options[:url])
|
80
|
+
signature = signature.toutf8 if(signature.respond_to? :toutf8)
|
81
|
+
Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature).digest)
|
82
|
+
end
|
83
|
+
|
84
|
+
def generate_signature20090919(options = {})
|
85
|
+
signature = options[:method].to_s.upcase + "\x0A" +
|
86
|
+
(options[:headers]["Content-Encoding"] or "") + "\x0A" +
|
87
|
+
(options[:headers]["Content-Language"] or "") + "\x0A" +
|
88
|
+
(options[:headers]["Content-Length"] or "").to_s + "\x0A" +
|
89
|
+
(options[:headers]["Content-MD5"] or "") + "\x0A" +
|
90
|
+
(options[:headers]["Content-Type"] or "") + "\x0A" +
|
91
|
+
(options[:headers]["Date"] or "")+ "\x0A" +
|
92
|
+
(options[:headers]["If-Modified-Since"] or "")+ "\x0A" +
|
93
|
+
(options[:headers]["If-Match"] or "")+ "\x0A" +
|
94
|
+
(options[:headers]["If-None-Match"] or "")+ "\x0A" +
|
95
|
+
(options[:headers]["If-Unmodified-Since"] or "")+ "\x0A" +
|
96
|
+
(options[:headers]["Range"] or "")+ "\x0A" +
|
97
|
+
canonicalize_headers(options[:headers]) + "\x0A" +
|
98
|
+
canonicalize_message20090919(options[:url])
|
99
|
+
|
100
|
+
signature = signature.toutf8 if(signature.respond_to? :toutf8)
|
101
|
+
Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature).digest)
|
102
|
+
end
|
103
|
+
|
104
|
+
def canonicalize_message20090919(url)
|
105
|
+
uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
|
106
|
+
query_component = (url.scan(/\?(.*)/i).first() or []).first()
|
107
|
+
query_component = query_component.split('&').sort{|a, b| a <=> b}.map{ |p| CGI::unescape(p.split('=').join(':')) }.join("\n") if query_component
|
108
|
+
canonicalized_message = "/#{self.account_name}/#{uri_component}"
|
109
|
+
canonicalized_message << "\n#{query_component}" if query_component
|
110
|
+
return canonicalized_message
|
111
|
+
end
|
112
|
+
|
113
|
+
# Generates a Windows Azure Storage call, it internally calls url generation method
|
114
|
+
# and the request generation message.
|
115
|
+
def execute(verb, path, query = {}, headers = {}, payload = nil)
|
116
|
+
url = generate_request_uri(path, query)
|
117
|
+
request = generate_request(verb, URI.encode(url), headers, payload)
|
118
|
+
request.execute()
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/waz-storage.gemspec
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waz-storage
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 1.1.
|
9
|
+
- 3
|
10
|
+
version: 1.1.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Johnny G. Halife
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-06-04 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rest-client
|