api-auth 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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