api-auth 2.5.1 → 2.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +9 -5
- data/.rubocop.yml +5 -1
- data/.rubocop_todo.yml +11 -30
- data/Appraisals +6 -6
- data/CHANGELOG.md +18 -0
- data/README.md +24 -15
- data/VERSION +1 -1
- data/api_auth.gemspec +9 -8
- data/gemfiles/{rails_52.gemfile → rails_70.gemfile} +3 -3
- data/lib/api_auth/base.rb +4 -4
- data/lib/api_auth/headers.rb +6 -4
- data/lib/api_auth/helpers.rb +4 -0
- data/lib/api_auth/railtie.rb +7 -7
- data/lib/api_auth/request_drivers/action_controller.rb +9 -4
- data/lib/api_auth/request_drivers/faraday_env.rb +102 -0
- data/lib/api_auth.rb +1 -0
- data/lib/faraday/api_auth/middleware.rb +35 -0
- data/lib/faraday/api_auth.rb +8 -0
- data/spec/api_auth_spec.rb +3 -3
- data/spec/faraday_middleware_spec.rb +17 -0
- data/spec/headers_spec.rb +8 -8
- data/spec/railtie_spec.rb +2 -1
- data/spec/request_drivers/action_dispatch_spec.rb +91 -6
- data/spec/request_drivers/faraday_env_spec.rb +188 -0
- data/spec/request_drivers/http_spec.rb +1 -1
- data/spec/request_drivers/httpi_spec.rb +2 -2
- data/spec/request_drivers/rest_client_spec.rb +19 -19
- data/spec/spec_helper.rb +6 -2
- metadata +64 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b642f1c16d0ccca2a260e5440012a3d02fe3b333d3cb769a76e139ec97c39368
|
4
|
+
data.tar.gz: 5f68593348b4cbcf7b76250675c7bb355904b8f365129b8a57696e04fed5a802
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f148aa229d78829b86af9772e962463866e3ad85653b9fc759494969c901717be7d9c6b11087f6873253777610d9dcf3b209189e0076249d72a5256dc2acf026
|
7
|
+
data.tar.gz: 6fab592a96c63ab8c52665c321c4291a5bd67fea0d366298a1c3efa82a6819e5a7434f2ed18c0cf48cac301cc219963fcdde831c237868e9a03cc5d09c3ae440
|
data/.github/workflows/main.yml
CHANGED
@@ -9,19 +9,23 @@ jobs:
|
|
9
9
|
fail-fast: true
|
10
10
|
matrix:
|
11
11
|
ruby-version:
|
12
|
-
- 2.5
|
13
12
|
- 2.6
|
14
13
|
- 2.7
|
15
14
|
- 3.0
|
15
|
+
- 3.1
|
16
16
|
gemfile:
|
17
|
-
- rails_52.gemfile
|
18
17
|
- rails_60.gemfile
|
19
18
|
- rails_61.gemfile
|
19
|
+
- rails_70.gemfile
|
20
20
|
exclude:
|
21
|
-
- ruby-version: [ 2.6, 2.7, 3.0 ]
|
22
|
-
gemfile: rails_52.gemfile
|
23
21
|
- ruby-version: 3.0
|
24
22
|
gemfile: rails_60.gemfile
|
23
|
+
- ruby-version: 3.1
|
24
|
+
gemfile: rails_60.gemfile
|
25
|
+
- ruby-version: 2.6
|
26
|
+
gemfile: rails_70.gemfile
|
27
|
+
- ruby-version: 2.7
|
28
|
+
gemfile: rails_70.gemfile
|
25
29
|
steps:
|
26
30
|
- name: Install packages required for `curb` gem
|
27
31
|
run: |
|
@@ -57,7 +61,7 @@ jobs:
|
|
57
61
|
- name: Install Ruby
|
58
62
|
uses: ruby/setup-ruby@v1
|
59
63
|
with:
|
60
|
-
ruby-version: 3.
|
64
|
+
ruby-version: 3.1
|
61
65
|
bundler-cache: true
|
62
66
|
|
63
67
|
- name: Install required gems
|
data/.rubocop.yml
CHANGED
@@ -2,7 +2,7 @@ inherit_from: .rubocop_todo.yml
|
|
2
2
|
|
3
3
|
AllCops:
|
4
4
|
NewCops: enable
|
5
|
-
TargetRubyVersion: 2.
|
5
|
+
TargetRubyVersion: 2.6
|
6
6
|
|
7
7
|
Metrics/AbcSize:
|
8
8
|
Max: 28
|
@@ -33,3 +33,7 @@ Style/StringLiterals:
|
|
33
33
|
|
34
34
|
Lint/DuplicateBranch:
|
35
35
|
Enabled: false
|
36
|
+
|
37
|
+
# Development dependencies in gemspec is valid for gems
|
38
|
+
Gemspec/DevelopmentDependencies:
|
39
|
+
Enabled: false
|
data/.rubocop_todo.yml
CHANGED
@@ -1,19 +1,11 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on
|
3
|
+
# on 2022-08-03 07:19:11 UTC using RuboCop version 1.32.0.
|
4
4
|
# The point is for the user to remove these configuration records
|
5
5
|
# one by one as the offenses are removed from the code base.
|
6
6
|
# Note that changes in the inspected code, or installation of new
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count: 1
|
10
|
-
# Cop supports --auto-correct.
|
11
|
-
# Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include.
|
12
|
-
# Include: **/*.gemspec
|
13
|
-
Gemspec/OrderedDependencies:
|
14
|
-
Exclude:
|
15
|
-
- 'api_auth.gemspec'
|
16
|
-
|
17
9
|
# Offense count: 1
|
18
10
|
# Configuration parameters: AllowSafeAssignment.
|
19
11
|
Lint/AssignmentInCondition:
|
@@ -41,29 +33,19 @@ Lint/Void:
|
|
41
33
|
- 'lib/api_auth/request_drivers/rack.rb'
|
42
34
|
- 'lib/api_auth/request_drivers/rest_client.rb'
|
43
35
|
|
44
|
-
# Offense count: 1
|
45
|
-
# Configuration parameters: IgnoredMethods, CountRepeatedAttributes.
|
46
|
-
Metrics/AbcSize:
|
47
|
-
Max: 28
|
48
|
-
|
49
|
-
# Offense count: 1
|
50
|
-
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
|
51
|
-
# IgnoredMethods: refine
|
52
|
-
Metrics/BlockLength:
|
53
|
-
Max: 27
|
54
|
-
|
55
36
|
# Offense count: 2
|
56
37
|
# Configuration parameters: IgnoredMethods.
|
57
38
|
Metrics/CyclomaticComplexity:
|
58
|
-
Max:
|
39
|
+
Max: 16
|
59
40
|
|
60
|
-
# Offense count:
|
41
|
+
# Offense count: 11
|
61
42
|
Naming/AccessorMethodName:
|
62
43
|
Exclude:
|
63
44
|
- 'lib/api_auth/railtie.rb'
|
64
45
|
- 'lib/api_auth/request_drivers/action_controller.rb'
|
65
46
|
- 'lib/api_auth/request_drivers/curb.rb'
|
66
47
|
- 'lib/api_auth/request_drivers/faraday.rb'
|
48
|
+
- 'lib/api_auth/request_drivers/faraday_env.rb'
|
67
49
|
- 'lib/api_auth/request_drivers/grape_request.rb'
|
68
50
|
- 'lib/api_auth/request_drivers/http.rb'
|
69
51
|
- 'lib/api_auth/request_drivers/httpi.rb'
|
@@ -79,20 +61,14 @@ Naming/MethodParameterName:
|
|
79
61
|
- 'lib/api_auth/base.rb'
|
80
62
|
- 'spec/railtie_spec.rb'
|
81
63
|
|
82
|
-
# Offense count:
|
83
|
-
# Cop supports --auto-correct.
|
84
|
-
Style/CommentedKeyword:
|
85
|
-
Exclude:
|
86
|
-
- 'lib/api_auth/base.rb'
|
87
|
-
- 'lib/api_auth/railtie.rb'
|
88
|
-
|
89
|
-
# Offense count: 3
|
64
|
+
# Offense count: 4
|
90
65
|
# Configuration parameters: AllowedConstants.
|
91
66
|
Style/Documentation:
|
92
67
|
Exclude:
|
93
68
|
- 'spec/**/*'
|
94
69
|
- 'test/**/*'
|
95
70
|
- 'lib/api_auth/railtie.rb'
|
71
|
+
- 'lib/api_auth/request_drivers/rest_client.rb'
|
96
72
|
|
97
73
|
# Offense count: 1
|
98
74
|
# Configuration parameters: AllowedMethods.
|
@@ -100,3 +76,8 @@ Style/Documentation:
|
|
100
76
|
Style/OptionalBooleanParameter:
|
101
77
|
Exclude:
|
102
78
|
- 'lib/api_auth/railtie.rb'
|
79
|
+
|
80
|
+
# Offense count: 1
|
81
|
+
Lint/UselessConstantScoping:
|
82
|
+
Exclude:
|
83
|
+
- 'lib/api_auth/base.rb'
|
data/Appraisals
CHANGED
@@ -1,9 +1,3 @@
|
|
1
|
-
appraise 'rails-52' do
|
2
|
-
gem 'actionpack', '~> 5.2'
|
3
|
-
gem 'activeresource', '~> 5.1'
|
4
|
-
gem 'activesupport', '~> 5.2'
|
5
|
-
end
|
6
|
-
|
7
1
|
appraise 'rails-60' do
|
8
2
|
gem 'actionpack', '~> 6.0'
|
9
3
|
gem 'activeresource', '~> 5.1'
|
@@ -15,3 +9,9 @@ appraise 'rails-61' do
|
|
15
9
|
gem 'activeresource', '~> 5.1'
|
16
10
|
gem 'activesupport', '~> 6.1'
|
17
11
|
end
|
12
|
+
|
13
|
+
appraise 'rails-70' do
|
14
|
+
gem 'actionpack', '~> 7.0'
|
15
|
+
gem 'activeresource', '~> 6.0'
|
16
|
+
gem 'activesupport', '~> 7.0'
|
17
|
+
end
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,21 @@
|
|
1
|
+
# 2.6.0 (2025-01-18)
|
2
|
+
- Add Faraday middleware support (#1322051 Frédéric Mangano)
|
3
|
+
- Add MD5 compatibility option in authentic? method (#a618e15 Samir ALI CHERIF)
|
4
|
+
- Add support for Ruby 3.1 and Rails 7.0 (#552cab0 fwininger)
|
5
|
+
- Drop support for Rails 5 and Ruby 2.5 (#552cab0 fwininger)
|
6
|
+
- Fix HTTPS URL handling (#c734a88 fwininger)
|
7
|
+
- Update Grape to v2.0+ for Rails 7/Rack 3 compatibility
|
8
|
+
- Update Rubocop to v1.50+ and Curb to v1.0+ for Ruby 3.x compatibility
|
9
|
+
- Fix Ruby 2.6 compatibility with Rails 6.x (Logger loading issue)
|
10
|
+
- Add drb gem dependency (2.0.4-2.0.5) for Ruby 3.4+ compatibility while avoiding Ruby 2.6 conflicts
|
11
|
+
|
12
|
+
# 2.5.1 (2021-11-26)
|
13
|
+
- Add spec coverage for all content hashes (#202 fwininger)
|
14
|
+
- Require MFA for Rubygems (#203 fwininger)
|
15
|
+
- Integration with GitHub Actions
|
16
|
+
- Fix look up of `X-AUTHORIZATION-CONTENT-SHA256` header
|
17
|
+
- Adding license information to the gemspec
|
18
|
+
|
1
19
|
# 2.5.0 (2021-05-11)
|
2
20
|
- Add support for Ruby 3.0 (#194 fwininger)
|
3
21
|
- Add support for Rails 6.1 (#194 fwininger)
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ Logins and passwords are for humans. Communication between applications need to
|
|
7
7
|
be protected through different means.
|
8
8
|
|
9
9
|
ApiAuth is a Ruby gem designed to be used both in your client and server
|
10
|
-
HTTP-based applications. It implements the same authentication methods (HMAC-
|
10
|
+
HTTP-based applications. It implements the same authentication methods (HMAC-SHA2)
|
11
11
|
used by Amazon Web Services.
|
12
12
|
|
13
13
|
The gem will sign your requests on the client side and authenticate that
|
@@ -48,10 +48,10 @@ Authorization = APIAuth "#{client access id}:#{signature from step 2}"
|
|
48
48
|
A cURL request would look like:
|
49
49
|
|
50
50
|
```sh
|
51
|
-
curl -X POST --header 'Content-Type: application/json' --header "Date: Tue, 30 May 2017 03:51:43 GMT" --header "Authorization: ${AUTHORIZATION}"
|
51
|
+
curl -X POST --header 'Content-Type: application/json' --header "Date: Tue, 30 May 2017 03:51:43 GMT" --header "Authorization: ${AUTHORIZATION}" https://my-app.com/request_path`
|
52
52
|
```
|
53
53
|
|
54
|
-
5. On the server side, the
|
54
|
+
5. On the server side, the SHA2 HMAC is computed in the same way using the
|
55
55
|
request headers and the client's secret key, which is known to only
|
56
56
|
the client and the server but can be looked up on the server using the client's
|
57
57
|
access id that was attached in the header. The access id can be any integer or
|
@@ -60,18 +60,14 @@ minutes in order to avoid replay attacks.
|
|
60
60
|
|
61
61
|
## References
|
62
62
|
|
63
|
-
* [Hash functions](
|
64
|
-
* [SHA-
|
65
|
-
* [HMAC algorithm](
|
66
|
-
* [RFC 2104 (HMAC)](
|
63
|
+
* [Hash functions](https://en.wikipedia.org/wiki/Cryptographic_hash_function)
|
64
|
+
* [SHA-2 Hash function](https://en.wikipedia.org/wiki/SHA-2)
|
65
|
+
* [HMAC algorithm](https://en.wikipedia.org/wiki/HMAC)
|
66
|
+
* [RFC 2104 (HMAC)](https://tools.ietf.org/html/rfc2104)
|
67
67
|
|
68
68
|
## Requirement
|
69
69
|
|
70
|
-
This gem require Ruby >= 2.
|
71
|
-
|
72
|
-
For older version of Ruby or Rails, please use ApiAuth v2.1 and older.
|
73
|
-
|
74
|
-
**IMPORTANT: v2.0.0 is backwards incompatible with the default settings of v1.x to address a security vulnerability. See [CHANGELOG.md](/CHANGELOG.md) for security update information.**
|
70
|
+
This gem require Ruby >= 2.6 and Rails >= 6.0 if you use rails.
|
75
71
|
|
76
72
|
## Install
|
77
73
|
|
@@ -186,6 +182,19 @@ Simply add this configuration to your Flexirest initializer in your app and it w
|
|
186
182
|
Flexirest::Base.api_auth_credentials(@access_id, @secret_key)
|
187
183
|
```
|
188
184
|
|
185
|
+
### Faraday
|
186
|
+
|
187
|
+
ApiAuth provides a middleware for adding authentication to a Faraday connection:
|
188
|
+
|
189
|
+
```ruby
|
190
|
+
require 'faraday/api_auth'
|
191
|
+
Faraday.new do |f|
|
192
|
+
f.request :api_auth, @access_id, @secret_key
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
The order of middlewares is important. You should make sure api_auth is last.
|
197
|
+
|
189
198
|
## Server
|
190
199
|
|
191
200
|
ApiAuth provides some built in methods to help you generate API keys for your
|
@@ -289,9 +298,9 @@ the public methods for each driver are required to be implemented by your driver
|
|
289
298
|
|
290
299
|
## Authors
|
291
300
|
|
292
|
-
* [Mauricio Gomes](
|
293
|
-
* [Kevin Glowacz](
|
294
|
-
* [Florian Wininger](
|
301
|
+
* [Mauricio Gomes](https://github.com/mgomes)
|
302
|
+
* [Kevin Glowacz](https://github.com/kjg)
|
303
|
+
* [Florian Wininger](https://github.com/fwininger)
|
295
304
|
|
296
305
|
## Copyright
|
297
306
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.6.0
|
data/api_auth.gemspec
CHANGED
@@ -14,28 +14,29 @@ Gem::Specification.new do |s|
|
|
14
14
|
'rubygems_mfa_required' => 'true'
|
15
15
|
}
|
16
16
|
|
17
|
-
s.required_ruby_version = '>= 2.
|
17
|
+
s.required_ruby_version = '>= 2.6.0'
|
18
18
|
|
19
|
-
s.add_development_dependency 'actionpack', '
|
19
|
+
s.add_development_dependency 'actionpack', '>= 6.0'
|
20
20
|
s.add_development_dependency 'activeresource', '>= 4.0'
|
21
|
-
s.add_development_dependency 'activesupport', '
|
21
|
+
s.add_development_dependency 'activesupport', '>= 6.0'
|
22
22
|
s.add_development_dependency 'amatch'
|
23
23
|
s.add_development_dependency 'appraisal'
|
24
|
-
s.add_development_dependency 'curb', '~> 0
|
24
|
+
s.add_development_dependency 'curb', '~> 1.0'
|
25
|
+
# DRb is required for Ruby 3.4+ but must avoid 2.0.6 which breaks Ruby 2.6
|
26
|
+
s.add_development_dependency 'drb', '>= 2.0.4', '< 2.0.6'
|
25
27
|
s.add_development_dependency 'faraday', '>= 1.1.0'
|
28
|
+
s.add_development_dependency 'grape', '~> 2.0'
|
26
29
|
s.add_development_dependency 'http'
|
27
30
|
s.add_development_dependency 'httpi'
|
28
31
|
s.add_development_dependency 'multipart-post', '~> 2.0'
|
29
32
|
s.add_development_dependency 'pry'
|
30
33
|
s.add_development_dependency 'rake'
|
31
34
|
s.add_development_dependency 'rest-client', '~> 2.0'
|
32
|
-
s.add_development_dependency 'grape', '~> 1.1.0'
|
33
|
-
s.add_development_dependency 'rspec', '~> 3.4'
|
34
35
|
s.add_development_dependency 'rexml'
|
35
|
-
s.add_development_dependency '
|
36
|
+
s.add_development_dependency 'rspec', '~> 3.4'
|
37
|
+
s.add_development_dependency 'rubocop', '~> 1.50'
|
36
38
|
|
37
39
|
s.files = `git ls-files`.split("\n")
|
38
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
39
40
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
40
41
|
s.require_paths = ['lib']
|
41
42
|
end
|
data/lib/api_auth/base.rb
CHANGED
@@ -32,9 +32,9 @@ module ApiAuth
|
|
32
32
|
def authentic?(request, secret_key, options = {})
|
33
33
|
return false if secret_key.nil?
|
34
34
|
|
35
|
-
options = { override_http_method: nil }.merge(options)
|
35
|
+
options = { override_http_method: nil, authorize_md5: false }.merge(options)
|
36
36
|
|
37
|
-
headers = Headers.new(request)
|
37
|
+
headers = Headers.new(request, authorize_md5: options[:authorize_md5])
|
38
38
|
|
39
39
|
# 900 seconds is 15 minutes
|
40
40
|
clock_skew = options.fetch(:clock_skew, 900)
|
@@ -118,5 +118,5 @@ module ApiAuth
|
|
118
118
|
def parse_auth_header(auth_header)
|
119
119
|
AUTH_HEADER_PATTERN.match(auth_header)
|
120
120
|
end
|
121
|
-
end
|
122
|
-
end
|
121
|
+
end
|
122
|
+
end
|
data/lib/api_auth/headers.rb
CHANGED
@@ -3,13 +3,13 @@ module ApiAuth
|
|
3
3
|
class Headers
|
4
4
|
include RequestDrivers
|
5
5
|
|
6
|
-
def initialize(request)
|
6
|
+
def initialize(request, authorize_md5: false)
|
7
7
|
@original_request = request
|
8
|
-
@request = initialize_request_driver(request)
|
8
|
+
@request = initialize_request_driver(request, authorize_md5: authorize_md5)
|
9
9
|
true
|
10
10
|
end
|
11
11
|
|
12
|
-
def initialize_request_driver(request)
|
12
|
+
def initialize_request_driver(request, authorize_md5: false)
|
13
13
|
new_request =
|
14
14
|
case request.class.to_s
|
15
15
|
when /Net::HTTP/
|
@@ -29,13 +29,15 @@ module ApiAuth
|
|
29
29
|
when /Grape::Request/
|
30
30
|
GrapeRequest.new(request)
|
31
31
|
when /ActionDispatch::Request/
|
32
|
-
ActionDispatchRequest.new(request)
|
32
|
+
ActionDispatchRequest.new(request, authorize_md5: authorize_md5)
|
33
33
|
when /ActionController::CgiRequest/
|
34
34
|
ActionControllerRequest.new(request)
|
35
35
|
when /HTTPI::Request/
|
36
36
|
HttpiRequest.new(request)
|
37
37
|
when /Faraday::Request/
|
38
38
|
FaradayRequest.new(request)
|
39
|
+
when /Faraday::Env/
|
40
|
+
FaradayEnv.new(request)
|
39
41
|
when /HTTP::Request/
|
40
42
|
HttpRequest.new(request)
|
41
43
|
end
|
data/lib/api_auth/helpers.rb
CHANGED
data/lib/api_auth/railtie.rb
CHANGED
@@ -18,7 +18,7 @@ module ApiAuth
|
|
18
18
|
ActionController::Base.include(ControllerMethods::InstanceMethods)
|
19
19
|
end
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
22
22
|
|
23
23
|
module ActiveResourceExtension # :nodoc:
|
24
24
|
module ActiveResourceApiAuth # :nodoc:
|
@@ -51,11 +51,11 @@ module ApiAuth
|
|
51
51
|
c.api_auth_options = api_auth_options
|
52
52
|
c
|
53
53
|
end
|
54
|
-
end
|
54
|
+
end
|
55
55
|
|
56
56
|
module InstanceMethods
|
57
57
|
end
|
58
|
-
end
|
58
|
+
end
|
59
59
|
|
60
60
|
module Connection
|
61
61
|
def self.included(base)
|
@@ -82,7 +82,7 @@ module ApiAuth
|
|
82
82
|
|
83
83
|
request_without_auth(method, path, *arguments)
|
84
84
|
end
|
85
|
-
end
|
85
|
+
end
|
86
86
|
|
87
87
|
if defined?(ActiveSupport)
|
88
88
|
ActiveSupport.on_load(:active_resource) do
|
@@ -90,6 +90,6 @@ module ApiAuth
|
|
90
90
|
ActiveResource::Connection.include(Connection)
|
91
91
|
end
|
92
92
|
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -3,8 +3,9 @@ module ApiAuth
|
|
3
3
|
class ActionControllerRequest # :nodoc:
|
4
4
|
include ApiAuth::Helpers
|
5
5
|
|
6
|
-
def initialize(request)
|
6
|
+
def initialize(request, authorize_md5: false)
|
7
7
|
@request = request
|
8
|
+
@authorize_md5 = authorize_md5
|
8
9
|
fetch_headers
|
9
10
|
true
|
10
11
|
end
|
@@ -17,7 +18,9 @@ module ApiAuth
|
|
17
18
|
|
18
19
|
def calculated_hash
|
19
20
|
body = @request.raw_post
|
20
|
-
sha256_base64digest(body)
|
21
|
+
hashes = [sha256_base64digest(body)]
|
22
|
+
hashes << md5_base64digest(body) if @authorize_md5
|
23
|
+
hashes
|
21
24
|
end
|
22
25
|
|
23
26
|
def populate_content_hash
|
@@ -29,7 +32,7 @@ module ApiAuth
|
|
29
32
|
|
30
33
|
def content_hash_mismatch?
|
31
34
|
if @request.put? || @request.post?
|
32
|
-
calculated_hash
|
35
|
+
!calculated_hash.include?(content_hash)
|
33
36
|
else
|
34
37
|
false
|
35
38
|
end
|
@@ -48,7 +51,9 @@ module ApiAuth
|
|
48
51
|
end
|
49
52
|
|
50
53
|
def content_hash
|
51
|
-
|
54
|
+
headers = %w[X-AUTHORIZATION-CONTENT-SHA256 X_AUTHORIZATION_CONTENT_SHA256 HTTP_X_AUTHORIZATION_CONTENT_SHA256]
|
55
|
+
headers += %w[CONTENT-MD5 CONTENT_MD5 HTTP_CONTENT_MD5] if @authorize_md5
|
56
|
+
find_header(headers)
|
52
57
|
end
|
53
58
|
|
54
59
|
def original_uri
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
module RequestDrivers # :nodoc:
|
3
|
+
# Internally, Faraday uses the class Faraday::Env to represent requests. The class is not meant
|
4
|
+
# to be directly exposed to users, but this is what Faraday middlewares work with. See
|
5
|
+
# <https://lostisland.github.io/faraday/middleware/>.
|
6
|
+
class FaradayEnv
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
9
|
+
def initialize(env)
|
10
|
+
@env = env
|
11
|
+
end
|
12
|
+
|
13
|
+
def set_auth_header(header)
|
14
|
+
@env.request_headers['Authorization'] = header
|
15
|
+
@env
|
16
|
+
end
|
17
|
+
|
18
|
+
def calculated_hash
|
19
|
+
sha256_base64digest(body)
|
20
|
+
end
|
21
|
+
|
22
|
+
def populate_content_hash
|
23
|
+
return unless %w[POST PUT PATCH].include?(http_method)
|
24
|
+
|
25
|
+
@env.request_headers['X-Authorization-Content-SHA256'] = calculated_hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_hash_mismatch?
|
29
|
+
if %w[POST PUT PATCH].include?(http_method)
|
30
|
+
calculated_hash != content_hash
|
31
|
+
else
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def http_method
|
37
|
+
@env.method.to_s.upcase
|
38
|
+
end
|
39
|
+
|
40
|
+
def content_type
|
41
|
+
type = find_header(%w[CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE])
|
42
|
+
|
43
|
+
# When sending a body-less POST request, the Content-Type is set at the last minute by the
|
44
|
+
# Net::HTTP adapter, which states in the documentation for Net::HTTP#post:
|
45
|
+
#
|
46
|
+
# > You should set Content-Type: header field for POST. If no Content-Type: field given,
|
47
|
+
# > this method uses “application/x-www-form-urlencoded” by default.
|
48
|
+
#
|
49
|
+
# The same applies to PATCH and PUT. Hopefully the other HTTP adapters behave similarly.
|
50
|
+
#
|
51
|
+
type ||= 'application/x-www-form-urlencoded' if %w[POST PATCH PUT].include?(http_method)
|
52
|
+
|
53
|
+
type
|
54
|
+
end
|
55
|
+
|
56
|
+
def content_hash
|
57
|
+
find_header(%w[X-AUTHORIZATION-CONTENT-SHA256])
|
58
|
+
end
|
59
|
+
|
60
|
+
def original_uri
|
61
|
+
find_header(%w[X-ORIGINAL-URI X_ORIGINAL_URI HTTP_X_ORIGINAL_URI])
|
62
|
+
end
|
63
|
+
|
64
|
+
def request_uri
|
65
|
+
@env.url.request_uri
|
66
|
+
end
|
67
|
+
|
68
|
+
def set_date
|
69
|
+
@env.request_headers['Date'] = Time.now.utc.httpdate
|
70
|
+
end
|
71
|
+
|
72
|
+
def timestamp
|
73
|
+
find_header(%w[DATE HTTP_DATE])
|
74
|
+
end
|
75
|
+
|
76
|
+
def authorization_header
|
77
|
+
find_header(%w[Authorization AUTHORIZATION HTTP_AUTHORIZATION])
|
78
|
+
end
|
79
|
+
|
80
|
+
def body
|
81
|
+
body_source = @env.request_body
|
82
|
+
if body_source.respond_to?(:read)
|
83
|
+
result = body_source.read
|
84
|
+
body_source.rewind
|
85
|
+
result
|
86
|
+
else
|
87
|
+
body_source.to_s
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch_headers
|
92
|
+
capitalize_keys @env.request_headers
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def find_header(keys)
|
98
|
+
keys.map { |key| @env.request_headers[key] }.compact.first
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/api_auth.rb
CHANGED
@@ -14,6 +14,7 @@ require 'api_auth/request_drivers/action_dispatch'
|
|
14
14
|
require 'api_auth/request_drivers/rack'
|
15
15
|
require 'api_auth/request_drivers/httpi'
|
16
16
|
require 'api_auth/request_drivers/faraday'
|
17
|
+
require 'api_auth/request_drivers/faraday_env'
|
17
18
|
require 'api_auth/request_drivers/http'
|
18
19
|
|
19
20
|
require 'api_auth/headers'
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'api_auth'
|
2
|
+
|
3
|
+
module Faraday
|
4
|
+
module ApiAuth
|
5
|
+
# Request middleware for Faraday. It takes the same arguments as ApiAuth.sign!.
|
6
|
+
#
|
7
|
+
# You will usually need to include it after the other middlewares since ApiAuth needs to hash
|
8
|
+
# the final request.
|
9
|
+
#
|
10
|
+
# Usage:
|
11
|
+
#
|
12
|
+
# ```ruby
|
13
|
+
# require 'faraday/api_auth'
|
14
|
+
#
|
15
|
+
# conn = Faraday.new do |f|
|
16
|
+
# f.request :api_auth, access_id, secret_key
|
17
|
+
# # Alternatively:
|
18
|
+
# # f.use Faraday::ApiAuth::Middleware, access_id, secret_key
|
19
|
+
# end
|
20
|
+
# ```
|
21
|
+
#
|
22
|
+
class Middleware < Faraday::Middleware
|
23
|
+
def initialize(app, access_id, secret_key, options = {})
|
24
|
+
super(app)
|
25
|
+
@access_id = access_id
|
26
|
+
@secret_key = secret_key
|
27
|
+
@options = options
|
28
|
+
end
|
29
|
+
|
30
|
+
def on_request(env)
|
31
|
+
::ApiAuth.sign!(env, @access_id, @secret_key, @options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/spec/api_auth_spec.rb
CHANGED
@@ -24,7 +24,7 @@ describe 'ApiAuth' do
|
|
24
24
|
end
|
25
25
|
|
26
26
|
describe '.sign!' do
|
27
|
-
let(:request) { RestClient::Request.new(url: '
|
27
|
+
let(:request) { RestClient::Request.new(url: 'https://google.com', method: :get) }
|
28
28
|
let(:headers) { ApiAuth::Headers.new(request) }
|
29
29
|
|
30
30
|
it 'generates date header before signing' do
|
@@ -182,7 +182,7 @@ describe 'ApiAuth' do
|
|
182
182
|
context 'normal APIAuth Auth header' do
|
183
183
|
let(:request) do
|
184
184
|
RestClient::Request.new(
|
185
|
-
url: '
|
185
|
+
url: 'https://google.com',
|
186
186
|
method: :get,
|
187
187
|
headers: { authorization: 'APIAuth 1044:aGVsbG8gd29ybGQ=' }
|
188
188
|
)
|
@@ -196,7 +196,7 @@ describe 'ApiAuth' do
|
|
196
196
|
context 'Corporate prefixed APIAuth header' do
|
197
197
|
let(:request) do
|
198
198
|
RestClient::Request.new(
|
199
|
-
url: '
|
199
|
+
url: 'https://google.com',
|
200
200
|
method: :get,
|
201
201
|
headers: { authorization: 'Corporate APIAuth 1044:aGVsbG8gd29ybGQ=' }
|
202
202
|
)
|