jmoses_api-auth 1.0.4
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/.document +5 -0
- data/.gitignore +44 -0
- data/.rspec +3 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +46 -0
- data/LICENSE.txt +20 -0
- data/README.md +185 -0
- data/Rakefile +22 -0
- data/VERSION +1 -0
- data/api_auth.gemspec +26 -0
- data/lib/api-auth.rb +2 -0
- data/lib/api_auth/base.rb +97 -0
- data/lib/api_auth/errors.rb +9 -0
- data/lib/api_auth/headers.rb +82 -0
- data/lib/api_auth/helpers.rb +18 -0
- data/lib/api_auth/railtie.rb +129 -0
- data/lib/api_auth/request_drivers/action_controller.rb +85 -0
- data/lib/api_auth/request_drivers/action_dispatch.rb +15 -0
- data/lib/api_auth/request_drivers/curb.rb +70 -0
- data/lib/api_auth/request_drivers/net_http.rb +78 -0
- data/lib/api_auth/request_drivers/rack.rb +85 -0
- data/lib/api_auth/request_drivers/rest_client.rb +93 -0
- data/lib/api_auth.rb +16 -0
- data/spec/api_auth_spec.rb +407 -0
- data/spec/application_helper.rb +2 -0
- data/spec/headers_spec.rb +223 -0
- data/spec/helpers_spec.rb +14 -0
- data/spec/railtie_spec.rb +114 -0
- data/spec/spec_helper.rb +22 -0
- data/spec/test_helper.rb +2 -0
- metadata +212 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
# Integration with Rails
|
4
|
+
#
|
5
|
+
class Rails # :nodoc:
|
6
|
+
|
7
|
+
module ControllerMethods # :nodoc:
|
8
|
+
|
9
|
+
module InstanceMethods # :nodoc:
|
10
|
+
|
11
|
+
def get_api_access_id_from_request
|
12
|
+
ApiAuth.access_id(request)
|
13
|
+
end
|
14
|
+
|
15
|
+
def api_authenticated?(secret_key)
|
16
|
+
ApiAuth.authentic?(request, secret_key)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
unless defined?(ActionController)
|
22
|
+
begin
|
23
|
+
require 'rubygems'
|
24
|
+
gem 'actionpack'
|
25
|
+
gem 'activesupport'
|
26
|
+
require 'action_controller'
|
27
|
+
require 'active_support'
|
28
|
+
rescue LoadError
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if defined?(ActionController::Base)
|
34
|
+
ActionController::Base.send(:include, ControllerMethods::InstanceMethods)
|
35
|
+
end
|
36
|
+
|
37
|
+
end # ControllerMethods
|
38
|
+
|
39
|
+
module ActiveResourceExtension # :nodoc:
|
40
|
+
|
41
|
+
module ActiveResourceApiAuth # :nodoc:
|
42
|
+
|
43
|
+
def self.included(base)
|
44
|
+
base.extend(ClassMethods)
|
45
|
+
|
46
|
+
if base.respond_to?('class_attribute')
|
47
|
+
base.class_attribute :hmac_access_id
|
48
|
+
base.class_attribute :hmac_secret_key
|
49
|
+
base.class_attribute :use_hmac
|
50
|
+
else
|
51
|
+
base.class_inheritable_accessor :hmac_access_id
|
52
|
+
base.class_inheritable_accessor :hmac_secret_key
|
53
|
+
base.class_inheritable_accessor :use_hmac
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
module ClassMethods
|
59
|
+
|
60
|
+
def with_api_auth(access_id, secret_key)
|
61
|
+
self.hmac_access_id = access_id
|
62
|
+
self.hmac_secret_key = secret_key
|
63
|
+
self.use_hmac = true
|
64
|
+
|
65
|
+
class << self
|
66
|
+
alias_method_chain :connection, :auth
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def connection_with_auth(refresh = false)
|
71
|
+
c = connection_without_auth(refresh)
|
72
|
+
c.hmac_access_id = self.hmac_access_id
|
73
|
+
c.hmac_secret_key = self.hmac_secret_key
|
74
|
+
c.use_hmac = self.use_hmac
|
75
|
+
c
|
76
|
+
end
|
77
|
+
|
78
|
+
end # class methods
|
79
|
+
|
80
|
+
module InstanceMethods
|
81
|
+
end
|
82
|
+
|
83
|
+
end # BaseApiAuth
|
84
|
+
|
85
|
+
module Connection
|
86
|
+
|
87
|
+
def self.included(base)
|
88
|
+
base.send :alias_method_chain, :request, :auth
|
89
|
+
base.class_eval do
|
90
|
+
attr_accessor :hmac_secret_key, :hmac_access_id, :use_hmac
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def request_with_auth(method, path, *arguments)
|
95
|
+
if use_hmac && hmac_access_id && hmac_secret_key
|
96
|
+
h = arguments.last
|
97
|
+
tmp = "Net::HTTP::#{method.to_s.capitalize}".constantize.new(path, h)
|
98
|
+
tmp.body = arguments[0] if arguments.length > 1
|
99
|
+
ApiAuth.sign!(tmp, hmac_access_id, hmac_secret_key)
|
100
|
+
arguments.last['Content-MD5'] = tmp['Content-MD5'] if tmp['Content-MD5']
|
101
|
+
arguments.last['DATE'] = tmp['DATE']
|
102
|
+
arguments.last['Authorization'] = tmp['Authorization']
|
103
|
+
end
|
104
|
+
|
105
|
+
request_without_auth(method, path, *arguments)
|
106
|
+
end
|
107
|
+
|
108
|
+
end # Connection
|
109
|
+
|
110
|
+
unless defined?(ActiveResource)
|
111
|
+
begin
|
112
|
+
require 'rubygems'
|
113
|
+
gem 'activeresource'
|
114
|
+
require 'active_resource'
|
115
|
+
rescue LoadError
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
if defined?(ActiveResource)
|
121
|
+
ActiveResource::Base.send(:include, ActiveResourceApiAuth)
|
122
|
+
ActiveResource::Connection.send(:include, Connection)
|
123
|
+
end
|
124
|
+
|
125
|
+
end # ActiveResourceExtension
|
126
|
+
|
127
|
+
end # Rails
|
128
|
+
|
129
|
+
end # ApiAuth
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
module RequestDrivers # :nodoc:
|
4
|
+
|
5
|
+
class ActionControllerRequest # :nodoc:
|
6
|
+
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
@headers = fetch_headers
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_auth_header(header)
|
16
|
+
@request.env["Authorization"] = header
|
17
|
+
@headers = fetch_headers
|
18
|
+
@request
|
19
|
+
end
|
20
|
+
|
21
|
+
def calculated_md5
|
22
|
+
if @request.body
|
23
|
+
body = @request.raw_post
|
24
|
+
else
|
25
|
+
body = ''
|
26
|
+
end
|
27
|
+
Digest::MD5.base64digest(body)
|
28
|
+
end
|
29
|
+
|
30
|
+
def populate_content_md5
|
31
|
+
if @request.put? || @request.post?
|
32
|
+
@request.env["Content-MD5"] = calculated_md5
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def md5_mismatch?
|
37
|
+
if @request.put? || @request.post?
|
38
|
+
calculated_md5 != content_md5
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_headers
|
45
|
+
capitalize_keys @request.env
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_type
|
49
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
50
|
+
value.nil? ? "" : value
|
51
|
+
end
|
52
|
+
|
53
|
+
def content_md5
|
54
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5 HTTP_CONTENT_MD5))
|
55
|
+
value.nil? ? "" : value
|
56
|
+
end
|
57
|
+
|
58
|
+
def request_uri
|
59
|
+
@request.request_uri
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_date
|
63
|
+
@request.env['DATE'] = Time.now.utc.httpdate
|
64
|
+
end
|
65
|
+
|
66
|
+
def timestamp
|
67
|
+
value = find_header(%w(DATE HTTP_DATE))
|
68
|
+
value.nil? ? "" : value
|
69
|
+
end
|
70
|
+
|
71
|
+
def authorization_header
|
72
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def find_header(keys)
|
78
|
+
keys.map {|key| @headers[key] }.compact.first
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
module RequestDrivers # :nodoc:
|
4
|
+
|
5
|
+
class CurbRequest # :nodoc:
|
6
|
+
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
@headers = fetch_headers
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_auth_header(header)
|
16
|
+
@request.headers.merge!({ "Authorization" => header })
|
17
|
+
@headers = fetch_headers
|
18
|
+
@request
|
19
|
+
end
|
20
|
+
|
21
|
+
def populate_content_md5
|
22
|
+
nil #doesn't appear to be possible
|
23
|
+
end
|
24
|
+
|
25
|
+
def md5_mismatch?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_headers
|
30
|
+
capitalize_keys @request.headers
|
31
|
+
end
|
32
|
+
|
33
|
+
def content_type
|
34
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
35
|
+
value.nil? ? "" : value
|
36
|
+
end
|
37
|
+
|
38
|
+
def content_md5
|
39
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
|
40
|
+
value.nil? ? "" : value
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_uri
|
44
|
+
@request.url
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_date
|
48
|
+
@request.headers.merge!({ "DATE" => Time.now.utc.httpdate })
|
49
|
+
end
|
50
|
+
|
51
|
+
def timestamp
|
52
|
+
value = find_header(%w(DATE HTTP_DATE))
|
53
|
+
value.nil? ? "" : value
|
54
|
+
end
|
55
|
+
|
56
|
+
def authorization_header
|
57
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def find_header(keys)
|
63
|
+
keys.map {|key| @headers[key] }.compact.first
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
module RequestDrivers # :nodoc:
|
4
|
+
|
5
|
+
class NetHttpRequest # :nodoc:
|
6
|
+
|
7
|
+
def initialize(request)
|
8
|
+
@request = request
|
9
|
+
@headers = fetch_headers
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_auth_header(header)
|
14
|
+
@request["Authorization"] = header
|
15
|
+
@headers = fetch_headers
|
16
|
+
@request
|
17
|
+
end
|
18
|
+
|
19
|
+
def calculated_md5
|
20
|
+
Digest::MD5.base64digest(@request.body || '')
|
21
|
+
end
|
22
|
+
|
23
|
+
def populate_content_md5
|
24
|
+
if @request.class::REQUEST_HAS_BODY
|
25
|
+
@request["Content-MD5"] = calculated_md5
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def md5_mismatch?
|
30
|
+
if @request.class::REQUEST_HAS_BODY
|
31
|
+
calculated_md5 != content_md5
|
32
|
+
else
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_headers
|
38
|
+
@request
|
39
|
+
end
|
40
|
+
|
41
|
+
def content_type
|
42
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
43
|
+
value.nil? ? "" : value
|
44
|
+
end
|
45
|
+
|
46
|
+
def content_md5
|
47
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
|
48
|
+
value.nil? ? "" : value
|
49
|
+
end
|
50
|
+
|
51
|
+
def request_uri
|
52
|
+
@request.path
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_date
|
56
|
+
@request["DATE"] = Time.now.utc.httpdate
|
57
|
+
end
|
58
|
+
|
59
|
+
def timestamp
|
60
|
+
value = find_header(%w(DATE HTTP_DATE))
|
61
|
+
value.nil? ? "" : value
|
62
|
+
end
|
63
|
+
|
64
|
+
def authorization_header
|
65
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def find_header(keys)
|
71
|
+
keys.map {|key| @headers[key] }.compact.first
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
module RequestDrivers # :nodoc:
|
4
|
+
|
5
|
+
class RackRequest # :nodoc:
|
6
|
+
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
@headers = fetch_headers
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_auth_header(header)
|
16
|
+
@request.env.merge!({ "Authorization" => header })
|
17
|
+
@headers = fetch_headers
|
18
|
+
@request
|
19
|
+
end
|
20
|
+
|
21
|
+
def calculated_md5
|
22
|
+
if @request.body
|
23
|
+
body = @request.body.read
|
24
|
+
else
|
25
|
+
body = ''
|
26
|
+
end
|
27
|
+
Digest::MD5.base64digest(body)
|
28
|
+
end
|
29
|
+
|
30
|
+
def populate_content_md5
|
31
|
+
if ['POST', 'PUT'].include?(@request.request_method)
|
32
|
+
@request.env["Content-MD5"] = calculated_md5
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def md5_mismatch?
|
37
|
+
if ['POST', 'PUT'].include?(@request.request_method)
|
38
|
+
calculated_md5 != content_md5
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def fetch_headers
|
45
|
+
capitalize_keys @request.env
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_type
|
49
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
50
|
+
value.nil? ? "" : value
|
51
|
+
end
|
52
|
+
|
53
|
+
def content_md5
|
54
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
|
55
|
+
value.nil? ? "" : value
|
56
|
+
end
|
57
|
+
|
58
|
+
def request_uri
|
59
|
+
@request.url
|
60
|
+
end
|
61
|
+
|
62
|
+
def set_date
|
63
|
+
@request.env.merge!({ "DATE" => Time.now.utc.httpdate })
|
64
|
+
end
|
65
|
+
|
66
|
+
def timestamp
|
67
|
+
value = find_header(%w(DATE HTTP_DATE))
|
68
|
+
value.nil? ? "" : value
|
69
|
+
end
|
70
|
+
|
71
|
+
def authorization_header
|
72
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def find_header(keys)
|
78
|
+
keys.map {|key| @headers[key] }.compact.first
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# give access to RestClient @processed_headers
|
2
|
+
module RestClient;class Request;attr_accessor :processed_headers;end;end
|
3
|
+
|
4
|
+
module ApiAuth
|
5
|
+
|
6
|
+
module RequestDrivers # :nodoc:
|
7
|
+
|
8
|
+
class RestClientRequest # :nodoc:
|
9
|
+
|
10
|
+
include ApiAuth::Helpers
|
11
|
+
|
12
|
+
def initialize(request)
|
13
|
+
@request = request
|
14
|
+
@headers = fetch_headers
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def set_auth_header(header)
|
19
|
+
@request.headers.merge!({ "Authorization" => header })
|
20
|
+
@headers = fetch_headers
|
21
|
+
save_headers # enforce update of processed_headers based on last updated headers
|
22
|
+
@request
|
23
|
+
end
|
24
|
+
|
25
|
+
def calculated_md5
|
26
|
+
if @request.payload
|
27
|
+
body = @request.payload.read
|
28
|
+
else
|
29
|
+
body = ''
|
30
|
+
end
|
31
|
+
Digest::MD5.base64digest(body)
|
32
|
+
end
|
33
|
+
|
34
|
+
def populate_content_md5
|
35
|
+
if [:post, :put].include?(@request.method)
|
36
|
+
@request.headers["Content-MD5"] = calculated_md5
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def md5_mismatch?
|
41
|
+
if [:post, :put].include?(@request.method)
|
42
|
+
calculated_md5 != content_md5
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def fetch_headers
|
49
|
+
capitalize_keys @request.headers
|
50
|
+
end
|
51
|
+
|
52
|
+
def content_type
|
53
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
54
|
+
value.nil? ? "" : value
|
55
|
+
end
|
56
|
+
|
57
|
+
def content_md5
|
58
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
|
59
|
+
value.nil? ? "" : value
|
60
|
+
end
|
61
|
+
|
62
|
+
def request_uri
|
63
|
+
@request.url
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_date
|
67
|
+
@request.headers.merge!({ "DATE" => Time.now.utc.httpdate })
|
68
|
+
end
|
69
|
+
|
70
|
+
def timestamp
|
71
|
+
value = find_header(%w(DATE HTTP_DATE))
|
72
|
+
value.nil? ? "" : value
|
73
|
+
end
|
74
|
+
|
75
|
+
def authorization_header
|
76
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def find_header(keys)
|
82
|
+
keys.map {|key| @headers[key] }.compact.first
|
83
|
+
end
|
84
|
+
|
85
|
+
def save_headers
|
86
|
+
@request.processed_headers = @request.make_headers(@headers)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/api_auth.rb
ADDED
@@ -0,0 +1,16 @@
|
|
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
|
+
require 'api_auth/request_drivers/action_dispatch'
|
12
|
+
require 'api_auth/request_drivers/rack'
|
13
|
+
|
14
|
+
require 'api_auth/headers'
|
15
|
+
require 'api_auth/base'
|
16
|
+
require 'api_auth/railtie'
|