ey-hmac 2.0.2 → 2.3.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 +5 -5
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/ruby.yml +37 -0
- data/.rubocop.yml +29 -0
- data/.rubocop_todo.yml +82 -0
- data/CHANGELOG.md +68 -0
- data/Gemfile +7 -2
- data/Rakefile +3 -1
- data/ey-hmac.gemspec +18 -14
- data/lib/ey-hmac/adapter/faraday.rb +40 -22
- data/lib/ey-hmac/adapter/rack.rb +22 -19
- data/lib/ey-hmac/adapter.rb +57 -23
- data/lib/ey-hmac/faraday.rb +8 -5
- data/lib/ey-hmac/rack.rb +4 -1
- data/lib/ey-hmac/version.rb +4 -2
- data/lib/ey-hmac.rb +94 -94
- data/spec/faraday_spec.rb +70 -68
- data/spec/rack_spec.rb +47 -42
- data/spec/shared/authenticated.rb +43 -21
- data/spec/spec_helper.rb +6 -3
- metadata +24 -21
- data/.travis.yml +0 -7
data/spec/rack_spec.rb
CHANGED
@@ -1,130 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'securerandom'
|
3
5
|
|
4
|
-
describe
|
6
|
+
describe 'rack' do
|
5
7
|
before(:all) { Bundler.require(:rack) }
|
6
8
|
|
7
9
|
let!(:key_id) { SecureRandom.hex(8) }
|
8
10
|
let!(:key_secret) { SecureRandom.hex(16) }
|
9
11
|
|
10
|
-
describe
|
11
|
-
let(:adapter)
|
12
|
+
describe 'adapter' do
|
13
|
+
let(:adapter) { Ey::Hmac::Adapter::Rack }
|
12
14
|
|
13
|
-
it
|
15
|
+
it 'signs and read request' do
|
14
16
|
request = Rack::Request.new(
|
15
|
-
|
16
|
-
|
17
|
+
'rack.input' => StringIO.new('{1: 2}'),
|
18
|
+
'HTTP_CONTENT_TYPE' => 'application/json'
|
17
19
|
)
|
18
20
|
Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
|
19
21
|
|
20
|
-
expect(request.env['HTTP_AUTHORIZATION']).to start_with(
|
22
|
+
expect(request.env['HTTP_AUTHORIZATION']).to start_with('EyHmac')
|
21
23
|
expect(request.env['HTTP_CONTENT_DIGEST']).to eq(Digest::MD5.hexdigest(request.body.tap(&:rewind).read))
|
22
24
|
expect(Time.parse(request.env['HTTP_DATE'])).not_to be_nil
|
23
25
|
|
24
26
|
yielded = false
|
25
27
|
|
26
|
-
expect(Ey::Hmac.
|
28
|
+
expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
|
27
29
|
expect(key_id).to eq(key_id)
|
28
30
|
yielded = true
|
29
31
|
key_secret
|
30
|
-
end
|
32
|
+
end
|
31
33
|
|
32
34
|
expect(yielded).to be_truthy
|
33
35
|
end
|
34
36
|
|
35
|
-
it
|
37
|
+
it 'does not set Content-Digest if body is nil' do
|
36
38
|
request = Rack::Request.new(
|
37
|
-
|
39
|
+
'HTTP_CONTENT_TYPE' => 'application/json'
|
38
40
|
)
|
39
41
|
|
40
42
|
Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
|
41
43
|
|
42
|
-
expect(request.env['HTTP_AUTHORIZATION']).to start_with(
|
44
|
+
expect(request.env['HTTP_AUTHORIZATION']).to start_with('EyHmac')
|
43
45
|
expect(request.env).not_to have_key('HTTP_CONTENT_DIGEST')
|
44
46
|
expect(Time.parse(request.env['HTTP_DATE'])).not_to be_nil
|
45
47
|
|
46
48
|
yielded = false
|
47
49
|
|
48
|
-
expect(Ey::Hmac.
|
50
|
+
expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
|
49
51
|
expect(key_id).to eq(key_id)
|
50
52
|
yielded = true
|
51
53
|
key_secret
|
52
|
-
end
|
54
|
+
end
|
53
55
|
|
54
56
|
expect(yielded).to be_truthy
|
55
57
|
end
|
56
58
|
|
57
|
-
it
|
59
|
+
it 'does not set Content-Digest if body is empty' do
|
58
60
|
request = Rack::Request.new(
|
59
|
-
|
60
|
-
|
61
|
+
'rack.input' => StringIO.new(''),
|
62
|
+
'HTTP_CONTENT_TYPE' => 'application/json'
|
61
63
|
)
|
62
64
|
|
63
65
|
Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
|
64
66
|
|
65
|
-
expect(request.env['HTTP_AUTHORIZATION']).to start_with(
|
67
|
+
expect(request.env['HTTP_AUTHORIZATION']).to start_with('EyHmac')
|
66
68
|
expect(request.env).not_to have_key('HTTP_CONTENT_DIGEST')
|
67
69
|
expect(Time.parse(request.env['HTTP_DATE'])).not_to be_nil
|
68
70
|
|
69
71
|
yielded = false
|
70
72
|
|
71
|
-
expect(Ey::Hmac.
|
73
|
+
expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
|
72
74
|
expect(key_id).to eq(key_id)
|
73
75
|
yielded = true
|
74
76
|
key_secret
|
75
|
-
end
|
77
|
+
end
|
76
78
|
|
77
79
|
expect(yielded).to be_truthy
|
78
80
|
end
|
79
81
|
|
80
|
-
context
|
81
|
-
let(:request)
|
82
|
+
context 'with a request' do
|
83
|
+
let(:request) do
|
82
84
|
Rack::Request.new(
|
83
|
-
|
84
|
-
|
85
|
+
'rack.input' => StringIO.new('{1: 2}'),
|
86
|
+
'HTTP_CONTENT_TYPE' => 'application/json'
|
85
87
|
)
|
86
|
-
|
88
|
+
end
|
87
89
|
|
88
|
-
include_examples
|
90
|
+
include_examples 'authentication'
|
89
91
|
end
|
90
92
|
end
|
91
93
|
|
92
|
-
describe
|
93
|
-
it
|
94
|
+
describe 'middleware' do
|
95
|
+
it 'accepts a SHA1 signature' do
|
94
96
|
app = lambda do |env|
|
95
|
-
authenticated = Ey::Hmac.authenticated?(env, accept_digests: [
|
97
|
+
authenticated = Ey::Hmac.authenticated?(env, accept_digests: %i[sha1 sha256],
|
98
|
+
adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
|
96
99
|
(auth_id == key_id) && key_secret
|
97
100
|
end
|
98
|
-
[(authenticated ? 200 : 401), {
|
101
|
+
[(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
|
99
102
|
end
|
100
103
|
|
101
|
-
|
104
|
+
outer_key_id = key_id
|
105
|
+
outer_key_secret = key_secret
|
102
106
|
client = Rack::Client.new do
|
103
|
-
use Ey::Hmac::Rack,
|
107
|
+
use Ey::Hmac::Rack, outer_key_id, outer_key_secret, sign_with: :sha1
|
104
108
|
run app
|
105
109
|
end
|
106
110
|
|
107
|
-
expect(client.get(
|
111
|
+
expect(client.get('/resource').status).to eq(200)
|
108
112
|
end
|
109
113
|
|
110
|
-
it
|
114
|
+
it 'accepts a SHA256 signature' do # default
|
111
115
|
app = lambda do |env|
|
112
116
|
authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
|
113
117
|
(auth_id == key_id) && key_secret
|
114
118
|
end
|
115
|
-
[(authenticated ? 200 : 401), {
|
119
|
+
[(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
|
116
120
|
end
|
117
121
|
|
118
|
-
|
122
|
+
outer_key_id = key_id
|
123
|
+
outer_key_secret = key_secret
|
119
124
|
client = Rack::Client.new do
|
120
|
-
use Ey::Hmac::Rack,
|
125
|
+
use Ey::Hmac::Rack, outer_key_id, outer_key_secret
|
121
126
|
run app
|
122
127
|
end
|
123
128
|
|
124
|
-
expect(client.get(
|
129
|
+
expect(client.get('/resource').status).to eq(200)
|
125
130
|
end
|
126
131
|
|
127
|
-
it
|
132
|
+
it 'accepts multiple digest signatures' do # default
|
128
133
|
require 'ey-hmac/faraday'
|
129
134
|
Bundler.require(:rack)
|
130
135
|
|
@@ -132,15 +137,15 @@ describe "rack" do
|
|
132
137
|
authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
|
133
138
|
(auth_id == key_id) && key_secret
|
134
139
|
end
|
135
|
-
[(authenticated ? 200 : 401), {
|
140
|
+
[(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
|
136
141
|
end
|
137
142
|
|
138
143
|
connection = Faraday.new do |c|
|
139
|
-
c.use :hmac, key_id, key_secret, digest: [
|
144
|
+
c.use :hmac, key_id, key_secret, digest: %i[sha1 sha256]
|
140
145
|
c.adapter(:rack, app)
|
141
146
|
end
|
142
147
|
|
143
|
-
expect(connection.get(
|
148
|
+
expect(connection.get('/resources').status).to eq(200)
|
144
149
|
end
|
145
150
|
end
|
146
151
|
end
|
@@ -1,55 +1,77 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
shared_examples_for 'authentication' do
|
4
|
+
describe '#authenticated?' do
|
5
|
+
it 'does not authenticate invalid secret' do
|
4
6
|
Ey::Hmac.sign!(request, key_id, "#{key_secret}bad", adapter: adapter)
|
5
7
|
|
6
|
-
expect(Ey::Hmac.
|
8
|
+
expect(Ey::Hmac).not_to be_authenticated(request, adapter: adapter) do |auth_id|
|
7
9
|
(auth_id == key_id) && key_secret
|
8
|
-
end
|
10
|
+
end
|
9
11
|
end
|
10
12
|
|
11
|
-
it
|
13
|
+
it 'does not authenticate invalid id' do
|
12
14
|
Ey::Hmac.sign!(request, "what#{key_id}", key_secret, adapter: adapter)
|
13
15
|
|
14
|
-
expect(Ey::Hmac.
|
16
|
+
expect(Ey::Hmac).not_to be_authenticated(request, adapter: adapter) do |auth_id|
|
15
17
|
(auth_id == key_id) && key_secret
|
16
|
-
end
|
18
|
+
end
|
17
19
|
end
|
18
20
|
|
19
|
-
it
|
20
|
-
expect(Ey::Hmac.
|
21
|
+
it 'does not authenticate missing header' do
|
22
|
+
expect(Ey::Hmac).not_to be_authenticated(request, adapter: adapter) do |auth_id|
|
21
23
|
(auth_id == key_id) && key_secret
|
22
|
-
end
|
24
|
+
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
26
|
-
describe
|
27
|
-
it
|
28
|
+
describe '#authenticate!' do
|
29
|
+
it 'does not authenticate invalid secret' do
|
28
30
|
Ey::Hmac.sign!(request, key_id, "#{key_secret}bad", adapter: adapter)
|
29
31
|
|
30
|
-
expect
|
32
|
+
expect do
|
31
33
|
Ey::Hmac.authenticate!(request, adapter: adapter) do |auth_id|
|
32
34
|
(auth_id == key_id) && key_secret
|
33
35
|
end
|
34
|
-
|
36
|
+
end.to raise_exception(Ey::Hmac::SignatureMismatch)
|
35
37
|
end
|
36
38
|
|
37
|
-
it
|
39
|
+
it 'does not authenticate invalid id' do
|
38
40
|
Ey::Hmac.sign!(request, "what#{key_id}", key_secret, adapter: adapter)
|
39
41
|
|
40
|
-
expect
|
42
|
+
expect do
|
41
43
|
Ey::Hmac.authenticate!(request, adapter: adapter) do |auth_id|
|
42
44
|
(auth_id == key_id) && key_secret
|
43
45
|
end
|
44
|
-
|
46
|
+
end.to raise_exception(Ey::Hmac::MissingSecret)
|
45
47
|
end
|
46
48
|
|
47
|
-
it
|
48
|
-
expect
|
49
|
+
it 'does not authenticate missing header' do
|
50
|
+
expect do
|
49
51
|
expect(Ey::Hmac.authenticate!(request, adapter: adapter) do |auth_id|
|
50
52
|
(auth_id == key_id) && key_secret
|
51
53
|
end).to be_falsey
|
52
|
-
|
54
|
+
end.to raise_exception(Ey::Hmac::MissingAuthorization)
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'when the server specifies an HMAC TTL' do
|
58
|
+
it 'does not authenticate expired hmac' do
|
59
|
+
Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
|
60
|
+
expect do
|
61
|
+
Ey::Hmac.authenticate!(request, adapter: adapter, ttl: 0) do |auth_id|
|
62
|
+
(auth_id == key_id) && key_secret
|
63
|
+
end
|
64
|
+
end.to raise_exception(Ey::Hmac::ExpiredHmac)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'authenticates non-expired hmac' do
|
68
|
+
Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
|
69
|
+
expect do
|
70
|
+
Ey::Hmac.authenticate!(request, adapter: adapter, ttl: 100) do |auth_id|
|
71
|
+
(auth_id == key_id) && key_secret
|
72
|
+
end
|
73
|
+
end.not_to raise_exception
|
74
|
+
end
|
53
75
|
end
|
54
76
|
end
|
55
77
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../lib/ey-hmac', __dir__)
|
2
4
|
|
3
5
|
Bundler.require(:test)
|
6
|
+
require 'securerandom'
|
4
7
|
|
5
|
-
Dir[File.expand_path(
|
8
|
+
Dir[File.expand_path('{support,shared}/*.rb', __dir__)].sort.each { |f| require(f) }
|
6
9
|
|
7
10
|
RSpec.configure do |config|
|
8
|
-
config.order =
|
11
|
+
config.order = 'random'
|
9
12
|
end
|
metadata
CHANGED
@@ -1,52 +1,56 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey-hmac
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Josh Lane
|
8
|
-
autorequire:
|
7
|
+
- Josh Lane
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '2.3'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '2.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
description: Lightweight HMAC signing libraries and middleware for Farday and Rack
|
42
42
|
email:
|
43
|
-
-
|
43
|
+
- me@joshualane.com
|
44
44
|
executables: []
|
45
45
|
extensions: []
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
|
+
- ".github/workflows/codeql-analysis.yml"
|
49
|
+
- ".github/workflows/ruby.yml"
|
48
50
|
- ".gitignore"
|
49
|
-
- ".
|
51
|
+
- ".rubocop.yml"
|
52
|
+
- ".rubocop_todo.yml"
|
53
|
+
- CHANGELOG.md
|
50
54
|
- Gemfile
|
51
55
|
- LICENSE.txt
|
52
56
|
- README.md
|
@@ -67,7 +71,7 @@ homepage: ''
|
|
67
71
|
licenses:
|
68
72
|
- MIT
|
69
73
|
metadata: {}
|
70
|
-
post_install_message:
|
74
|
+
post_install_message:
|
71
75
|
rdoc_options: []
|
72
76
|
require_paths:
|
73
77
|
- lib
|
@@ -75,16 +79,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
75
79
|
requirements:
|
76
80
|
- - ">="
|
77
81
|
- !ruby/object:Gem::Version
|
78
|
-
version: '
|
82
|
+
version: '2.5'
|
79
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
84
|
requirements:
|
81
85
|
- - ">="
|
82
86
|
- !ruby/object:Gem::Version
|
83
87
|
version: '0'
|
84
88
|
requirements: []
|
85
|
-
|
86
|
-
|
87
|
-
signing_key:
|
89
|
+
rubygems_version: 3.1.6
|
90
|
+
signing_key:
|
88
91
|
specification_version: 4
|
89
92
|
summary: Lightweight HMAC signing libraries and middleware for Farday and Rack
|
90
93
|
test_files:
|