Module: Visor::Common::Util

Extended by:
Util
Included in:
Util
Defined in:
lib/common/util.rb

Overview

The Util module provides a set of utility functions used along all VISOR subsystems.

Instance Method Summary collapse

Instance Method Details

#authorize(env, vas) ⇒ String

Authenticate an user request by analysing the request authorization string.

Parameters:

  • env (Hash)

    The request attributes.

  • vas (Visor::Image::Auth)

    A VAS interface object, used to query for user credentials.

Returns:

  • (String)

    The authenticated user access key.

Raises:

  • (Forbidden)

    If authorization header was not provided along the request.

  • (Forbidden)

    If no access key found in the authorization header string.

  • (Forbidden)

    If no user found with the given access key.

  • (Forbidden)

    If signatures do not match.

  • (InternalError)

    If VAS server was not found.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/common/util.rb', line 147

def authorize(env, vas)
  auth = env['headers']['Authorization']
  raise Visor::Common::Exception::Forbidden, "Authorization not provided." unless auth
  access_key = auth.scan(/\ (\w+):/).flatten.first
  raise Visor::Common::Exception::Forbidden, "No access key found in Authorization." unless access_key
  begin
    user = vas.get_user(access_key)
  rescue Visor::Common::Exception::InternalError => e
    raise Visor::Common::Exception::InternalError, e.message
  rescue => e
    nil
  end
  raise Visor::Common::Exception::Forbidden, "No user found with access key '#{access_key}'." unless user
  sign = sign_request(user[:access_key], user[:secret_key], env['REQUEST_METHOD'], env['REQUEST_PATH'], env['headers'])
  raise Visor::Common::Exception::Forbidden, "Invalid authorization, signatures do not match." unless auth == sign
  access_key
end

#canonical_description(method, path, headers = {}) ⇒ String

Generate a request canonical description, which will be used by #sign_request.

Parameters:

  • method (String)

    The request method.

  • path (String)

    The request path.

  • headers (Hash) (defaults to: {})

    The request headers.

Returns:

  • (String)

    The request canonical description string.



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/common/util.rb', line 117

def canonical_description(method, path, headers={})
  attributes = {}
  headers.each do |key, value|
    key = key.downcase
    attributes[key] = value.to_s.strip if key.match(/^x-image-meta-|^content-md5$|^content-type$|^date$/o)
  end

  attributes['content-type'] ||= ''
  attributes['content-md5']  ||= ''

  desc = "#{method}\n"
  attributes.sort { |a, b| a[0] <=> b[0] }.each do |key, value|
    desc << (key.match(/^x-image-meta-/o) ? "#{key}:#{value}\n" : "#{value}\n")
  end
  desc << path.gsub(/\?.*$/, '')
end

#is_date?(object) ⇒ Date, NilClass

Find if a an object can be converted to a date.

Parameters:

  • object (Object)

    The object to be converted to a date.

Returns:

  • (Date, NilClass)

    The converted float, or nil if it can not be converted to a date.



88
89
90
91
# File 'lib/common/util.rb', line 88

def is_date?(object)
  regexp = /\d{4}[-\/]\d{1,2}[-\/]\d{1,2}\s\d{2}:\d{2}:\d{2}\s\W\d{4}/
  object.match(regexp) ? true : false
end

#is_float?(object) ⇒ Float, NilClass

Find if a an object can be converted to a float.

Parameters:

  • object (Object)

    The object to be converted to a float.

Returns:

  • (Float, NilClass)

    The converted float, or nil if it can not be converted to a float.



78
79
80
# File 'lib/common/util.rb', line 78

def is_float?(object)
  true if Float(object) rescue false
end

#is_integer?(object) ⇒ Integer, NilClass

Find if a an object can be converted to an integer.

Parameters:

  • object (Object)

    The object to be converted to integer.

Returns:

  • (Integer, NilClass)

    The converted integer, or nil if it can not be converted to integer.



68
69
70
# File 'lib/common/util.rb', line 68

def is_integer?(object)
  true if Integer(object) rescue false
end

#parse_value(string) ⇒ Object

Find if a string value is an integer, a float or a date. If it matches a type, then it is converted to that type and returned.

Parameters:

  • string (String)

    The string to be parsed.

Returns:

  • (Object)

    The already converted string value.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/common/util.rb', line 50

def parse_value(string)
  if is_integer?(string) then
    Integer(string)
  elsif is_float?(string) then
    Float(object)
  elsif is_date?(string) then
    Time.parse(string)
  else
    string
  end
end

#pull_meta_from_headers(headers) ⇒ Hash

Pull image metadata from HTTP headers to a hash.

Parameters:

  • headers (Hash)

    (nil) The HTTP headers hash

Returns:

  • (Hash)

    The header containing the metadata



32
33
34
35
36
37
38
39
40
41
# File 'lib/common/util.rb', line 32

def pull_meta_from_headers(headers)
  meta = {}
  headers.each do |k, v|
    if key = k.split(/x[_-]image[_-]meta[_-]/i)[1]
      value                     = parse_value v
      meta[key.downcase.to_sym] = value
    end
  end
  meta
end

#push_meta_into_headers(meta, headers = {}) ⇒ Hash

Push a hash containing image metadata into an HTTP header. Each key value pair is pushed as a string of the form ‘x-image-meta-<key>’.

Parameters:

  • meta (Hash)

    The image metadata

  • headers (Hash) (defaults to: {})

    (nil) The HTTP headers hash

Returns:

  • (Hash)

    The header containing the metadata headers



21
22
23
24
# File 'lib/common/util.rb', line 21

def push_meta_into_headers(meta, headers = {})
  meta.each { |k, v| headers["x-image-meta-#{k.to_s.downcase}"] = v.to_s }
  headers
end

#sign_request(access_key, secret_key, method, path, headers = {}) ⇒ Object

Sign a request by generating an authorization string and embedding it in the request headers.

Parameters:

  • access_key (String)

    The requester user access key.

  • secret_key (String)

    The requester user secret key.

  • method (String)

    The request method.

  • path (String)

    The request path.

  • headers (Hash) (defaults to: {})

    The request headers.



101
102
103
104
105
106
107
# File 'lib/common/util.rb', line 101

def sign_request(access_key, secret_key, method, path, headers={})
  headers['Date'] ||= Time.now.utc.httpdate
  desc            = canonical_description(method, path, headers)
  signature       = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha1'), secret_key, desc)).strip

  headers['Authorization'] = "VISOR #{access_key}:#{signature}"
end