api-auth 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --colour
2
+ --format nested
3
+ --backtrace
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "rspec", "~> 2.4.0"
5
+ gem "bundler", "~> 1.0.0"
6
+ gem "jeweler", "~> 1.5.2"
7
+ gem "amatch", "~> 0.2.5"
8
+ gem "curb", "~> 0.7.7"
9
+ gem "rest-client", "~> 1.6.0"
10
+ gem "actionpack", "~> 2.3.2"
11
+ gem "activeresource", "~> 2.3.2"
12
+ end
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Gemini SBS LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,92 @@
1
+ api-auth
2
+ ========
3
+
4
+ Logins and passwords are for humans. Communication between applications need to
5
+ be protected through different means.
6
+
7
+ api-auth is Ruby gem designed to be used both in your client and server
8
+ HTTP-based applications. It implements the same authentication methods (HMAC)
9
+ used by Amazon Web Services.
10
+
11
+ The gem will sign your requests on the client side and authenticate that
12
+ signature on the server side. If your server resources are implemented as a
13
+ Rails ActiveResource, it will integrate with that. It will even generate the
14
+ secret keys necessary for your clients to sign their requests.
15
+
16
+ Since it operates entirely using HTTP headers, the server component does not
17
+ have to be written in the same language as the clients. Any language with
18
+ OpenSSL bindings will suffice.
19
+
20
+ How it works
21
+ ------------
22
+
23
+ 1. A canonical string is first created using your HTTP headers containing the
24
+ content-type, content-MD5, request URI and the
25
+ timestamp. The canonical string string is computed as follows:
26
+
27
+ canonical_string = "<content-type>,<content-MD5>,<URI>,<timestamp>"
28
+
29
+ If content-type or content-MD5 are not present, then a blank string is used in
30
+ their place. If the timestamp isn't present, a valid HTTP date is automatically
31
+ added to the request.
32
+
33
+ 2. This string is then used to create the signature which is a Base64 encoded
34
+ SHA1 HMAC, using the client's private secret key.
35
+
36
+ 3. This signature is then added as the `Authorization` HTTP header in the form:
37
+
38
+ Authorization = APIAuth <client access id>:<signature from step 2>
39
+
40
+ 5. On the server side, the SHA1 HMAC is computed in the same way using the
41
+ request headers and the client's secret key, which is known to only
42
+ the client and the server but can be looked up on the server using the client's
43
+ access id that was attached in the header. The access id can be any integer or
44
+ string that uniquely identifies the client.
45
+
46
+
47
+ References
48
+ ----------
49
+
50
+ * [Hash functions](http://en.wikipedia.org/wiki/Cryptographic_hash_function)
51
+ * [SHA-1 Hash function](http://en.wikipedia.org/wiki/SHA-1)
52
+ * [HMAC algorithm](http://en.wikipedia.org/wiki/HMAC)
53
+ * [RFC 2104 (HMAC)](http://tools.ietf.org/html/rfc2104)
54
+
55
+ Usage
56
+ -----
57
+
58
+ ### Install ###
59
+
60
+ [sudo] gem install api-auth
61
+
62
+ ### Supported Request Objects ###
63
+
64
+ ApiAuth supports most request objects. With the support of
65
+ ActionController::Request, ApiAuth is fully compatible with Rails. Support for
66
+ other request objects can be added as a request driver.
67
+
68
+ Here is the current list of supported request objects:
69
+
70
+ * Net::HTTP
71
+ * ActionController::Request
72
+ * Curb (Curl::Easy)
73
+ * RestClient
74
+
75
+ ### ActiveResource ###
76
+
77
+ class MyResource < ActiveResource::Base
78
+ with_api_auth(<access_id>, <secret_key>)
79
+ end
80
+
81
+ ### Server ###
82
+
83
+
84
+ Authors
85
+ -------
86
+
87
+ * [Mauricio Gomes](http://github.com/mgomes)
88
+
89
+ Copyright
90
+ ---------
91
+
92
+ Copyright (c) 2011 Gemini SBS LLC. See LICENSE.txt for further details.
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ gem.name = "api-auth"
15
+ gem.homepage = "http://github.com/geminisbs/api-auth"
16
+ gem.license = "MIT"
17
+ gem.summary = %Q{Simple HMAC authentication for your APIs}
18
+ gem.description = %Q{Full HMAC auth implementation for use in your gems and Rails apps.}
19
+ gem.email = "mgomes@geminisbs.com"
20
+ gem.authors = ["Mauricio Gomes"]
21
+ end
22
+ Jeweler::RubygemsDotOrgTasks.new
23
+
24
+ require 'rspec/core'
25
+ require 'rspec/core/rake_task'
26
+ RSpec::Core::RakeTask.new(:spec) do |spec|
27
+ spec.pattern = FileList['spec/**/*_spec.rb']
28
+ end
29
+
30
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "api-auth #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
@@ -0,0 +1,14 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ require 'api-auth/errors'
5
+ require 'api-auth/helpers'
6
+
7
+ require 'api-auth/request_drivers/net_http'
8
+ require 'api-auth/request_drivers/curb'
9
+ require 'api-auth/request_drivers/rest_client'
10
+ require 'api-auth/request_drivers/action_controller'
11
+
12
+ require 'api-auth/headers'
13
+ require 'api-auth/base'
14
+ require 'api-auth/railtie'
@@ -0,0 +1,138 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ApiAuth" do
4
+
5
+ describe "generating secret keys" do
6
+
7
+ it "should generate secret keys" do
8
+ ApiAuth.generate_secret_key
9
+ end
10
+
11
+ it "should generate secret keys that are 89 characters" do
12
+ ApiAuth.generate_secret_key.size.should be(89)
13
+ end
14
+
15
+ it "should generate keys that have a Hamming Distance of at least 65" do
16
+ key1 = ApiAuth.generate_secret_key
17
+ key2 = ApiAuth.generate_secret_key
18
+ Amatch::Hamming.new(key1).match(key2).should be > 65
19
+ end
20
+
21
+ end
22
+
23
+ describe "signing requests" do
24
+
25
+ def hmac(secret_key, request)
26
+ canonical_string = ApiAuth::Headers.new(request).canonical_string
27
+ digest = OpenSSL::Digest::Digest.new('sha1')
28
+ ApiAuth.b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
29
+ end
30
+
31
+ before(:all) do
32
+ @access_id = "1044"
33
+ @secret_key = ApiAuth.generate_secret_key
34
+ end
35
+
36
+ describe "with Net::HTTP" do
37
+
38
+ before(:each) do
39
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
40
+ 'content-type' => 'text/plain',
41
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260',
42
+ 'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
43
+ @signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
44
+ end
45
+
46
+ it "should return a Net::HTTP object after signing it" do
47
+ ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Net::HTTP")
48
+ end
49
+
50
+ it "should sign the request" do
51
+ @signed_request['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
52
+ end
53
+
54
+ it "should authenticate a valid request" do
55
+ ApiAuth.authentic?(@signed_request, @secret_key).should be_true
56
+ end
57
+
58
+ it "should NOT authenticate a non-valid request" do
59
+ ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
60
+ end
61
+
62
+ it "should retrieve the access_id" do
63
+ ApiAuth.access_id(@signed_request).should == "1044"
64
+ end
65
+
66
+ end
67
+
68
+ describe "with RestClient" do
69
+
70
+ before(:each) do
71
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
72
+ 'Content-Type' => "text/plain",
73
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
74
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
75
+ :headers => headers,
76
+ :method => :put)
77
+ @signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
78
+ end
79
+
80
+ it "should return a RestClient object after signing it" do
81
+ ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("RestClient")
82
+ end
83
+
84
+ it "should sign the request" do
85
+ @signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
86
+ end
87
+
88
+ it "should authenticate a valid request" do
89
+ ApiAuth.authentic?(@signed_request, @secret_key).should be_true
90
+ end
91
+
92
+ it "should NOT authenticate a non-valid request" do
93
+ ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
94
+ end
95
+
96
+ it "should retrieve the access_id" do
97
+ ApiAuth.access_id(@signed_request).should == "1044"
98
+ end
99
+
100
+ end
101
+
102
+ describe "with Curb" do
103
+
104
+ before(:each) do
105
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
106
+ 'Content-Type' => "text/plain",
107
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
108
+ @request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
109
+ curl.headers = headers
110
+ end
111
+ @signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
112
+ end
113
+
114
+ it "should return a Curl::Easy object after signing it" do
115
+ ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("Curl::Easy")
116
+ end
117
+
118
+ it "should sign the request" do
119
+ @signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
120
+ end
121
+
122
+ it "should authenticate a valid request" do
123
+ ApiAuth.authentic?(@signed_request, @secret_key).should be_true
124
+ end
125
+
126
+ it "should NOT authenticate a non-valid request" do
127
+ ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
128
+ end
129
+
130
+ it "should retrieve the access_id" do
131
+ ApiAuth.access_id(@signed_request).should == "1044"
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ end
@@ -0,0 +1,102 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ApiAuth::Headers" do
4
+
5
+ CANONICAL_STRING = "text/plain,e59ff97941044f85df5297e1c302d260,/resource.xml?foo=bar&bar=foo,Mon, 23 Jan 1984 03:29:56 GMT"
6
+
7
+ describe "with Net::HTTP" do
8
+
9
+ before(:each) do
10
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
11
+ 'content-type' => 'text/plain',
12
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260',
13
+ 'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
14
+ @headers = ApiAuth::Headers.new(@request)
15
+ end
16
+
17
+ it "should generate the proper canonical string" do
18
+ @headers.canonical_string.should == CANONICAL_STRING
19
+ end
20
+
21
+ it "should set the authorization header" do
22
+ @headers.sign_header("alpha")
23
+ @headers.authorization_header.should == "alpha"
24
+ end
25
+
26
+ it "should set the DATE header if one is not already present" do
27
+ @request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
28
+ 'content-type' => 'text/plain',
29
+ 'content-md5' => 'e59ff97941044f85df5297e1c302d260')
30
+ ApiAuth.sign!(@request, "some access id", "some secret key")
31
+ @request['DATE'].should_not be_nil
32
+ end
33
+
34
+ end
35
+
36
+ describe "with RestClient" do
37
+
38
+ before(:each) do
39
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
40
+ 'Content-Type' => "text/plain",
41
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
42
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
43
+ :headers => headers,
44
+ :method => :put)
45
+ @headers = ApiAuth::Headers.new(@request)
46
+ end
47
+
48
+ it "should generate the proper canonical string" do
49
+ @headers.canonical_string.should == CANONICAL_STRING
50
+ end
51
+
52
+ it "should set the authorization header" do
53
+ @headers.sign_header("alpha")
54
+ @headers.authorization_header.should == "alpha"
55
+ end
56
+
57
+ it "should set the DATE header if one is not already present" do
58
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
59
+ 'Content-Type' => "text/plain" }
60
+ @request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
61
+ :headers => headers,
62
+ :method => :put)
63
+ ApiAuth.sign!(@request, "some access id", "some secret key")
64
+ @request.headers['DATE'].should_not be_nil
65
+ end
66
+
67
+ end
68
+
69
+ describe "with Curb" do
70
+
71
+ before(:each) do
72
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
73
+ 'Content-Type' => "text/plain",
74
+ 'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
75
+ @request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
76
+ curl.headers = headers
77
+ end
78
+ @headers = ApiAuth::Headers.new(@request)
79
+ end
80
+
81
+ it "should generate the proper canonical string" do
82
+ @headers.canonical_string.should == CANONICAL_STRING
83
+ end
84
+
85
+ it "should set the authorization header" do
86
+ @headers.sign_header("alpha")
87
+ @headers.authorization_header.should == "alpha"
88
+ end
89
+
90
+ it "should set the DATE header if one is not already present" do
91
+ headers = { 'Content-MD5' => "e59ff97941044f85df5297e1c302d260",
92
+ 'Content-Type' => "text/plain" }
93
+ @request = Curl::Easy.new("/resource.xml?foo=bar&bar=foo") do |curl|
94
+ curl.headers = headers
95
+ end
96
+ ApiAuth.sign!(@request, "some access id", "some secret key")
97
+ @request.headers['DATE'].should_not be_nil
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,14 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ApiAuth::Helpers" do
4
+
5
+ it "should strip the new line character on a Base64 encoding" do
6
+ ApiAuth.b64_encode("some string").should_not match(/\n/)
7
+ end
8
+
9
+ it "should properly upcase a hash's keys" do
10
+ hsh = { "JoE" => "rOOLz" }
11
+ ApiAuth.capitalize_keys(hsh)["JOE"].should == "rOOLz"
12
+ end
13
+
14
+ end
@@ -0,0 +1,105 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Rails integration" do
4
+
5
+ API_KEY_STORE = { "1044" => "l16imAXie1sRMcJODpOG7UwC1VyoqvO13jejkfpKWX4Z09W8DC9IrU23DvCwMry7pgSFW6c5S1GIfV0OY6F/vUA==" }
6
+
7
+ describe "Rails controller integration" do
8
+
9
+ class ApplicationController < ActionController::Base
10
+
11
+ private
12
+
13
+ def require_api_auth
14
+ if (access_id = get_api_access_id_from_request)
15
+ return true if api_authenticated?(API_KEY_STORE[access_id])
16
+ end
17
+
18
+ respond_to do |format|
19
+ format.xml { render :xml => "You are unauthorized to perform this action.", :status => 401 }
20
+ format.json { render :json => "You are unauthorized to perform this action.", :status => 401 }
21
+ format.html { render :text => "You are unauthorized to perform this action", :status => 401 }
22
+ end
23
+ end
24
+
25
+ end
26
+
27
+ class TestController < ApplicationController
28
+ before_filter :require_api_auth, :only => [:index]
29
+
30
+ def index
31
+ render :text => "OK"
32
+ end
33
+
34
+ def public
35
+ render :text => "OK"
36
+ end
37
+
38
+ def rescue_action(e); raise(e); end
39
+ end
40
+ ActionController::Routing::Routes.draw {|map| map.resources :test }
41
+
42
+ it "should permit a request with properly signed headers" do
43
+ request = ActionController::TestRequest.new
44
+ request.env['DATE'] = "Mon, 23 Jan 1984 03:29:56 GMT"
45
+ request.action = 'index'
46
+ request.path = "/index"
47
+ ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
48
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
49
+ end
50
+
51
+ it "should insert a DATE header in the request when one hasn't been specified" do
52
+ request = ActionController::TestRequest.new
53
+ request.action = 'index'
54
+ request.path = "/index"
55
+ ApiAuth.sign!(request, "1044", API_KEY_STORE["1044"])
56
+ request.headers['DATE'].should_not be_nil
57
+ end
58
+
59
+ it "should forbid an unsigned request to a protected controller action" do
60
+ request = ActionController::TestRequest.new
61
+ request.action = 'index'
62
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
63
+ end
64
+
65
+ it "should forbid a request with a bogus signature" do
66
+ request = ActionController::TestRequest.new
67
+ request.action = 'index'
68
+ request.env['Authorization'] = "APIAuth bogus:bogus"
69
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "401"
70
+ end
71
+
72
+ it "should allow non-protected controller actions to function as before" do
73
+ request = ActionController::TestRequest.new
74
+ request.action = 'public'
75
+ request.path('/public')
76
+ TestController.new.process(request, ActionController::TestResponse.new).code.should == "200"
77
+ end
78
+
79
+ end
80
+
81
+ describe "Rails ActiveResource integration" do
82
+
83
+ class TestResource < ActiveResource::Base
84
+ with_api_auth "1044", API_KEY_STORE["1044"]
85
+ self.site = "http://localhost/"
86
+ end
87
+
88
+ it "should send signed requests automagically" do
89
+ timestamp = Time.parse("Mon, 23 Jan 1984 03:29:56 GMT")
90
+ Time.should_receive(:now).at_least(1).times.and_return(timestamp)
91
+ ActiveResource::HttpMock.respond_to do |mock|
92
+ mock.get "/test_resources/1.xml",
93
+ {
94
+ 'Authorization' => 'APIAuth 1044:IbTx7VzSOGU55HNbV4y2jZDnVis=',
95
+ 'Accept' => 'application/xml',
96
+ 'DATE' => "Mon, 23 Jan 1984 03:29:56 GMT"
97
+ },
98
+ { :id => "1" }.to_xml(:root => 'test_resource')
99
+ end
100
+ TestResource.find(1)
101
+ end
102
+
103
+ end
104
+
105
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'api-auth'
5
+ require 'amatch'
6
+ require 'rest_client'
7
+ require 'curb'
8
+
9
+ require 'active_support'
10
+ require 'active_support/test_case'
11
+ require 'action_controller'
12
+ require 'action_controller/test_process'
13
+ require 'active_resource'
14
+ require 'active_resource/http_mock'
15
+
16
+ # Requires supporting files with custom matchers and macros, etc,
17
+ # in ./support/ and its subdirectories.
18
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
19
+
20
+ RSpec.configure do |config|
21
+
22
+ end
metadata ADDED
@@ -0,0 +1,210 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: api-auth
3
+ version: !ruby/object:Gem::Version
4
+ hash: 59
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 0
10
+ version: 0.9.0
11
+ platform: ruby
12
+ authors:
13
+ - Mauricio Gomes
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-30 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ type: :development
23
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 31
29
+ segments:
30
+ - 2
31
+ - 4
32
+ - 0
33
+ version: 2.4.0
34
+ requirement: *id001
35
+ prerelease: false
36
+ name: rspec
37
+ - !ruby/object:Gem::Dependency
38
+ type: :development
39
+ version_requirements: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ hash: 23
45
+ segments:
46
+ - 1
47
+ - 0
48
+ - 0
49
+ version: 1.0.0
50
+ requirement: *id002
51
+ prerelease: false
52
+ name: bundler
53
+ - !ruby/object:Gem::Dependency
54
+ type: :development
55
+ version_requirements: &id003 !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ hash: 7
61
+ segments:
62
+ - 1
63
+ - 5
64
+ - 2
65
+ version: 1.5.2
66
+ requirement: *id003
67
+ prerelease: false
68
+ name: jeweler
69
+ - !ruby/object:Gem::Dependency
70
+ type: :development
71
+ version_requirements: &id004 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ hash: 29
77
+ segments:
78
+ - 0
79
+ - 2
80
+ - 5
81
+ version: 0.2.5
82
+ requirement: *id004
83
+ prerelease: false
84
+ name: amatch
85
+ - !ruby/object:Gem::Dependency
86
+ type: :development
87
+ version_requirements: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ~>
91
+ - !ruby/object:Gem::Version
92
+ hash: 13
93
+ segments:
94
+ - 0
95
+ - 7
96
+ - 7
97
+ version: 0.7.7
98
+ requirement: *id005
99
+ prerelease: false
100
+ name: curb
101
+ - !ruby/object:Gem::Dependency
102
+ type: :development
103
+ version_requirements: &id006 !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ~>
107
+ - !ruby/object:Gem::Version
108
+ hash: 15
109
+ segments:
110
+ - 1
111
+ - 6
112
+ - 0
113
+ version: 1.6.0
114
+ requirement: *id006
115
+ prerelease: false
116
+ name: rest-client
117
+ - !ruby/object:Gem::Dependency
118
+ type: :development
119
+ version_requirements: &id007 !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ~>
123
+ - !ruby/object:Gem::Version
124
+ hash: 7
125
+ segments:
126
+ - 2
127
+ - 3
128
+ - 2
129
+ version: 2.3.2
130
+ requirement: *id007
131
+ prerelease: false
132
+ name: actionpack
133
+ - !ruby/object:Gem::Dependency
134
+ type: :development
135
+ version_requirements: &id008 !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ~>
139
+ - !ruby/object:Gem::Version
140
+ hash: 7
141
+ segments:
142
+ - 2
143
+ - 3
144
+ - 2
145
+ version: 2.3.2
146
+ requirement: *id008
147
+ prerelease: false
148
+ name: activeresource
149
+ description: Full HMAC auth implementation for use in your gems and Rails apps.
150
+ email: mgomes@geminisbs.com
151
+ executables: []
152
+
153
+ extensions: []
154
+
155
+ extra_rdoc_files:
156
+ - LICENSE.txt
157
+ - README.md
158
+ files:
159
+ - .document
160
+ - .rspec
161
+ - Gemfile
162
+ - LICENSE.txt
163
+ - Rakefile
164
+ - lib/api-auth.rb
165
+ - spec/api-auth_spec.rb
166
+ - spec/spec_helper.rb
167
+ - README.md
168
+ - spec/headers_spec.rb
169
+ - spec/helpers_spec.rb
170
+ - spec/railtie_spec.rb
171
+ has_rdoc: true
172
+ homepage: http://github.com/geminisbs/api-auth
173
+ licenses:
174
+ - MIT
175
+ post_install_message:
176
+ rdoc_options: []
177
+
178
+ require_paths:
179
+ - lib
180
+ required_ruby_version: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ">="
184
+ - !ruby/object:Gem::Version
185
+ hash: 3
186
+ segments:
187
+ - 0
188
+ version: "0"
189
+ required_rubygems_version: !ruby/object:Gem::Requirement
190
+ none: false
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ hash: 3
195
+ segments:
196
+ - 0
197
+ version: "0"
198
+ requirements: []
199
+
200
+ rubyforge_project:
201
+ rubygems_version: 1.4.1
202
+ signing_key:
203
+ specification_version: 3
204
+ summary: Simple HMAC authentication for your APIs
205
+ test_files:
206
+ - spec/api-auth_spec.rb
207
+ - spec/headers_spec.rb
208
+ - spec/helpers_spec.rb
209
+ - spec/railtie_spec.rb
210
+ - spec/spec_helper.rb