Module: WAZ::Storage::SharedKeyCoreService

Included in:
Blobs::Service, Queues::Service, Tables::Service
Defined in:
lib/waz/storage/core_service.rb

Overview

This module is imported by the specific services that use Shared Key authentication profile. On the current implementation this module is imported from WAZ::Queues::Service and WAZ::Blobs::Service.

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#access_keyObject

Returns the value of attribute access_key.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def access_key
  @access_key
end

#account_nameObject

Returns the value of attribute account_name.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def 
  @account_name
end

#base_urlObject

Returns the value of attribute base_url.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def base_url
  @base_url
end

#type_of_serviceObject

Returns the value of attribute type_of_service.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def type_of_service
  @type_of_service
end

#use_devenvObject

Returns the value of attribute use_devenv.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def use_devenv
  @use_devenv
end

#use_sslObject

Returns the value of attribute use_ssl.



6
7
8
# File 'lib/waz/storage/core_service.rb', line 6

def use_ssl
  @use_ssl
end

Instance Method Details

#canonicalize_headers(headers) ⇒ Object

Canonicalizes the request headers by following Microsoft’s specification on how those headers have to be sorted and which of the given headers apply to be canonicalized.



45
46
47
48
# File 'lib/waz/storage/core_service.rb', line 45

def canonicalize_headers(headers)
  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")
  return cannonicalized_headers
end

#canonicalize_message(url) ⇒ Object

Creates a canonical representation of the message by combining account_name/resource_path.



51
52
53
54
55
56
57
# File 'lib/waz/storage/core_service.rb', line 51

def canonicalize_message(url)
  uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
  comp_component = url.scan(/(comp=[^&]+)/i).first()
  uri_component << "?#{comp_component}" if comp_component
  canonicalized_message = "/#{self.}/#{uri_component}"
  return canonicalized_message
end

#canonicalize_message20090919(url) ⇒ Object



95
96
97
98
99
100
101
102
# File 'lib/waz/storage/core_service.rb', line 95

def canonicalize_message20090919(url)
  uri_component = url.gsub(/https?:\/\/[^\/]+\//i, '').gsub(/\?.*/i, '')
  query_component = (url.scan(/\?(.*)/i).first() or []).first()
  query_component = query_component.split('&').sort{|a, b| a <=> b}.map{ |p| p.split('=').join(':') }.join("\n") if query_component
  canonicalized_message = "/#{self.}/#{uri_component}"
  canonicalized_message << "\n#{query_component}" if query_component
  return canonicalized_message
end

#execute(verb, path, query = {}, headers = {}, payload = nil) ⇒ Object

Generates a Windows Azure Storage call, it internally calls url generation method and the request generation message.



106
107
108
109
110
# File 'lib/waz/storage/core_service.rb', line 106

def execute(verb, path, query = {}, headers = {}, payload = nil)
  url = generate_request_uri(path, query)
  request = generate_request(verb, url, headers, payload)
  request.execute()
end

#generate_request(verb, url, headers = {}, payload = nil) ⇒ Object

Generates a request based on Adam Wiggings’ rest-client, including all the required headers for interacting with Windows Azure Storage API (except for Tables). This methods embeds the authorization key signature on the request based on the given access_key.



22
23
24
25
26
27
28
29
30
# File 'lib/waz/storage/core_service.rb', line 22

def generate_request(verb, url, headers = {}, payload = nil)
  http_headers = {}
  headers.each{ |k, v| http_headers[k.to_s.gsub(/_/, '-')] = v} unless headers.nil?
  http_headers.merge!("x-ms-Date" => Time.new.httpdate)
  http_headers.merge!("Content-Length" => (payload or "").length)
  request = {:headers => http_headers, :method => verb.to_s.downcase.to_sym, :url => url, :payload => payload}
  request[:headers].merge!("Authorization" => "SharedKey #{}:#{generate_signature(request)}")
  return RestClient::Request.new(request)
end

#generate_request_uri(path = nil, options = {}) ⇒ Object

Generates the request uri based on the resource path, the protocol, the account name and the parameters passed on the options hash.



34
35
36
37
38
39
40
41
# File 'lib/waz/storage/core_service.rb', line 34

def generate_request_uri(path = nil, options = {})
  protocol = use_ssl ? "https" : "http"
  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?
  uri = "#{protocol}://#{base_url}/#{path.start_with?() ? "" :  }#{((path or "").start_with?("/") or path.start_with?()) ? "" : "/"}#{(path or "")}" if !self.use_devenv.nil? and self.use_devenv
  uri ||= "#{protocol}://#{}.#{base_url}#{(path or "").start_with?("/") ? "" : "/"}#{(path or "")}" 
  uri << "?#{query_params}" if query_params
  return uri
end

#generate_signature(options = {}) ⇒ Object

Generates the signature based on Micosoft specs for the REST API. It includes some special headers, the canonicalized header line and the canonical form of the message, all of the joined by n character. Encoded with Base64 and encrypted with SHA256 using the access_key as the seed.



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/waz/storage/core_service.rb', line 62

def generate_signature(options = {})
  return generate_signature20090919(options) if options[:headers]["x-ms-version"] == "2009-09-19"

  signature = options[:method].to_s.upcase + "\x0A" +
               (options[:headers]["Content-MD5"] or "") + "\x0A" +
               (options[:headers]["Content-Type"] or "") + "\x0A" +
               (options[:headers]["Date"] or "")+ "\x0A"

  signature += canonicalize_headers(options[:headers]) + "\x0A" unless self.type_of_service == 'table'
  signature += canonicalize_message(options[:url])
               
  Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature.toutf8).digest)
end

#generate_signature20090919(options = {}) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/waz/storage/core_service.rb', line 76

def generate_signature20090919(options = {})
  signature = options[:method].to_s.upcase + "\x0A" +
              (options[:headers]["Content-Encoding"] or "") + "\x0A" +
              (options[:headers]["Content-Language"] or "") + "\x0A" +
              (options[:headers]["Content-Length"] or "").to_s + "\x0A" +                    
              (options[:headers]["Content-MD5"] or "") + "\x0A" +
              (options[:headers]["Content-Type"] or "") + "\x0A" +
              (options[:headers]["Date"] or "")+ "\x0A" +
              (options[:headers]["If-Modified-Since"] or "")+ "\x0A" +
              (options[:headers]["If-Match"] or "")+ "\x0A" +
              (options[:headers]["If-None-Match"] or "")+ "\x0A" +                    
              (options[:headers]["If-Unmodified-Since"] or "")+ "\x0A" +
              (options[:headers]["Range"] or "")+ "\x0A" +                    
              canonicalize_headers(options[:headers]) + "\x0A" +
              canonicalize_message20090919(options[:url])
              
  Base64.encode64(HMAC::SHA256.new(Base64.decode64(self.access_key)).update(signature.toutf8).digest)        
end

#initialize(options = {}) ⇒ Object

Creates an instance of the implementor service (internally used by the API).



9
10
11
12
13
14
15
16
17
# File 'lib/waz/storage/core_service.rb', line 9

def initialize(options = {})
  self. = options[:account_name]
  self.access_key = options[:access_key]
  self.type_of_service = options[:type_of_service]        
  self.use_ssl = options[:use_ssl] or false
  self.use_devenv = !!options[:use_devenv]
  self.base_url = "#{options[:type_of_service] or "blobs"}.#{options[:base_url] or "core.windows.net"}" unless self.use_devenv
  self.base_url ||= (options[:base_url] or "core.windows.net") 
end