Class: HmacRequest

Inherits:
Object
  • Object
show all
Defined in:
lib/hmac_request.rb

Constant Summary collapse

HTTP_HEADER_TO_ENV_MAP =
{ 'Content-Type'  => 'CONTENT_TYPE', 
'Content-MD5'   => 'CONTENT_MD5', 
'Date'          => 'HTTP_DATE', 
'Method'        => 'REQUEST_METHOD', 
'Authorization' => 'HTTP_AUTHORIZATION' }
@@valid_date_window =

seconds

600

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(env) ⇒ HmacRequest

Returns a new instance of HmacRequest.



33
34
35
36
37
# File 'lib/hmac_request.rb', line 33

def initialize(env)
  @request = Rack::Request.new(env)
  @env = @request.env
  @body = @request.body if has_body?(@env['REQUEST_METHOD'])
end

Instance Attribute Details

#bodyObject

Returns the value of attribute body.



7
8
9
# File 'lib/hmac_request.rb', line 7

def body
  @body
end

#envObject

Returns the value of attribute env.



7
8
9
# File 'lib/hmac_request.rb', line 7

def env
  @env
end

#hmac_hashObject

Returns the value of attribute hmac_hash.



7
8
9
# File 'lib/hmac_request.rb', line 7

def hmac_hash
  @hmac_hash
end

#hmac_idObject

Returns the value of attribute hmac_id.



7
8
9
# File 'lib/hmac_request.rb', line 7

def hmac_id
  @hmac_id
end

#requestObject

Returns the value of attribute request.



7
8
9
# File 'lib/hmac_request.rb', line 7

def request
  @request
end

Class Method Details

.new_from_rack_env(env) ⇒ Object



18
19
20
21
# File 'lib/hmac_request.rb', line 18

def self.new_from_rack_env(env)
  r = self.new(env)
  return r
end

.new_from_rails_request(request) ⇒ Object



23
24
25
26
27
28
29
30
31
# File 'lib/hmac_request.rb', line 23

def self.new_from_rails_request(request)
  r = self.new(request.headers)
  #pull stuff out of X-Referer, which is where the middleware sticks it
  HTTP_HEADER_TO_ENV_MAP.each_pair do |h,e|
    r.env[e] = r.env["X-Referer-#{h}"] unless r.env["X-Referer-#{h}"].blank?
  end

  return r
end

Instance Method Details

#[](key) ⇒ Object



39
40
41
# File 'lib/hmac_request.rb', line 39

def [](key)
  @request[key]
end

#date_is_recent?Boolean

Returns:

  • (Boolean)


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/hmac_request.rb', line 83

def date_is_recent?()
  req_date = nil

  begin
    req_date = Time.httpdate(@env['HTTP_DATE'])
  rescue Exception => ex
    if ex.message =~ /not RFC 2616 compliant/
      # try rfc2822
      req_date = Time.rfc2822(@env['HTTP_DATE'])
    else
      raise ex
    end
  end

  if Time.now.to_i - req_date.to_i >= @@valid_date_window
    log "Request date #{req_date} is more than #{@@valid_date_window} seconds old"
    return false
  else
    return true
  end
end

#get_hmac_valsObject



66
67
68
69
70
# File 'lib/hmac_request.rb', line 66

def get_hmac_vals
  @env['HTTP_AUTHORIZATION'].to_s =~ /^AuthHMAC ([^:]+):(.*)$/
  @hmac_id = $1
  @hmac_hash = $2
end

#get_xreferer_auth_headersObject

these are the X-Referer-Headers that get passed along to lockbox from the middleware for authentication



106
107
108
109
110
111
112
113
114
115
116
# File 'lib/hmac_request.rb', line 106

def get_xreferer_auth_headers()
  headers = {}    
  headers['Referer'] = "#{@env['rack.url_scheme']}://#{@env['SERVER_NAME']}#{@env['PATH_INFO']}"
  headers['Referer'] << "?#{@env['QUERY_STRING']}" unless @env['QUERY_STRING'].blank?
  HTTP_HEADER_TO_ENV_MAP.each_pair do |h,e|
    headers["X-Referer-#{h}"] = @env[e] unless @env[e].blank?
  end
  headers['X-Referer-Content-MD5'] = Digest::MD5.hexdigest(@request.body.read) if @env['CONTENT_TYPE']
  headers["X-Referer-Date"] = @env['HTTP_X_AUTHHMAC_REQUEST_DATE'] unless @env['HTTP_X_AUTHHMAC_REQUEST_DATE'].blank?
  headers
end

#has_body?(method) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/hmac_request.rb', line 52

def has_body?(method)
  ["PUT","POST"].include?(method)
end

#hmac_auth(credential_store) ⇒ Object



73
74
75
76
77
78
79
80
81
# File 'lib/hmac_request.rb', line 73

def hmac_auth(credential_store)
  authhmac = AuthHMAC.new(credential_store)
  if authhmac.authenticated?(self) && (@env['HTTP_DATE'].blank? || self.date_is_recent? )
    credential_store[self.hmac_id]
  else
    log_auth_error(credential_store[self.hmac_id])
    return false
  end
end

#log(msg) ⇒ Object



136
137
138
139
140
141
142
143
# File 'lib/hmac_request.rb', line 136

def log(msg)
  logger = nil
  if defined?(Rails.logger)
    Rails.logger.error msg
  else
    $stdout.puts msg
  end
end

#log_auth_error(key) ⇒ Object



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

def log_auth_error(key)
  log "Logging Lockbox HMAC authorization error:"
  log "Path: #{self.path}"

  HTTP_HEADER_TO_ENV_MAP.values.each do |header|
    log "#{header}: #{@env[header]}"
  end

  log "HMAC Canonical String: #{ AuthHMAC::CanonicalString.new(self).inspect}"

  if self.hmac_id.nil?
    log("HMAC failed because request is not signed")
  elsif key
    log("HMAC failed - expected #{AuthHMAC.signature(self,key)} but was #{self.hmac_hash}")
  end
end

#pathObject



43
44
45
46
47
48
49
50
# File 'lib/hmac_request.rb', line 43

def path
  #use Referer if it's there, which it will be when this gets called while hitting the AuthenticationController
  if @env['Referer'].to_s =~ /^(?:http:\/\/)?[^\/]*(\/.*)$/
    return $1
  end
  #if we're in the middleware, it won't be there but we can use the request's path to the same effect
  return @request.path
end