api-auth 2.2.0 → 2.4.1
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.
- checksums.yaml +4 -4
- data/.rubocop.yml +11 -52
- data/.rubocop_todo.yml +92 -0
- data/.travis.yml +15 -14
- data/CHANGELOG.md +28 -0
- data/Gemfile +1 -1
- data/README.md +77 -38
- data/VERSION +1 -1
- data/api_auth.gemspec +15 -11
- data/gemfiles/http2.gemfile +7 -0
- data/gemfiles/http3.gemfile +7 -0
- data/gemfiles/http4.gemfile +7 -0
- data/gemfiles/rails_5.gemfile +5 -7
- data/gemfiles/rails_51.gemfile +5 -5
- data/gemfiles/rails_52.gemfile +9 -0
- data/gemfiles/rails_60.gemfile +11 -0
- data/lib/api_auth.rb +3 -0
- data/lib/api_auth/base.rb +2 -2
- data/lib/api_auth/headers.rb +19 -8
- data/lib/api_auth/railtie.rb +9 -5
- data/lib/api_auth/request_drivers/action_controller.rb +1 -0
- data/lib/api_auth/request_drivers/faraday.rb +2 -1
- data/lib/api_auth/request_drivers/grape_request.rb +87 -0
- data/lib/api_auth/request_drivers/http.rb +96 -0
- data/lib/api_auth/request_drivers/httpi.rb +1 -0
- data/lib/api_auth/request_drivers/net_http.rb +1 -1
- data/lib/api_auth/request_drivers/rack.rb +1 -0
- data/lib/api_auth/request_drivers/rest_client.rb +3 -2
- data/spec/api_auth_spec.rb +7 -0
- data/spec/headers_spec.rb +26 -8
- data/spec/request_drivers/action_controller_spec.rb +10 -4
- data/spec/request_drivers/action_dispatch_spec.rb +17 -11
- data/spec/request_drivers/curb_spec.rb +9 -3
- data/spec/request_drivers/faraday_spec.rb +6 -0
- data/spec/request_drivers/grape_request_spec.rb +279 -0
- data/spec/request_drivers/http_spec.rb +190 -0
- data/spec/request_drivers/httpi_spec.rb +6 -0
- data/spec/request_drivers/net_http_spec.rb +6 -0
- data/spec/request_drivers/rack_spec.rb +6 -0
- data/spec/request_drivers/rest_client_spec.rb +93 -15
- data/spec/spec_helper.rb +3 -4
- metadata +102 -66
- data/gemfiles/rails_4.gemfile +0 -11
- data/gemfiles/rails_41.gemfile +0 -11
- data/gemfiles/rails_42.gemfile +0 -11
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.4.1
|
data/api_auth.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
$LOAD_PATH.push File.expand_path('
|
1
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = 'api-auth'
|
@@ -9,19 +9,23 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.authors = ['Mauricio Gomes']
|
10
10
|
s.email = 'mauricio@edge14.com'
|
11
11
|
|
12
|
-
s.
|
13
|
-
|
12
|
+
s.required_ruby_version = '>= 2.4.0'
|
13
|
+
|
14
|
+
s.add_development_dependency 'actionpack', '< 6.1', '> 4.0'
|
15
|
+
s.add_development_dependency 'activeresource', '>= 4.0'
|
16
|
+
s.add_development_dependency 'activesupport', '< 6.1', '> 4.0'
|
14
17
|
s.add_development_dependency 'amatch'
|
15
|
-
s.add_development_dependency '
|
16
|
-
s.add_development_dependency '
|
17
|
-
s.add_development_dependency 'activesupport', '< 6.0', '> 4.0'
|
18
|
-
s.add_development_dependency 'activeresource', '~> 4.0'
|
19
|
-
s.add_development_dependency 'rest-client', '~> 1.6.0'
|
20
|
-
s.add_development_dependency 'curb', '~> 0.8.1'
|
21
|
-
s.add_development_dependency 'httpi'
|
18
|
+
s.add_development_dependency 'appraisal'
|
19
|
+
s.add_development_dependency 'curb', '~> 0.8'
|
22
20
|
s.add_development_dependency 'faraday', '>= 0.10'
|
21
|
+
s.add_development_dependency 'http'
|
22
|
+
s.add_development_dependency 'httpi'
|
23
23
|
s.add_development_dependency 'multipart-post', '~> 2.0'
|
24
|
-
s.add_development_dependency '
|
24
|
+
s.add_development_dependency 'pry'
|
25
|
+
s.add_development_dependency 'rake'
|
26
|
+
s.add_development_dependency 'rest-client', '~> 2.0'
|
27
|
+
s.add_development_dependency 'grape', '~> 1.1.0'
|
28
|
+
s.add_development_dependency 'rspec', '~> 3.4'
|
25
29
|
|
26
30
|
s.files = `git ls-files`.split("\n")
|
27
31
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
data/gemfiles/rails_5.gemfile
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# This file was generated by Appraisal
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
gem
|
6
|
-
gem
|
7
|
-
gem
|
5
|
+
gem 'actionpack', '~> 5.0.2'
|
6
|
+
gem 'activeresource', '~> 5.0.0'
|
7
|
+
gem 'activesupport', '~> 5.0.2'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
gemspec :path => "../"
|
9
|
+
gemspec path: '../'
|
data/gemfiles/rails_51.gemfile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# This file was generated by Appraisal
|
2
2
|
|
3
|
-
source
|
3
|
+
source 'https://rubygems.org'
|
4
4
|
|
5
|
-
gem
|
6
|
-
gem
|
7
|
-
gem
|
5
|
+
gem 'actionpack', '~> 5.1.1'
|
6
|
+
gem 'activeresource', '~> 5.1.0'
|
7
|
+
gem 'activesupport', '~> 5.1.1'
|
8
8
|
|
9
|
-
gemspec :
|
9
|
+
gemspec path: '../'
|
data/lib/api_auth.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'openssl'
|
2
2
|
require 'base64'
|
3
|
+
require 'time'
|
3
4
|
|
4
5
|
require 'api_auth/errors'
|
5
6
|
require 'api_auth/helpers'
|
@@ -8,10 +9,12 @@ require 'api_auth/request_drivers/net_http'
|
|
8
9
|
require 'api_auth/request_drivers/curb'
|
9
10
|
require 'api_auth/request_drivers/rest_client'
|
10
11
|
require 'api_auth/request_drivers/action_controller'
|
12
|
+
require 'api_auth/request_drivers/grape_request'
|
11
13
|
require 'api_auth/request_drivers/action_dispatch'
|
12
14
|
require 'api_auth/request_drivers/rack'
|
13
15
|
require 'api_auth/request_drivers/httpi'
|
14
16
|
require 'api_auth/request_drivers/faraday'
|
17
|
+
require 'api_auth/request_drivers/http'
|
15
18
|
|
16
19
|
require 'api_auth/headers'
|
17
20
|
require 'api_auth/base'
|
data/lib/api_auth/base.rb
CHANGED
@@ -71,7 +71,7 @@ module ApiAuth
|
|
71
71
|
|
72
72
|
private
|
73
73
|
|
74
|
-
AUTH_HEADER_PATTERN = /APIAuth(?:-HMAC-(MD5|SHA(?:1|224|256|384|512)?))? ([^:]+):(.+)
|
74
|
+
AUTH_HEADER_PATTERN = /APIAuth(?:-HMAC-(MD5|SHA(?:1|224|256|384|512)?))? ([^:]+):(.+)$/.freeze
|
75
75
|
|
76
76
|
def request_within_time_window?(headers, clock_skew)
|
77
77
|
Time.httpdate(headers.timestamp).utc > (Time.now.utc - clock_skew) &&
|
@@ -105,7 +105,7 @@ module ApiAuth
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def hmac_signature(headers, secret_key, options)
|
108
|
-
canonical_string = headers.canonical_string(options[:override_http_method])
|
108
|
+
canonical_string = headers.canonical_string(options[:override_http_method], options[:headers_to_sign])
|
109
109
|
digest = OpenSSL::Digest.new(options[:digest])
|
110
110
|
b64_encode(OpenSSL::HMAC.digest(digest, secret_key, canonical_string))
|
111
111
|
end
|
data/lib/api_auth/headers.rb
CHANGED
@@ -26,6 +26,8 @@ module ApiAuth
|
|
26
26
|
else
|
27
27
|
ActionControllerRequest.new(request)
|
28
28
|
end
|
29
|
+
when /Grape::Request/
|
30
|
+
GrapeRequest.new(request)
|
29
31
|
when /ActionDispatch::Request/
|
30
32
|
ActionDispatchRequest.new(request)
|
31
33
|
when /ActionController::CgiRequest/
|
@@ -34,10 +36,13 @@ module ApiAuth
|
|
34
36
|
HttpiRequest.new(request)
|
35
37
|
when /Faraday::Request/
|
36
38
|
FaradayRequest.new(request)
|
39
|
+
when /HTTP::Request/
|
40
|
+
HttpRequest.new(request)
|
37
41
|
end
|
38
42
|
|
39
43
|
return new_request if new_request
|
40
44
|
return RackRequest.new(request) if request.is_a?(Rack::Request)
|
45
|
+
|
41
46
|
raise UnknownHTTPRequest, "#{request.class} is not yet supported."
|
42
47
|
end
|
43
48
|
private :initialize_request_driver
|
@@ -47,18 +52,24 @@ module ApiAuth
|
|
47
52
|
@request.timestamp
|
48
53
|
end
|
49
54
|
|
50
|
-
def canonical_string(override_method = nil)
|
55
|
+
def canonical_string(override_method = nil, headers_to_sign = [])
|
51
56
|
request_method = override_method || @request.http_method
|
52
57
|
|
53
|
-
if request_method.nil?
|
54
|
-
|
58
|
+
raise ArgumentError, 'unable to determine the http method from the request, please supply an override' if request_method.nil?
|
59
|
+
|
60
|
+
headers = @request.fetch_headers
|
61
|
+
|
62
|
+
canonical_array = [request_method.upcase,
|
63
|
+
@request.content_type,
|
64
|
+
@request.content_md5,
|
65
|
+
parse_uri(@request.original_uri || @request.request_uri),
|
66
|
+
@request.timestamp]
|
67
|
+
|
68
|
+
if headers_to_sign.is_a?(Array) && headers_to_sign.any?
|
69
|
+
headers_to_sign.each { |h| canonical_array << headers[h] if headers[h].present? }
|
55
70
|
end
|
56
71
|
|
57
|
-
|
58
|
-
@request.content_type,
|
59
|
-
@request.content_md5,
|
60
|
-
parse_uri(@request.original_uri || @request.request_uri),
|
61
|
-
@request.timestamp].join(',')
|
72
|
+
canonical_array.join(',')
|
62
73
|
end
|
63
74
|
|
64
75
|
# Returns the authorization header from the request's headers
|
data/lib/api_auth/railtie.rb
CHANGED
@@ -13,8 +13,10 @@ module ApiAuth
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
if defined?(
|
17
|
-
|
16
|
+
if defined?(ActiveSupport)
|
17
|
+
ActiveSupport.on_load(:action_controller) do
|
18
|
+
ActionController::Base.include(ControllerMethods::InstanceMethods)
|
19
|
+
end
|
18
20
|
end
|
19
21
|
end # ControllerMethods
|
20
22
|
|
@@ -80,9 +82,11 @@ module ApiAuth
|
|
80
82
|
end
|
81
83
|
end # Connection
|
82
84
|
|
83
|
-
if defined?(
|
84
|
-
|
85
|
-
|
85
|
+
if defined?(ActiveSupport)
|
86
|
+
ActiveSupport.on_load(:active_resource) do
|
87
|
+
ActiveResource::Base.include(ActiveResourceApiAuth)
|
88
|
+
ActiveResource::Connection.include(Connection)
|
89
|
+
end
|
86
90
|
end
|
87
91
|
end # ActiveResourceExtension
|
88
92
|
end # Rails
|
@@ -16,12 +16,13 @@ module ApiAuth
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def calculated_md5
|
19
|
-
body = @request.body
|
19
|
+
body = @request.body || ''
|
20
20
|
md5_base64digest(body)
|
21
21
|
end
|
22
22
|
|
23
23
|
def populate_content_md5
|
24
24
|
return unless %w[POST PUT].include?(@request.method.to_s.upcase)
|
25
|
+
|
25
26
|
@request.headers['Content-MD5'] = calculated_md5
|
26
27
|
fetch_headers
|
27
28
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
module RequestDrivers # :nodoc:
|
3
|
+
class GrapeRequest # :nodoc:
|
4
|
+
include ApiAuth::Helpers
|
5
|
+
|
6
|
+
def initialize(request)
|
7
|
+
@request = request
|
8
|
+
save_headers
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_auth_header(header)
|
13
|
+
@request.env['HTTP_AUTHORIZATION'] = header
|
14
|
+
save_headers # enforce update of processed_headers based on last updated headers
|
15
|
+
@request
|
16
|
+
end
|
17
|
+
|
18
|
+
def calculated_md5
|
19
|
+
body = @request.body.read
|
20
|
+
@request.body.rewind
|
21
|
+
md5_base64digest(body)
|
22
|
+
end
|
23
|
+
|
24
|
+
def populate_content_md5
|
25
|
+
return if !@request.put? && !@request.post?
|
26
|
+
|
27
|
+
@request.env['HTTP_CONTENT_MD5'] = calculated_md5
|
28
|
+
save_headers
|
29
|
+
end
|
30
|
+
|
31
|
+
def md5_mismatch?
|
32
|
+
if @request.put? || @request.post?
|
33
|
+
calculated_md5 != content_md5
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_headers
|
40
|
+
capitalize_keys @request.env
|
41
|
+
end
|
42
|
+
|
43
|
+
def http_method
|
44
|
+
@request.request_method.upcase
|
45
|
+
end
|
46
|
+
|
47
|
+
def content_type
|
48
|
+
find_header %w[HTTP_X_HMAC_CONTENT_TYPE HTTP_X_CONTENT_TYPE CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE]
|
49
|
+
end
|
50
|
+
|
51
|
+
def content_md5
|
52
|
+
find_header %w[HTTP_X_HMAC_CONTENT_MD5 HTTP_X_CONTENT_MD5 CONTENT-MD5 CONTENT_MD5 HTTP_CONTENT_MD5]
|
53
|
+
end
|
54
|
+
|
55
|
+
def original_uri
|
56
|
+
find_header %w[HTTP_X_HMAC_ORIGINAL_URI HTTP_X_ORIGINAL_URI X-ORIGINAL-URI X_ORIGINAL_URI]
|
57
|
+
end
|
58
|
+
|
59
|
+
def request_uri
|
60
|
+
@request.url
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_date
|
64
|
+
@request.env['HTTP_DATE'] = Time.now.utc.httpdate
|
65
|
+
save_headers
|
66
|
+
end
|
67
|
+
|
68
|
+
def timestamp
|
69
|
+
find_header %w[HTTP_X_HMAC_DATE HTTP_X_DATE DATE HTTP_DATE]
|
70
|
+
end
|
71
|
+
|
72
|
+
def authorization_header
|
73
|
+
find_header %w[HTTP_X_HMAC_AUTHORIZATION HTTP_X_AUTHORIZATION Authorization AUTHORIZATION HTTP_AUTHORIZATION]
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def find_header(keys)
|
79
|
+
keys.map { |key| @headers[key] }.compact.first
|
80
|
+
end
|
81
|
+
|
82
|
+
def save_headers
|
83
|
+
@headers = fetch_headers
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
module RequestDrivers # :nodoc:
|
3
|
+
class HttpRequest # :nodoc:
|
4
|
+
include ApiAuth::Helpers
|
5
|
+
|
6
|
+
def initialize(request)
|
7
|
+
@request = request
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_auth_header(header)
|
11
|
+
@request['Authorization'] = header
|
12
|
+
@request
|
13
|
+
end
|
14
|
+
|
15
|
+
def calculated_md5
|
16
|
+
md5_base64digest(body)
|
17
|
+
end
|
18
|
+
|
19
|
+
def populate_content_md5
|
20
|
+
return unless %w[POST PUT].include?(http_method)
|
21
|
+
|
22
|
+
@request['Content-MD5'] = calculated_md5
|
23
|
+
end
|
24
|
+
|
25
|
+
def md5_mismatch?
|
26
|
+
if %w[POST PUT].include?(http_method)
|
27
|
+
calculated_md5 != content_md5
|
28
|
+
else
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def http_method
|
34
|
+
@request.verb.to_s.upcase
|
35
|
+
end
|
36
|
+
|
37
|
+
def content_type
|
38
|
+
find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
|
39
|
+
end
|
40
|
+
|
41
|
+
def content_md5
|
42
|
+
find_header(%w[CONTENT-MD5 CONTENT_MD5])
|
43
|
+
end
|
44
|
+
|
45
|
+
def original_uri
|
46
|
+
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
|
47
|
+
end
|
48
|
+
|
49
|
+
def request_uri
|
50
|
+
@request.uri.request_uri
|
51
|
+
end
|
52
|
+
|
53
|
+
def set_date
|
54
|
+
@request['Date'] = Time.now.utc.httpdate
|
55
|
+
end
|
56
|
+
|
57
|
+
def timestamp
|
58
|
+
find_header(%w[DATE HTTP_DATE])
|
59
|
+
end
|
60
|
+
|
61
|
+
def authorization_header
|
62
|
+
find_header %w[Authorization AUTHORIZATION HTTP_AUTHORIZATION]
|
63
|
+
end
|
64
|
+
|
65
|
+
def body
|
66
|
+
if body_source.respond_to?(:read)
|
67
|
+
result = body_source.read
|
68
|
+
body_source.rewind
|
69
|
+
result
|
70
|
+
else
|
71
|
+
body_source.to_s
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def fetch_headers
|
76
|
+
capitalize_keys @request.headers.to_h
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def find_header(keys)
|
82
|
+
keys.map { |key| @request[key] }.compact.first
|
83
|
+
end
|
84
|
+
|
85
|
+
def body_source
|
86
|
+
body = @request.body
|
87
|
+
|
88
|
+
if defined?(::HTTP::Request::Body)
|
89
|
+
body.respond_to?(:source) ? body.source : body.instance_variable_get(:@body)
|
90
|
+
else
|
91
|
+
body
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|