king_hmac 1.0.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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ nbproject/*
2
+ coverage/*
3
+ rdoc/*
4
+ pkg/*
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Georg Leciejewski
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.
data/README.rdoc ADDED
@@ -0,0 +1,145 @@
1
+ = king-hmac
2
+
3
+ This gem started with a copy & disection of auth-hmac gem v1.1.1
4
+
5
+ king-hmac is a Ruby implementation of HMAC[http://en.wikipedia.org/wiki/HMAC]
6
+ based authentication of HTTP requests. HMAC authentication involves a client and
7
+ server having a shared secret key. When sending the request the client, signs
8
+ the request using the secret key. This involves building a canonical
9
+ representation of the request and then generating a HMAC of the request using
10
+ the secret. The generated HMAC is then sent as part of the request.
11
+
12
+ When the server receives the request it builds the same canonical representation
13
+ and generates a HMAC using it's copy of the secret key, if the HMAC produced by
14
+ the server matches the HMAC sent by the client, the server can be assured that
15
+ the client also possesses the shared secret key.
16
+
17
+ HMAC based authentication also provides message integrity checking because the
18
+ HMAC is based on a combination of the shared secret and the content of the
19
+ request. So if any part of the request that is used to build the canonical
20
+ representation is modified by a malicious party or in transit the authentication
21
+ will then fail.
22
+
23
+ KingHmac::Auth is loosely based on the Amazon Web Services authentication scheme
24
+ but without the Amazon specific components, i.e. it is HMAC for the rest of us.
25
+
26
+
27
+ == INSTALL:
28
+ Gem hosted on gemcutter.org
29
+ sudo gem install king-hmac
30
+
31
+ == Source Code
32
+ See http://github.com/salesking/king-hmac
33
+ The source repository:
34
+ git clone git://github.com/salesking/king-hmac.git
35
+
36
+ == When to use it?
37
+
38
+ HMAC Authentication is best used as authentication for communication between
39
+ applications such as web services. It provides better security than HTTP Basic
40
+ authentication without the need to set up SSL. Of course if you need to protect
41
+ the confidentiality of the data then you need SSL, but if you just want to
42
+ authenticate requests without sending credentials in the clear KingHmac::Auth
43
+ is a good choice.
44
+
45
+ == Usage
46
+
47
+ The simplest way to use KingHmac::Auth is with the KingHmac::Auth.sign! and
48
+ KingHmac::Auth.authenticate? methods. KingHmac::Auth.sign! takes a HTTP request
49
+ object, an access id and a secret key and signs the request with the access_id
50
+ and secret key.
51
+
52
+ On the server side you can then authenticate these requests using the
53
+ KingHmac::Auth.authenticated? method. This takes the same arguments as the sign!
54
+ method but returns true if the request has been signed with the access id and
55
+ secret or false if it hasn't.
56
+
57
+ If you have more than one set of credentials you might find it useful to create
58
+ an instance of the KingHmac::Auth class, passing your credentials as a Hash of
59
+ access id => secret keys, like so:
60
+ @hmac_auth = KingHmac::Auth.new('access_id1' => 'secret1', 'access_id2' => 'secret2')
61
+
62
+ You can then use the instance methods of the @hmac_auth object to sign and
63
+ authenticate requests, for example:
64
+ @hmac_auth.sign!(request, "access_id1")
65
+
66
+ Sign +request+ with "access_id1" and it's corresponding secret key. Similarly
67
+ authentication is done like so:
68
+ @hmac_auth.authenticated?(request)
69
+ which will return true if the request has been signed with one of the access id
70
+ and secret key pairs provided in the constructor.
71
+
72
+ === Supported HTTP request objects
73
+
74
+ KingHmac::Auth will do its best to figure out which type it is an handle it
75
+ accordingly.
76
+ * Net::HTTP::HTTPRequest
77
+ * CGI::Request
78
+ * Webrick HTTP request object.
79
+ * Rack::Request
80
+
81
+ === access id
82
+ The access_id is used to identify the secret key that was used to sign the
83
+ request. Think of it as like a user name, it allows you to hand out different
84
+ keys to different clients and authenticate each of them individually. The
85
+ access_id is sent in the clear so you should avoid making it an important string.
86
+
87
+ === secret key
88
+ The secret key is the shared secret between the client and the server.
89
+ You should make this sufficiently random so that is can't be guessed or exposed
90
+ to dictionary attacks.
91
+ The follow code will give you a pretty good secret key:
92
+
93
+ random = File.read('/dev/random', 512)
94
+ secret_key = Base64.encode64(Digest::SHA2.new(512).digest(random))
95
+
96
+ === Rails Integration
97
+
98
+ KingHmac::Auth supports authentication within Rails controllers and signing of
99
+ requests generated by Active Resource. See
100
+ KingHmac::Rails::ControllerFilter::ClassMethods and
101
+ KingHmac::Rails::ActiveResourceExtension::BaseHmac::ClassMethods for details.
102
+
103
+ == How does it work?
104
+
105
+ When creating a signature for a HTTP request KingHmac::Auth first generates a
106
+ canonical representation of the request.
107
+
108
+ This canonical string is created like so:
109
+
110
+ canonical_string = HTTP-Verb + "\n" +
111
+ Content-Type + "\n" +
112
+ Content-MD5 + "\n" +
113
+ Date + "\n" +
114
+ request-uri;
115
+
116
+ Where Content-Type, Content-MD5 and Date are all taken from the headers of the
117
+ request. If Content-Type or Content-MD5 are not present, they are substituted
118
+ with an empty string. If Date is not present it is added to the request headers
119
+ with the value +Time.now.httpdate+. +request-uri+ is the path component of the
120
+ request, without any query string, i.e. everything up to the ?.
121
+
122
+ This string is then used with the secret to generate a SHA1 HMAC using the
123
+ following:
124
+
125
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), secret_key, canonical_string)
126
+
127
+ The result is then Base64 encoded and added to the headers of the request as the
128
+ +Authorization+ header in the format:
129
+ Authorization: KingHmac::Auth <access_id>:<base64 encoded king-hmac>
130
+
131
+ When authenaticating a request, KingHmac::Auth looks for the Authorization
132
+ header in the above format, parses out the components, regenerates a HMAC for
133
+ the request, using the secret key identified by the access id and then compares
134
+ the generated HMAC with the one provided by the client. If they match the
135
+ request is authenticated.
136
+
137
+ Using these details it is possible to build code that will sign and authenticate
138
+ KingHmac::Auth style requests in other languages.
139
+
140
+
141
+ == Authors and Contributors
142
+
143
+ This gem started with a copy & disection of auth-king-hmac gem v1.1.1.
144
+ Most of this doc was written by Sean Geoghegan
145
+ auth-king-hmac was developed by Sean Geoghegan http://rubyforge.org/projects/auth-king-hmac && by Peerworks[http://peerworks.org].
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/rdoctask'
4
+ require 'spec/rake/spectask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "king_hmac"
10
+ gem.summary = %Q{A Ruby Gem for authenticating HTTP requests using a HMAC}
11
+ gem.description = %Q{A Ruby Gem for authenticating HTTP requests using a HMAC}
12
+ gem.email = "gl@salesking.eu"
13
+ gem.homepage = "http://github.com/salesking/king_hmac"
14
+ gem.authors = ["Georg Leciejewski"]
15
+ gem.add_development_dependency "rspec", ">= 0"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ desc 'Default: run specs.'
24
+ task :default => :spec
25
+
26
+ spec_files = Rake::FileList["spec/**/*_spec.rb"]
27
+
28
+ desc "Run specs"
29
+ Spec::Rake::SpecTask.new do |t|
30
+ t.spec_files = spec_files
31
+ t.spec_opts = ["-c"]
32
+ end
33
+
34
+ desc "Generate code coverage"
35
+ Spec::Rake::SpecTask.new(:coverage) do |t|
36
+ t.spec_files = spec_files
37
+ t.rcov = true
38
+ t.rcov_opts = ['--exclude', 'spec,/var/lib/gems']
39
+ end
40
+
41
+ desc 'Generate king_hmac documentation.'
42
+ Rake::RDocTask.new(:rdoc) do |rdoc|
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = 'king_hmac'
45
+ rdoc.options << '--line-numbers' << '--inline-source'
46
+ rdoc.rdoc_files.include('README')
47
+ rdoc.rdoc_files.include('lib/**/*.rb')
48
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require "#{File.dirname(__FILE__)}/lib/king_hmac"
data/king_hmac.gemspec ADDED
@@ -0,0 +1,60 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{king_hmac}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Georg Leciejewski"]
12
+ s.date = %q{2010-04-10}
13
+ s.description = %q{A Ruby Gem for authenticating HTTP requests using a HMAC}
14
+ s.email = %q{gl@salesking.eu}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "init.rb",
25
+ "king_hmac.gemspec",
26
+ "lib/king_hmac.rb",
27
+ "lib/king_hmac/auth.rb",
28
+ "lib/king_hmac/cannonical_string.rb",
29
+ "lib/king_hmac/headers.rb",
30
+ "lib/king_hmac/rack/middleware.rb",
31
+ "lib/king_hmac/rails/active_resource.rb",
32
+ "lib/king_hmac/rails/controller.rb",
33
+ "spec/fixtures/credentials.yml",
34
+ "spec/king_hmac/king_hmac_spec.rb",
35
+ "spec/spec_helper.rb"
36
+ ]
37
+ s.homepage = %q{http://github.com/salesking/king_hmac}
38
+ s.rdoc_options = ["--charset=UTF-8"]
39
+ s.require_paths = ["lib"]
40
+ s.rubygems_version = %q{1.3.6}
41
+ s.summary = %q{A Ruby Gem for authenticating HTTP requests using a HMAC}
42
+ s.test_files = [
43
+ "spec/king_hmac/king_hmac_spec.rb",
44
+ "spec/spec_helper.rb"
45
+ ]
46
+
47
+ if s.respond_to? :specification_version then
48
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
49
+ s.specification_version = 3
50
+
51
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
52
+ s.add_development_dependency(%q<rspec>, [">= 0"])
53
+ else
54
+ s.add_dependency(%q<rspec>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<rspec>, [">= 0"])
58
+ end
59
+ end
60
+
@@ -0,0 +1,143 @@
1
+ module KingHmac
2
+ # This module provides a HMAC Authentication method for HTTP requests. It should work with
3
+ # net/http request classes and CGIRequest classes and hence Rails.
4
+ #
5
+ # It is loosely based on the Amazon Web Services Authentication mechanism but
6
+ # generalized to be useful to any application that requires HMAC based authentication.
7
+ # As a result of the generalization, it won't work with AWS because it doesn't support
8
+ # the Amazon extension headers.
9
+ #
10
+ # === References
11
+ # Cryptographic Hash functions:: http://en.wikipedia.org/wiki/Cryptographic_hash_function
12
+ # SHA-1 Hash function:: http://en.wikipedia.org/wiki/SHA-1
13
+ # HMAC algorithm:: http://en.wikipedia.org/wiki/HMAC
14
+ # RFC 2104:: http://tools.ietf.org/html/rfc2104
15
+ #
16
+ class Auth
17
+
18
+ include KingHmac::Headers
19
+
20
+
21
+ @@default_signature_class = KingHmac::CanonicalString
22
+
23
+ # Create an KingHmac::Auth instance using the given credential store
24
+ #
25
+ # Credential Store:
26
+ # * Credential store must respond to the [] method and return a secret for access key id
27
+ #
28
+ # Options:
29
+ # Override default options
30
+ # * <tt>:service_id</tt> - Service ID used in the AUTHORIZATION header string. Default is KingHmac::Auth.
31
+ # * <tt>:signature_method</tt> - Proc object that takes request and produces the signature string
32
+ # used for authentication. Default is CanonicalString.
33
+ # Examples:
34
+ # my_hmac = KingHmac::Auth.new('access_id1' => 'secret1', 'access_id2' => 'secret2')
35
+ #
36
+ # cred_store = { 'access_id1' => 'secret1', 'access_id2' => 'secret2' }
37
+ # options = { :service_id => 'MyApp', :signature_method => lambda { |r| MyRequestString.new(r) } }
38
+ # my_hmac = KingHmac::Auth.new(cred_store, options)
39
+ #
40
+ def initialize(credential_store, options = nil)
41
+ @credential_store = credential_store
42
+
43
+ # Defaults
44
+ @service_id = self.class.name
45
+ @signature_class = @@default_signature_class
46
+
47
+ unless options.nil?
48
+ @service_id = options[:service_id] if options.key?(:service_id)
49
+ @signature_class = options[:signature] if options.key?(:signature) && options[:signature].is_a?(Class)
50
+ end
51
+
52
+ @signature_method = lambda { |r| @signature_class.send(:new, r) }
53
+ end
54
+
55
+ # Generates canonical signing string for given request
56
+ #
57
+ # Supports same options as KingHmac::Auth.initialize for overriding service_id and
58
+ # signature method.
59
+ #
60
+ def self.canonical_string(request, options = nil)
61
+ self.new(nil, options).canonical_string(request)
62
+ end
63
+
64
+ # Generates signature string for a given secret
65
+ #
66
+ # Supports same options as KingHmac::Auth.initialize for overriding service_id and
67
+ # signature method.
68
+ #
69
+ def self.signature(request, secret, options = nil)
70
+ self.new(nil, options).signature(request, secret)
71
+ end
72
+
73
+ # Signs a request using a given access key id and secret.
74
+ #
75
+ # Supports same options as KingHmac::Auth.initialize for overriding service_id and
76
+ # signature method.
77
+ #
78
+ def self.sign!(request, access_key_id, secret, options = nil)
79
+ credentials = { access_key_id => secret }
80
+ self.new(credentials, options).sign!(request, access_key_id)
81
+ end
82
+
83
+ # Authenticates a request using HMAC
84
+ #
85
+ # Supports same options as KingHmac::Auth.initialize for overriding service_id and
86
+ # signature method.
87
+ #
88
+ def self.authenticated?(request, access_key_id, secret, options)
89
+ credentials = { access_key_id => secret }
90
+ self.new(credentials, options).authenticated?(request)
91
+ end
92
+
93
+ # Signs a request using the access_key_id and the secret associated with that id
94
+ # in the credential store.
95
+ #
96
+ # Signing a requests adds an Authorization header to the request in the format:
97
+ #
98
+ # <service_id> <access_key_id>:<signature>
99
+ #
100
+ # where <signature> is the Base64 encoded HMAC-SHA1 of the CanonicalString and the secret.
101
+ #
102
+ def sign!(request, access_key_id)
103
+ secret = @credential_store[access_key_id]
104
+ raise ArgumentError, "No secret found for key id '#{access_key_id}'" if secret.nil?
105
+ request['Authorization'] = authorization(request, access_key_id, secret)
106
+ end
107
+
108
+ # Authenticates a request using HMAC
109
+ #
110
+ # Returns true if the request has an KingHmac::Auth Authorization header and
111
+ # the access id and HMAC match an id and HMAC produced for the secret
112
+ # in the credential store. Otherwise returns false.
113
+ #
114
+ def authenticated?(request)
115
+ rx = Regexp.new("#{@service_id} ([^:]+):(.+)$")
116
+ if md = rx.match(authorization_header(request))
117
+ access_key_id = md[1]
118
+ hmac = md[2]
119
+ secret = @credential_store[access_key_id]
120
+ !secret.nil? && hmac == signature(request, secret)
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ def signature(request, secret)
127
+ digest = OpenSSL::Digest::Digest.new('sha1')
128
+ Base64.encode64(OpenSSL::HMAC.digest(digest, secret, canonical_string(request))).strip
129
+ end
130
+
131
+ def canonical_string(request)
132
+ @signature_method.call(request)
133
+ end
134
+
135
+ def authorization_header(request)
136
+ find_header(%w(Authorization HTTP_AUTHORIZATION), headers(request))
137
+ end
138
+
139
+ def authorization(request, access_key_id, secret)
140
+ "#{@service_id} #{access_key_id}:#{signature(request, secret)}"
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,69 @@
1
+ module KingHmac
2
+ # Build a Canonical String for a HTTP request.
3
+ #
4
+ # A Canonical String has the following format:
5
+ #
6
+ # CanonicalString = HTTP-Verb + "\n" +
7
+ # Content-Type + "\n" +
8
+ # Content-MD5 + "\n" +
9
+ # Date + "\n" +
10
+ # request-uri;
11
+ #
12
+ #
13
+ # If the Date header doesn't exist, one will be generated since
14
+ # Net/HTTP will generate one if it doesn't exist and it will be
15
+ # used on the server side to do authentication.
16
+ #
17
+ class CanonicalString < String # :nodoc:
18
+ include Headers
19
+
20
+ def initialize(request)
21
+ self << request_method(request) + "\n"
22
+ self << header_values(headers(request)) + "\n"
23
+ self << request_path(request)
24
+ end
25
+
26
+ private
27
+ def request_method(request)
28
+ if request.respond_to?(:request_method) && request.request_method.is_a?(String)
29
+ request.request_method
30
+ elsif request.respond_to?(:method) && request.method.is_a?(String)
31
+ request.method
32
+ elsif request.respond_to?(:env) && request.env
33
+ request.env['REQUEST_METHOD']
34
+ else
35
+ raise ArgumentError, "Don't know how to get the request method from #{request.inspect}"
36
+ end
37
+ end
38
+
39
+ def header_values(headers)
40
+ [ content_type(headers),
41
+ content_md5(headers),
42
+ (date(headers) or headers['Date'] = Time.now.utc.httpdate)
43
+ ].join("\n")
44
+ end
45
+
46
+ def content_type(headers)
47
+ find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE), headers)
48
+ end
49
+
50
+ def date(headers)
51
+ find_header(%w(DATE HTTP_DATE), headers)
52
+ end
53
+
54
+ def content_md5(headers)
55
+ find_header(%w(CONTENT-MD5 CONTENT_MD5), headers)
56
+ end
57
+
58
+ def request_path(request)
59
+ # Try unparsed_uri in case it is a Webrick request
60
+ path = if request.respond_to?(:unparsed_uri)
61
+ request.unparsed_uri
62
+ else
63
+ request.path
64
+ end
65
+
66
+ path[/^[^?]*/]
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,28 @@
1
+ module KingHmac
2
+ module Headers # :nodoc:
3
+ # Gets the headers for a request.
4
+ #
5
+ # Attempts to deal with known HTTP header representations in Ruby.
6
+ # Currently handles net/http and Rails.
7
+ #
8
+ def headers(request)
9
+ if request.respond_to?(:headers)
10
+ request.headers
11
+ elsif request.respond_to?(:env) && request.env
12
+ request.env
13
+ elsif request.respond_to?(:[])
14
+ request
15
+ else
16
+ raise ArgumentError, "Don't know how to get the headers from #{request.inspect}"
17
+ end
18
+ end
19
+
20
+ def find_header(keys, headers)
21
+ val = keys.map do |key|
22
+ headers[key]
23
+ end.compact.first
24
+ var = 'adsf'
25
+ val
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,44 @@
1
+ require 'rack/request'
2
+ module KingHmac
3
+ module Rack
4
+ class Middleware
5
+
6
+ # === Parameter
7
+ # opts<Hash>::
8
+ # === opts params:
9
+ # keys<Hash{String=>String}>:: Must be an array of accesskey=> secret
10
+ # respond to the [] method and return a secret for access key id
11
+ # only<Array[String]>:: path's to protect
12
+ def initialize(app, opts={})
13
+ @app = app
14
+ @opts = opts
15
+ @plain_error = "HMAC Authentication failed. Get yourself a valid HMAC Key .. Dude .. or ask your admin to get you some credentials"
16
+ @hmac_auth = KingHmac::Auth.new(@opts['keys'])
17
+ end
18
+
19
+ def call(env)
20
+ path = env['PATH_INFO']
21
+ do_hmac_check = @opts['only'].detect{|i| path.include?(i) }
22
+ if do_hmac_check
23
+ unless hmac_authenticated?(::Rack::Request.new(env))
24
+ headers = {'Content-Type' => "text/plain",
25
+ 'Content-Length' => "#{@plain_error.length}",
26
+ 'WWW-Authenticate' => 'AuthHMAC'
27
+ }
28
+ [401, headers, [@plain_error]]
29
+ else #valid credentials
30
+ @app.call(env)
31
+ end
32
+ else # unprotected
33
+ @app.call(env)
34
+ end
35
+ end
36
+
37
+ def hmac_authenticated?(request)
38
+ @hmac_auth.authenticated?(request)
39
+ end
40
+
41
+
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,99 @@
1
+ module ActiveResourceExtension # :nodoc:
2
+ module BaseHmac # :nodoc:
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+
6
+ base.class_inheritable_accessor :hmac_access_id
7
+ base.class_inheritable_accessor :hmac_secret
8
+ base.class_inheritable_accessor :use_hmac
9
+ base.class_inheritable_accessor :hmac_options
10
+ end
11
+
12
+ module ClassMethods
13
+ # Call with an Active Resource class definition to sign
14
+ # all HTTP requests sent by that class with the provided
15
+ # credentials.
16
+ #
17
+ # Can be called with either a hash or two separate parameters
18
+ # like so:
19
+ #
20
+ # class MyResource < ActiveResource::Base
21
+ # with_auth_hmac("my_access_id", "my_secret")
22
+ # end
23
+ #
24
+ # or
25
+ #
26
+ # class MyOtherResource < ActiveResource::Base
27
+ # with_auth_hmac("my_access_id" => "my_secret")
28
+ # end
29
+ #
30
+ #
31
+ # This has only been tested with Rails 2.1 and since it is virtually a monkey
32
+ # patch of the internals of ActiveResource it might not work with past or
33
+ # future versions.
34
+ #
35
+ def with_auth_hmac(access_id, secret = nil, options = nil)
36
+ if access_id.is_a?(Hash)
37
+ self.hmac_access_id = access_id.keys.first
38
+ self.hmac_secret = access_id[self.hmac_access_id]
39
+ else
40
+ self.hmac_access_id = access_id
41
+ self.hmac_secret = secret
42
+ end
43
+ self.use_hmac = true
44
+ self.hmac_options = options
45
+
46
+ class << self
47
+ alias_method_chain :connection, :hmac
48
+ end
49
+ end
50
+
51
+ def connection_with_hmac(refresh = false) # :nodoc:
52
+ c = connection_without_hmac(refresh)
53
+ c.hmac_access_id = self.hmac_access_id
54
+ c.hmac_secret = self.hmac_secret
55
+ c.use_hmac = self.use_hmac
56
+ c.hmac_options = self.hmac_options
57
+ c
58
+ end
59
+ end
60
+
61
+ module InstanceMethods # :nodoc:
62
+ end
63
+ end
64
+
65
+ module Connection # :nodoc:
66
+ def self.included(base)
67
+ base.send :alias_method_chain, :request, :hmac
68
+ base.class_eval do
69
+ attr_accessor :hmac_secret, :hmac_access_id, :use_hmac, :hmac_options
70
+ end
71
+ end
72
+
73
+ def request_with_hmac(method, path, *arguments)
74
+ if use_hmac && hmac_access_id && hmac_secret
75
+ arguments.last['Date'] = Time.now.httpdate if arguments.last['Date'].nil?
76
+ temp = "Net::HTTP::#{method.to_s.capitalize}".constantize.new(path, arguments.last)
77
+ AuthHMAC.sign!(temp, hmac_access_id, hmac_secret, hmac_options)
78
+ arguments.last['Authorization'] = temp['Authorization']
79
+ end
80
+
81
+ request_without_hmac(method, path, *arguments)
82
+ end
83
+ end
84
+
85
+ unless defined?(ActiveResource)
86
+ begin
87
+ require 'rubygems'
88
+ gem 'activeresource'
89
+ require 'activeresource'
90
+ rescue
91
+ nil
92
+ end
93
+ end
94
+
95
+ if defined?(ActiveResource)
96
+ ActiveResource::Base.send(:include, BaseHmac)
97
+ ActiveResource::Connection.send(:include, Connection)
98
+ end
99
+ end