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 CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- waz-storage (1.1.1)
4
+ waz-storage (1.1.3)
5
5
  rest-client
6
6
  ruby-hmac
7
7
 
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
@@ -11,7 +11,7 @@ Gem::Specification.new do |gem|
11
11
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
12
12
  gem.name = "waz-storage"
13
13
  gem.require_paths = ["lib"]
14
- gem.version = "1.1.2"
14
+ gem.version = "1.1.3"
15
15
 
16
16
  gem.test_files = Dir['tests/**/*']
17
17
 
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: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 2
10
- version: 1.1.2
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-05-09 00:00:00 Z
18
+ date: 2012-06-04 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: rest-client