lockbox_middleware 1.5.1 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,145 @@
1
+ require 'rubygems'
2
+ gem 'dnclabs-auth-hmac'
3
+ require 'auth-hmac'
4
+ require 'digest/md5'
5
+
6
+ class HmacRequest
7
+ attr_accessor :request, :env, :body, :hmac_id, :hmac_hash
8
+ undef :method
9
+
10
+ @@valid_date_window = 600 # seconds
11
+
12
+ HTTP_HEADER_TO_ENV_MAP = { 'Content-Type' => 'CONTENT_TYPE',
13
+ 'Content-MD5' => 'CONTENT_MD5',
14
+ 'Date' => 'HTTP_DATE',
15
+ 'Method' => 'REQUEST_METHOD',
16
+ 'Authorization' => 'HTTP_AUTHORIZATION' }
17
+
18
+ def self.new_from_rack_env(env)
19
+ r = self.new(env)
20
+ return r
21
+ end
22
+
23
+ def self.new_from_rails_request(request)
24
+ r = self.new(request.headers)
25
+ #pull stuff out of X-Referer, which is where the middleware sticks it
26
+ HTTP_HEADER_TO_ENV_MAP.each_pair do |h,e|
27
+ r.env[e] = r.env["X-Referer-#{h}"] unless r.env["X-Referer-#{h}"].blank?
28
+ end
29
+
30
+ return r
31
+ end
32
+
33
+ def initialize(env)
34
+ @request = Rack::Request.new(env)
35
+ @env = @request.env
36
+ @body = @request.body if has_body?(@env['REQUEST_METHOD'])
37
+ end
38
+
39
+ def [](key)
40
+ @request[key]
41
+ end
42
+
43
+ def path
44
+ #use Referer if it's there, which it will be when this gets called while hitting the AuthenticationController
45
+ if @env['Referer'].to_s =~ /^(?:http:\/\/)?[^\/]*(\/.*)$/
46
+ return $1
47
+ end
48
+ #if we're in the middleware, it won't be there but we can use the request's path to the same effect
49
+ return @request.path
50
+ end
51
+
52
+ def has_body?(method)
53
+ ["PUT","POST"].include?(method)
54
+ end
55
+
56
+ def hmac_id
57
+ get_hmac_vals if @hmac_id.nil?
58
+ @hmac_id
59
+ end
60
+
61
+ def hmac_hash
62
+ get_hmac_vals if @hmac_hash.nil?
63
+ @hmac_hash
64
+ end
65
+
66
+ def get_hmac_vals
67
+ @env['HTTP_AUTHORIZATION'].to_s =~ /^AuthHMAC ([^:]+):(.*)$/
68
+ @hmac_id = $1
69
+ @hmac_hash = $2
70
+ end
71
+
72
+
73
+ def hmac_auth(credential_store)
74
+ authhmac = AuthHMAC.new(credential_store)
75
+ if authhmac.authenticated?(self) && (@env['HTTP_DATE'].blank? || self.date_is_recent? )
76
+ credential_store[self.hmac_id]
77
+ else
78
+ log_auth_error(credential_store[self.hmac_id])
79
+ return false
80
+ end
81
+ end
82
+
83
+ def date_is_recent?()
84
+ req_date = nil
85
+
86
+ begin
87
+ req_date = Time.httpdate(@env['HTTP_DATE'])
88
+ rescue Exception => ex
89
+ if ex.message =~ /not RFC 2616 compliant/
90
+ # try rfc2822
91
+ req_date = Time.rfc2822(@env['HTTP_DATE'])
92
+ else
93
+ raise ex
94
+ end
95
+ end
96
+
97
+ if Time.now.to_i - req_date.to_i >= @@valid_date_window
98
+ log "Request date #{req_date} is more than #{@@valid_date_window} seconds old"
99
+ return false
100
+ else
101
+ return true
102
+ end
103
+ end
104
+
105
+ #these are the X-Referer-Headers that get passed along to lockbox from the middleware for authentication
106
+ def get_xreferer_auth_headers()
107
+ headers = {}
108
+ headers['Referer'] = "#{@env['rack.url_scheme']}://#{@env['SERVER_NAME']}#{@env['PATH_INFO']}"
109
+ headers['Referer'] << "?#{@env['QUERY_STRING']}" unless @env['QUERY_STRING'].blank?
110
+ HTTP_HEADER_TO_ENV_MAP.each_pair do |h,e|
111
+ headers["X-Referer-#{h}"] = @env[e] unless @env[e].blank?
112
+ end
113
+ headers['X-Referer-Content-MD5'] = Digest::MD5.hexdigest(@request.body.read) if @env['CONTENT_TYPE']
114
+ headers["X-Referer-Date"] = @env['HTTP_X_AUTHHMAC_REQUEST_DATE'] unless @env['HTTP_X_AUTHHMAC_REQUEST_DATE'].blank?
115
+ headers
116
+ end
117
+
118
+ def log_auth_error(key)
119
+ log "Logging Lockbox HMAC authorization error:"
120
+ log "Path: #{self.path}"
121
+
122
+ HTTP_HEADER_TO_ENV_MAP.values.each do |header|
123
+ log "#{header}: #{@env[header]}"
124
+ end
125
+
126
+ log "HMAC Canonical String: #{ AuthHMAC::CanonicalString.new(self).inspect}"
127
+
128
+ if self.hmac_id.nil?
129
+ log("HMAC failed because request is not signed")
130
+ elsif key
131
+ log("HMAC failed - expected #{AuthHMAC.signature(self,key)} but was #{self.hmac_hash}")
132
+ end
133
+ end
134
+
135
+
136
+ def log(msg)
137
+ logger = nil
138
+ if defined?(Rails.logger)
139
+ Rails.logger.error msg
140
+ else
141
+ $stdout.puts msg
142
+ end
143
+ end
144
+
145
+ end
@@ -3,22 +3,8 @@ require 'forwardable'
3
3
  module LockBoxCache
4
4
  class Cache
5
5
  extend Forwardable
6
- def_delegators :@cache, :write, :read, :delete
6
+ def_delegators :@cache, :write, :read, :delete, :clear
7
7
 
8
- class RailsCache
9
- def write(key, value)
10
- Rails.cache.write(key, value)
11
- end
12
-
13
- def read(key)
14
- Rails.cache.read(key)
15
- end
16
-
17
- def delete(key)
18
- Rails.cache.delete(key)
19
- end
20
- end
21
-
22
8
  class HashCache
23
9
  def initialize
24
10
  @store = Hash.new
@@ -35,14 +21,18 @@ module LockBoxCache
35
21
  def delete(key)
36
22
  @store.delete(key)
37
23
  end
24
+
25
+ def clear
26
+ @store = {}
27
+ end
38
28
  end
39
29
 
40
30
  def initialize(use_rails_cache=true)
41
31
  if use_rails_cache && defined?(Rails)
42
- @cache = RailsCache.new
32
+ @cache = Rails.cache
43
33
  else
44
34
  @cache = HashCache.new
45
35
  end
46
36
  end
47
37
  end
48
- end
38
+ end
@@ -1,36 +1,35 @@
1
1
  require 'rubygems'
2
- gem 'dnclabs-httparty'
3
- require 'httparty'
2
+ require 'httpotato'
4
3
  require 'lockbox_cache'
5
- require 'digest/md5'
4
+ require 'hmac_request'
6
5
 
7
6
  class LockBox
8
- include HTTParty
7
+ include HTTPotato
9
8
  include LockBoxCache
9
+
10
+ attr_accessor :cache
11
+
10
12
  @@config = nil
13
+ @@protected_paths = nil
11
14
 
12
15
  def self.config
13
16
  return @@config if @@config
14
- if defined?(Rails)
15
- root_dir = Rails.root
17
+ #use rails config if it's there
18
+ if defined?(Rails) && Rails.root
19
+ config_file = Rails.root.join('config','lockbox.yml')
20
+ @@config = YAML.load_file(config_file)[Rails.env]
16
21
  else
17
- root_dir = '.'
18
- end
19
- yaml_config = YAML.load_file(File.join(root_dir,'config','lockbox.yml'))
20
- return_config = {}
21
- environment = Rails.env if defined? Rails
22
- environment ||= ENV['RACK_ENV']
23
- environment ||= 'test'
24
- if !environment.nil?
25
- if !yaml_config['all'].nil?
22
+ env = ENV['RACK_ENV'] || "test"
23
+ config_file = File.join(Dir.pwd, 'config','lockbox.yml')
24
+ all_configs = YAML.load_file(config_file)
25
+ if !all_configs['all'].nil?
26
26
  $stderr.puts "The 'all' environment is deprecated in lockbox.yml; use built-in yaml convention instead."
27
- return_config = yaml_config['all']
28
- return_config.merge!(yaml_config[environment])
27
+ @@config = all_configs['all'].merge!(all_configs[env])
29
28
  else
30
- return_config = yaml_config[environment]
29
+ @@config = all_configs[env]
31
30
  end
32
31
  end
33
- @@config = return_config
32
+ return @@config
34
33
  end
35
34
 
36
35
  base_uri config['base_uri']
@@ -43,64 +42,65 @@ class LockBox
43
42
  def call(env)
44
43
  dup.call!(env)
45
44
  end
46
-
45
+
46
+ def cache_string_for_key(api_key)
47
+ "lockbox_key_#{api_key}"
48
+ end
49
+
50
+ def cache_string_for_hmac(hmac_id)
51
+ "lockbox_hmac_#{hmac_id.gsub(/[^a-z0-9]/i,'_')}"
52
+ end
53
+
47
54
  def protected_paths
48
- self.class.config['protect_paths'].map do |path|
49
- Regexp.new(path)
50
- end
55
+ @@protect_paths ||= self.class.config['protect_paths'].map{ |path| Regexp.new(path) }
51
56
  end
52
57
 
53
58
  def call!(env)
54
- #attempt to authenticate any requests to /api
55
- request = Rack::Request.new(env)
56
- path_protected = false
57
- protected_paths.each do |path|
58
- if env['PATH_INFO'] =~ path
59
- path_protected = true
60
- authorized = false
61
- key = request['key']
62
- if key.blank?
63
- key = 'hmac'
59
+ protected_path = protected_paths.detect{|path| env['PATH_INFO'] =~ path}
60
+ #if the requested path is protected, it needs to be authenticated
61
+ if protected_path
62
+ request = HmacRequest.new_from_rack_env(env)
63
+ if !request['key'].nil?
64
+ auth = auth_via_key(request['key'], request)
65
+ else
66
+ auth = auth_via_hmac(request)
64
67
  end
65
-
66
- auth = auth_response(key,env)
67
- authorized = auth[:authorized]
68
- auth_headers = auth[:headers]
69
-
70
- if authorized
68
+
69
+ if auth[:authorized]
71
70
  app_response = @app.call(env)
72
- app_headers = app_response[1]
73
- response_headers = app_headers.merge(auth_headers)
74
- return [app_response[0], response_headers, app_response[2]]
71
+ return [app_response[0], app_response[1].merge(auth[:headers]), app_response[2]]
75
72
  else
76
73
  message = "Access Denied"
77
74
  return [401, {'Content-Type' => 'text/plain', 'Content-Length' => "#{message.length}"}, [message]]
78
75
  end
79
- end
80
- end
81
- unless path_protected
76
+ else
82
77
  #pass everything else straight through to app
83
78
  return @app.call(env)
84
79
  end
85
80
  end
86
81
 
87
- def auth_response(api_key, env={})
88
- if api_key != 'hmac'
89
- cached_auth = auth_cache(api_key)
90
- if !cached_auth.nil?
91
- # currently we don't cache forward headers
92
- return {:authorized => cached_auth, :headers => {}}
93
- end
94
- end
95
- auth_response = self.class.get("/authentication/#{api_key}", {:headers => auth_headers(env), :request => {:application_name => LockBox.config['application_name']}})
82
+ def auth_via_key(api_key, request)
83
+ cached_auth = check_key_cache(api_key)
84
+ # currently we don't cache forward headers
85
+ return {:authorized => cached_auth, :headers => {}} if cached_auth
86
+ auth_response = self.class.get("/authentication/#{api_key}", {:headers => request.get_xreferer_auth_headers, :request => {:application_name => LockBox.config['application_name']}})
87
+ authorized = (auth_response.code == 200)
88
+ cache_key_response_if_allowed(api_key, auth_response) if authorized
89
+ {:authorized => authorized, :headers => response_headers(auth_response)}
90
+ end
91
+
92
+ def auth_via_hmac(hmac_request)
93
+ cached_auth = check_hmac_cache(hmac_request)
94
+ return {:authorized => cached_auth, :headers => {}} if cached_auth
95
+ auth_response = self.class.get("/authentication/hmac", {:headers => hmac_request.get_xreferer_auth_headers, :request => {:application_name => LockBox.config['application_name']}})
96
96
  authorized = (auth_response.code == 200)
97
- cache_response_if_allowed(api_key, auth_response) if authorized
97
+ cache_hmac_response_if_allowed(hmac_request, auth_response) if authorized
98
98
  {:authorized => authorized, :headers => response_headers(auth_response)}
99
99
  end
100
-
100
+
101
101
  private
102
-
103
- def cache_response_if_allowed(api_key, auth_response)
102
+
103
+ def cache_key_response_if_allowed(api_key, auth_response)
104
104
  cache_control = auth_response.headers['Cache-Control'].split(/,\s*/)
105
105
  cache_max_age = 0
106
106
  cache_public = false
@@ -113,52 +113,63 @@ class LockBox
113
113
  end
114
114
  caching_allowed = (cache_max_age > 0 && cache_public)
115
115
  expiration = Time.at(Time.now.to_i + cache_max_age)
116
- cache_auth(api_key,expiration) if caching_allowed
116
+ @cache.write(cache_string_for_key(api_key), expiration.to_i) if caching_allowed
117
117
  end
118
118
 
119
- def response_headers(auth_response)
120
- headers = {}
121
- auth_response.headers.each_pair do |h,v|
122
- if h =~ /^X-RateLimit-/
123
- headers[h] = v
124
- elsif h =~ /^X-LockBox-/
125
- headers[h] = v
119
+ def cache_hmac_response_if_allowed(hmac_request, auth_response)
120
+ cache_control = auth_response.headers['Cache-Control'].split(/,\s*/)
121
+ cache_max_age = 0
122
+ cache_public = false
123
+ cache_control.each do |c|
124
+ if c =~ /^max-age=\s*(\d+)$/
125
+ cache_max_age = $1.to_i
126
+ elsif c == 'public'
127
+ cache_public = true
126
128
  end
127
129
  end
128
- headers
130
+ caching_allowed = (cache_max_age > 0 && cache_public)
131
+ expiration = Time.at(Time.now.to_i + cache_max_age)
132
+ if caching_allowed
133
+ api_key = auth_response.headers['X-LockBox-API-Key']
134
+ @cache.write(cache_string_for_hmac(hmac_request.hmac_id), [api_key, expiration.to_i])
135
+ end
129
136
  end
130
137
 
131
- def auth_headers(env)
138
+ def response_headers(auth_response)
132
139
  headers = {}
133
- headers['Referer'] = "#{env['rack.url_scheme']}://#{env['SERVER_NAME']}#{env['PATH_INFO']}"
134
- headers['Referer'] << "?#{env['QUERY_STRING']}" unless env['QUERY_STRING'].blank?
135
- headers['X-Referer-Content-MD5'] = Digest::MD5.hexdigest(Rack::Request.new(env).body.read) if env['CONTENT_TYPE']
136
- {'Content-Type' => 'CONTENT_TYPE', 'Date' => 'HTTP_DATE', 'Method' => 'REQUEST_METHOD',
137
- 'Authorization' => 'HTTP_AUTHORIZATION'}.each_pair do |h,e|
138
- headers["X-Referer-#{h}"] = env[e] unless env[e].blank?
140
+ auth_response.headers.each_pair do |h,v|
141
+ headers[h] = v if h =~ /^X-RateLimit-|^X-LockBox-/
139
142
  end
140
- headers["X-Referer-Date"] = env['HTTP_X_AUTHHMAC_REQUEST_DATE'] unless env['HTTP_X_AUTHHMAC_REQUEST_DATE'].blank?
141
143
  headers
142
144
  end
143
-
144
- def cache_key(api_key)
145
- "lockbox_#{api_key}"
146
- end
147
145
 
148
- def auth_cache(api_key)
149
- expiration = @cache.read(cache_key(api_key))
146
+ def check_key_cache(api_key)
147
+ expiration = @cache.read(cache_string_for_key(api_key))
150
148
  return nil if expiration.nil?
151
149
  expiration = Time.at(expiration)
152
150
  if expiration <= Time.now
153
- @cache.delete(cache_key(api_key))
151
+ @cache.delete(cache_string_for_key(api_key))
154
152
  nil
155
- elsif expiration > Time.now
153
+ else
156
154
  true
157
155
  end
158
156
  end
159
157
 
160
- def cache_auth(api_key,expiration)
161
- @cache.write(cache_key(api_key),expiration.to_i)
158
+ def check_hmac_cache(hmac_request)
159
+ hmac_id, hmac_hash = hmac_request.hmac_id, hmac_request.hmac_hash
160
+ return nil if hmac_id.nil? || hmac_hash.nil?
161
+ cached_val = @cache.read(cache_string_for_hmac(hmac_id))
162
+ return nil if cached_val.nil?
163
+ key, expiration = cached_val
164
+ expiration = Time.at(expiration)
165
+ if expiration <= Time.now
166
+ @cache.delete(cache_string_for_hmac(hmac_id))
167
+ nil
168
+ else
169
+ #as long as the request is signed correctly, no need to contact the lockbox server to verify
170
+ #just see if the request is signed properly and let it through if it is
171
+ return true if hmac_request.hmac_auth({hmac_id => key}) == key
172
+ return nil
173
+ end
162
174
  end
163
-
164
175
  end
@@ -99,7 +99,7 @@ describe 'LockBox' do
99
99
  end
100
100
  end
101
101
 
102
- context "hitting API actions" do
102
+ context "hitting API actions with key-based authentication" do
103
103
  before :each do
104
104
  @max_age = 3600
105
105
  successful_response = mock("MockResponse")
@@ -214,9 +214,7 @@ describe 'LockBox' do
214
214
  @path = "/api/some_controller/some_action"
215
215
 
216
216
  hmac_request = Net::HTTP::Get.new(@path, {'Date' => Time.now.httpdate})
217
- store = mock("MockStore")
218
- store.stubs(:[]).with('key-id').returns("123456")
219
- authhmac = AuthHMAC.new(store)
217
+ authhmac = AuthHMAC.new({"key-id" => "123456"})
220
218
  authhmac.sign!(hmac_request, 'key-id')
221
219
  @hmac_headers = hmac_request.to_hash
222
220
  end
@@ -228,7 +226,7 @@ describe 'LockBox' do
228
226
  get @path
229
227
  last_response.status.should == 200
230
228
  end
231
-
229
+
232
230
  it "should return 200 for a valid HMAC request from a .NET client" do
233
231
  # first test w/ a Date header too, then test w/o a separate Date header
234
232
  @hmac_headers['X-AuthHMAC-Request-Date'] = @hmac_headers['Date']
@@ -238,7 +236,7 @@ describe 'LockBox' do
238
236
  get @path
239
237
  last_response.status.should == 200
240
238
  end
241
-
239
+
242
240
  it "should return 200 for a valid HMAC request from a .NET client with no Date header" do
243
241
  @hmac_headers['X-AuthHMAC-Request-Date'] = @hmac_headers.delete('Date')
244
242
  @hmac_headers.each_pair do |key,value|
@@ -256,8 +254,9 @@ describe 'LockBox' do
256
254
  get @path
257
255
  last_response.status.should == 401
258
256
  end
257
+
259
258
  end
260
-
259
+
261
260
  context "hitting API actions via POST requests with HMAC auth" do
262
261
  before :each do
263
262
  @content = "" # TODO: Rack::Test sucks at some stuff, like setting the request body when making a POST
@@ -280,9 +279,7 @@ describe 'LockBox' do
280
279
 
281
280
  hmac_request = Net::HTTP::Post.new(@path, {'Date' => Time.now.httpdate})
282
281
  hmac_request.body = @content
283
- store = mock("MockStore")
284
- store.stubs(:[]).with('key-id').returns("123456")
285
- authhmac = AuthHMAC.new(store)
282
+ authhmac = AuthHMAC.new({"key-id" => "123456"})
286
283
  authhmac.sign!(hmac_request, 'key-id')
287
284
  @hmac_headers = hmac_request.to_hash
288
285
  end
@@ -303,8 +300,9 @@ describe 'LockBox' do
303
300
  post @path, @content
304
301
  last_response.status.should == 401
305
302
  end
303
+
306
304
  end
307
-
305
+
308
306
  context "hitting actions without API" do
309
307
 
310
308
  it "should not try to authenticate a request that doesn't start with /api" do
@@ -314,5 +312,67 @@ describe 'LockBox' do
314
312
  end
315
313
 
316
314
  end
315
+
316
+ context "hitting API actions with HMAC auth caching" do
317
+ before :each do
318
+ Time.stubs(:now).returns(Time.parse("2010-05-10 16:30:00 EDT"))
319
+ @response_with_caching = mock("MockResponse")
320
+ @response_with_caching.stubs(:code).returns(200)
321
+ @response_with_caching.stubs(:headers).returns({'Cache-Control' => "public,max-age=3600,must-revalidate", "X-LockBox-API-Key" => "123456"})
322
+
323
+ @response_without_caching = mock("MockResponse")
324
+ @response_without_caching.stubs(:code).returns(200)
325
+ @response_without_caching.stubs(:headers).returns({'Cache-Control' => 'public, no-cache', "X-LockBox-API-Key" => "123456"})
326
+
327
+ @bad_response = mock("MockResponse")
328
+ @bad_response.stubs(:code).returns(401)
329
+ @bad_response.stubs(:headers).returns({'Cache-Control' => 'public, no-cache'})
330
+
331
+ @path = "/api/some_controller/some_action"
332
+
333
+ hmac_request = Net::HTTP::Get.new(@path, {'Date' => Time.now.httpdate, 'Referer' =>'http://example.org/api/some_controller/some_action' })
334
+ authhmac = AuthHMAC.new({'key-id' => '123456'})
335
+ authhmac.sign!(hmac_request, 'key-id')
336
+ @hmac_headers = hmac_request.to_hash
337
+ @hmac_headers.each_pair do |key,value|
338
+ header key, value
339
+ end
340
+
341
+ end
342
+
343
+ after :each do
344
+ app.cache.clear
345
+ end
346
+
347
+
348
+ it "should cache lockbox responses for max-age when Cache-Control allows it" do
349
+ LockBox.stubs(:get).returns(@response_with_caching)
350
+ get @path
351
+ last_response.status.should == 200
352
+ LockBox.stubs(:get).returns(@bad_response)
353
+ get @path
354
+ last_response.status.should == 200
355
+ end
356
+
357
+ it "should expire cached lockbox responses when max-age seconds have passed" do
358
+ LockBox.stubs(:get).returns(@response_with_caching)
359
+ get @path
360
+ last_response.status.should == 200
361
+ expired_time = Time.at(Time.now.to_i + 3600)
362
+ Time.stubs(:now).returns(expired_time)
363
+ LockBox.stubs(:get).returns(@bad_response)
364
+ get @path
365
+ last_response.status.should == 401
366
+ end
367
+
368
+ it "should not cache lockbox responses when Cache-Control does not allow it" do
369
+ LockBox.stubs(:get).returns(@response_without_caching)
370
+ get @path
371
+ last_response.status.should == 200
372
+ LockBox.stubs(:get).returns(@bad_response)
373
+ get @path
374
+ last_response.status.should == 401
375
+ end
376
+ end
317
377
 
318
378
  end
@@ -10,6 +10,6 @@ module Mocha
10
10
  end
11
11
  def teardown_mocks_for_rspec
12
12
  mocha_teardown
13
- end
13
+ end
14
14
  end
15
15
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lockbox_middleware
3
3
  version: !ruby/object:Gem::Version
4
- hash: 1
5
- prerelease: false
4
+ hash: 11
5
+ prerelease:
6
6
  segments:
7
7
  - 1
8
- - 5
9
- - 1
10
- version: 1.5.1
8
+ - 6
9
+ - 2
10
+ version: 1.6.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Gill
@@ -19,7 +19,7 @@ autorequire:
19
19
  bindir: bin
20
20
  cert_chain: []
21
21
 
22
- date: 2010-12-28 00:00:00 -06:00
22
+ date: 2011-03-02 00:00:00 -05:00
23
23
  default_executable:
24
24
  dependencies:
25
25
  - !ruby/object:Gem::Dependency
@@ -37,7 +37,7 @@ dependencies:
37
37
  type: :runtime
38
38
  version_requirements: *id001
39
39
  - !ruby/object:Gem::Dependency
40
- name: dnclabs-httparty
40
+ name: httpotato
41
41
  prerelease: false
42
42
  requirement: &id002 !ruby/object:Gem::Requirement
43
43
  none: false
@@ -50,6 +50,20 @@ dependencies:
50
50
  version: "0"
51
51
  type: :runtime
52
52
  version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: dnclabs-auth-hmac
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ type: :runtime
66
+ version_requirements: *id003
53
67
  description: Rack middleware for the LockBox centralized API authorization service. Brought to you by the DNC Innovation Lab.
54
68
  email: innovationlab@dnc.org
55
69
  executables: []
@@ -62,6 +76,7 @@ extra_rdoc_files:
62
76
  files:
63
77
  - lib/lockbox_cache.rb
64
78
  - lib/lockbox_middleware.rb
79
+ - lib/hmac_request.rb
65
80
  - LICENSE
66
81
  - README.rdoc
67
82
  - spec/lib/lockbox_cache_spec.rb
@@ -100,7 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
100
115
  requirements: []
101
116
 
102
117
  rubyforge_project:
103
- rubygems_version: 1.3.7
118
+ rubygems_version: 1.6.0
104
119
  signing_key:
105
120
  specification_version: 3
106
121
  summary: Rack middleware for the LockBox centralized API authorization service.