ey-hmac 2.3.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/ey-hmac.rb CHANGED
@@ -1,101 +1,102 @@
1
- require "ey-hmac/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'ey-hmac/version'
2
4
 
3
5
  require 'base64'
4
6
  require 'digest/md5'
5
7
  require 'openssl'
6
8
  require 'time'
7
9
 
8
- module Ey
9
- module Hmac
10
- Error = Class.new(StandardError)
11
-
12
- MissingSecret = Class.new(Error)
13
- MissingAuthorization = Class.new(Error)
14
- SignatureMismatch = Class.new(Error)
15
- ExpiredHmac = Class.new(Error)
16
-
17
- autoload :Adapter, "ey-hmac/adapter"
18
- autoload :Faraday, "ey-hmac/faraday"
19
- autoload :Rack, "ey-hmac/rack"
20
-
21
- def self.default_adapter=(default_adapter)
22
- @default_adapter = default_adapter
23
- end
24
-
25
- def self.default_adapter
26
- @default_adapter ||= if defined?(::Rack) || defined?(::Rails)
27
- Ey::Hmac::Adapter::Rack
28
- elsif defined?(::Faraday)
29
- Ey::Hmac::Adapter::Faraday
30
- end
31
- end
32
-
33
- # Signs request by calculating signature and adding it to the specified header
34
- # @example
35
- # Ey::Hmac.sign!(env, @key_id, @key_secret)
36
- #
37
- # @see Ey::Hmac::Adapter#sign!
38
- #
39
- # @param request [Hash] request environment
40
- # @option options [Ey::Hmac::Adapter] :adapter (#{default_adapter}) adapter to sign request with
41
- # @option options [Integer] :version (nil) signature version
42
- # @option options [String] :authorization_header ('Authorization') Authorization header key.
43
- # @option options [String] :service ('EyHmac') service name prefixed to {Ey::Hmac::Adapter#authorization}
44
- #
45
- # @return [String] authorization signature
46
- def self.sign!(request, key_id, key_secret, options={})
47
- adapter = options[:adapter] || Ey::Hmac.default_adapter
48
-
49
- raise ArgumentError, "Missing adapter and Ey::Hmac.default_adapter" unless adapter
50
-
51
- adapter.new(request, options).sign!(key_id, key_secret)
52
- end
53
-
54
- # @example
55
- # Ey::Hmac.authenticated? do |key_id|
56
- # @consumer = Consumer.where(auth_id: key_id).first
57
- # @consumer && @consumer.auth_key
58
- # end
59
- #
60
- # @see Ey::Hmac::Adapter#authenticated?
61
- # @see Ey::Hmac#authenticate!
62
- #
63
- # @param request [Hash] request environment
64
- # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
65
- # @yieldparam key_id [String] public HMAC key
66
- #
67
- # @return [Boolean] success of authentication
68
- def self.authenticated?(request, options={}, &block)
69
- adapter = options[:adapter] || Ey::Hmac.default_adapter
70
-
71
- raise ArgumentError, "Missing adapter and Ey::Hmac.default_adapter" unless adapter
72
-
73
- adapter.new(request, options).authenticated?(&block)
74
- end
75
-
76
- # Check {Ey::Hmac::Adapter#authorization_signature} against calculated {Ey::Hmac::Adapter#signature}
77
- # @example
78
- # Ey::Hmac.authenticate! do |key_id|
79
- # @consumer = Consumer.where(auth_id: key_id).first
80
- # @consumer && @consumer.auth_key
81
- # end
82
- #
83
- # @see Ey::Hmac::Adapter#authenticate!
84
- #
85
- # @param request [Hash] request environment
86
- # @yieldparam key_id [String] public HMAC key
87
- # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
88
- #
89
- # @raise [SignatureMismatch] if the value of {Ey::Hmac::Adapter#authorization_signature} does not match {Ey::Hmac::Adapter#signature}
90
- # @raise [MissingSecret] if the block does not return a private key matching +key_id+
91
- # @raise [MissingAuthorization] if the value of {Ey::Hmac::Adapter#authorization_signature} is nil
92
- # @return [TrueClass] if authentication was successful
93
- def self.authenticate!(request, options={}, &block)
94
- adapter = options[:adapter] || Ey::Hmac.default_adapter
95
-
96
- raise ArgumentError, "Missing adapter and Ey::Hmac.default_adapter" unless adapter
97
-
98
- adapter.new(request, options).authenticate!(&block)
99
- end
10
+ module Ey::Hmac
11
+ Error = Class.new(StandardError)
12
+
13
+ MissingSecret = Class.new(Error)
14
+ MissingAuthorization = Class.new(Error)
15
+ SignatureMismatch = Class.new(Error)
16
+ ExpiredHmac = Class.new(Error)
17
+
18
+ autoload :Adapter, 'ey-hmac/adapter'
19
+ autoload :Faraday, 'ey-hmac/faraday'
20
+ autoload :Rack, 'ey-hmac/rack'
21
+
22
+ def self.default_adapter=(default_adapter)
23
+ @default_adapter = default_adapter
24
+ end
25
+
26
+ def self.default_adapter
27
+ @default_adapter ||= if defined?(::Rack) || defined?(::Rails)
28
+ Ey::Hmac::Adapter::Rack
29
+ elsif defined?(::Faraday)
30
+ Ey::Hmac::Adapter::Faraday
31
+ end
32
+ end
33
+
34
+ # Signs request by calculating signature and adding it to the specified header
35
+ # @example
36
+ # Ey::Hmac.sign!(env, @key_id, @key_secret)
37
+ #
38
+ # @see Ey::Hmac::Adapter#sign!
39
+ #
40
+ # @param request [Hash] request environment
41
+ # @option options [Ey::Hmac::Adapter] :adapter (#{default_adapter}) adapter to sign request with
42
+ # @option options [Integer] :version (nil) signature version
43
+ # @option options [String] :authorization_header ('Authorization') Authorization header key.
44
+ # @option options [String] :service ('EyHmac') service name prefixed to {Ey::Hmac::Adapter#authorization}
45
+ #
46
+ # @return [String] authorization signature
47
+ def self.sign!(request, key_id, key_secret, options = {})
48
+ adapter = options[:adapter] || Ey::Hmac.default_adapter
49
+
50
+ raise ArgumentError, 'Missing adapter and Ey::Hmac.default_adapter' unless adapter
51
+
52
+ adapter.new(request, options).sign!(key_id, key_secret)
53
+ end
54
+
55
+ # @example
56
+ # Ey::Hmac.authenticated? do |key_id|
57
+ # @consumer = Consumer.where(auth_id: key_id).first
58
+ # @consumer && @consumer.auth_key
59
+ # end
60
+ #
61
+ # @see Ey::Hmac::Adapter#authenticated?
62
+ # @see Ey::Hmac#authenticate!
63
+ #
64
+ # @param request [Hash] request environment
65
+ # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
66
+ # @yieldparam key_id [String] public HMAC key
67
+ #
68
+ # @return [Boolean] success of authentication
69
+ def self.authenticated?(request, options = {}, &block)
70
+ adapter = options[:adapter] || Ey::Hmac.default_adapter
71
+
72
+ raise ArgumentError, 'Missing adapter and Ey::Hmac.default_adapter' unless adapter
73
+
74
+ adapter.new(request, options).authenticated?(&block)
75
+ end
76
+
77
+ # Check {Ey::Hmac::Adapter#authorization_signature} against calculated {Ey::Hmac::Adapter#signature}
78
+ # @example
79
+ # Ey::Hmac.authenticate! do |key_id|
80
+ # @consumer = Consumer.where(auth_id: key_id).first
81
+ # @consumer && @consumer.auth_key
82
+ # end
83
+ #
84
+ # @see Ey::Hmac::Adapter#authenticate!
85
+ #
86
+ # @param request [Hash] request environment
87
+ # @yieldparam key_id [String] public HMAC key
88
+ # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
89
+ #
90
+ # @raise [SignatureMismatch] if the value of {Ey::Hmac::Adapter#authorization_signature} does not
91
+ # match {Ey::Hmac::Adapter#signature}
92
+ # @raise [MissingSecret] if the block does not return a private key matching +key_id+
93
+ # @raise [MissingAuthorization] if the value of {Ey::Hmac::Adapter#authorization_signature} is nil
94
+ # @return [TrueClass] if authentication was successful
95
+ def self.authenticate!(request, options = {}, &block)
96
+ adapter = options[:adapter] || Ey::Hmac.default_adapter
97
+
98
+ raise ArgumentError, 'Missing adapter and Ey::Hmac.default_adapter' unless adapter
99
+
100
+ adapter.new(request, options).authenticate!(&block)
100
101
  end
101
102
  end
data/spec/faraday_spec.rb CHANGED
@@ -1,20 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
- describe "faraday" do
5
+ describe 'faraday' do
4
6
  before(:all) { Bundler.require(:faraday) }
5
7
 
6
8
  let!(:key_id) { SecureRandom.hex(8) }
7
9
  let!(:key_secret) { SecureRandom.hex(16) }
8
10
 
9
- describe "adapter" do
11
+ describe 'adapter' do
10
12
  let!(:adapter) { Ey::Hmac::Adapter::Faraday }
11
13
 
12
- it "signs a multipart post" do
14
+ it 'signs a multipart post' do
13
15
  app = lambda do |env|
14
16
  authenticated = Ey::Hmac.authenticate!(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
15
17
  (auth_id == key_id) && key_secret
16
18
  end
17
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
19
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
18
20
  end
19
21
 
20
22
  require 'ey-hmac/faraday'
@@ -28,113 +30,115 @@ describe "faraday" do
28
30
  c.adapter(:rack, app)
29
31
  end
30
32
 
31
- tempfile = Tempfile.new("hmac")
33
+ tempfile = Tempfile.new('hmac')
32
34
  tempfile.write SecureRandom.hex(512)
33
35
  tempfile.close
34
36
 
35
37
  expect(
36
- connection.post { |req| req.body = {"output" => Faraday::UploadIO.new(tempfile.path, "text/plain")} }.status
38
+ connection.post { |req| req.body = { 'output' => Faraday::UploadIO.new(tempfile.path, 'text/plain') } }.status
37
39
  ).to eq(200)
38
40
  end
39
41
 
40
- it "signs and reads a request" do
41
- request = Faraday::Request.create(:get) { |r|
42
- r.path = "/auth"
43
- r.body = "{1: 2}"
44
- r.headers = {"Content-Type" => "application/xml"}
45
- }.to_env(
46
- Faraday::Connection.new("http://localhost")
42
+ it 'signs and reads a request' do
43
+ request = Faraday::Request.create(:get) do |r|
44
+ r.path = '/auth'
45
+ r.body = '{1: 2}'
46
+ r.headers = { 'Content-Type' => 'application/xml' }
47
+ end.to_env(
48
+ Faraday::Connection.new('http://localhost')
47
49
  )
48
50
 
49
51
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
50
52
 
51
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
53
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
52
54
  expect(request[:request_headers]['Content-Digest']).to eq(Digest::MD5.hexdigest(request[:body]))
53
55
  expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
54
56
 
55
57
  yielded = false
56
58
 
57
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
59
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
58
60
  expect(key_id).to eq(key_id)
59
61
  yielded = true
60
62
  key_secret
61
- end).to be_truthy
63
+ end
62
64
 
63
65
  expect(yielded).to be_truthy
64
66
  end
65
67
 
66
- it "does not set Content-Digest if body is nil" do
67
- request = Faraday::Request.create(:get) { |r|
68
- r.path = "/auth"
68
+ it 'does not set Content-Digest if body is nil' do
69
+ request = Faraday::Request.create(:get) do |r|
70
+ r.path = '/auth'
69
71
  r.body = nil
70
- r.headers = {"Content-Type" => "application/xml"}
71
- }.to_env(
72
- Faraday::Connection.new("http://localhost")
72
+ r.headers = { 'Content-Type' => 'application/xml' }
73
+ end.to_env(
74
+ Faraday::Connection.new('http://localhost')
73
75
  )
74
76
 
75
77
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
76
78
 
77
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
79
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
78
80
  expect(request[:request_headers]).not_to have_key('Content-Digest')
79
81
  expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
80
82
 
81
83
  yielded = false
82
84
 
83
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
85
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
84
86
  expect(key_id).to eq(key_id)
85
87
  yielded = true
86
88
  key_secret
87
- end).to be_truthy
89
+ end
88
90
 
89
91
  expect(yielded).to be_truthy
90
92
  end
91
93
 
92
- it "does not set Content-Digest if body is empty" do
94
+ it 'does not set Content-Digest if body is empty' do
93
95
  request = Faraday::Request.create(:get) do |r|
94
- r.path = "/auth"
95
- r.body = ""
96
- r.headers = {"Content-Type" => "application/xml"}
97
- end.to_env(Faraday::Connection.new("http://localhost"))
96
+ r.path = '/auth'
97
+ r.body = ''
98
+ r.headers = { 'Content-Type' => 'application/xml' }
99
+ end.to_env(Faraday::Connection.new('http://localhost'))
98
100
 
99
101
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
100
102
 
101
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
103
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
102
104
  expect(request[:request_headers]).not_to have_key('Content-Digest')
103
- #expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
105
+ # expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
104
106
 
105
107
  yielded = false
106
108
 
107
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
109
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
108
110
  expect(key_id).to eq(key_id)
109
111
  yielded = true
110
112
  key_secret
111
- end).to be_truthy
113
+ end
112
114
 
113
115
  expect(yielded).to be_truthy
114
116
  end
115
117
 
116
- context "with a request" do
118
+ context 'with a request' do
117
119
  let!(:request) do
118
120
  Faraday::Request.create(:get) do |r|
119
- r.path = "/auth"
120
- r.body = "{1: 2}"
121
- r.headers = {"Content-Type" => "application/xml"}
122
- end.to_env(Faraday::Connection.new("http://localhost"))
121
+ r.path = '/auth'
122
+ r.body = '{1: 2}'
123
+ r.headers = { 'Content-Type' => 'application/xml' }
124
+ end.to_env(Faraday::Connection.new('http://localhost'))
123
125
  end
124
- include_examples "authentication"
126
+
127
+ include_examples 'authentication'
125
128
  end
126
129
  end
127
130
 
128
- describe "middleware" do
129
- it "accepts a SHA1 signature" do
131
+ describe 'middleware' do
132
+ it 'accepts a SHA1 signature' do
130
133
  require 'ey-hmac/faraday'
131
134
  Bundler.require(:rack)
132
135
 
133
136
  app = lambda do |env|
134
- authenticated = Ey::Hmac.authenticated?(env, accept_digests: :sha1, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
137
+ authenticated = Ey::Hmac.authenticated?(env, accept_digests: :sha1,
138
+ adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
135
139
  (auth_id == key_id) && key_secret
136
140
  end
137
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
141
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
138
142
  end
139
143
 
140
144
  connection = Faraday.new do |c|
@@ -142,10 +146,10 @@ describe "faraday" do
142
146
  c.adapter(:rack, app)
143
147
  end
144
148
 
145
- expect(connection.get("/resources").status).to eq(200)
149
+ expect(connection.get('/resources').status).to eq(200)
146
150
  end
147
151
 
148
- it "accepts a SHA256 signature" do # default
152
+ it 'accepts a SHA256 signature' do # default
149
153
  require 'ey-hmac/faraday'
150
154
  Bundler.require(:rack)
151
155
 
@@ -153,7 +157,7 @@ describe "faraday" do
153
157
  authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
154
158
  (auth_id == key_id) && key_secret
155
159
  end
156
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
160
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
157
161
  end
158
162
 
159
163
  connection = Faraday.new do |c|
@@ -161,18 +165,19 @@ describe "faraday" do
161
165
  c.adapter(:rack, app)
162
166
  end
163
167
 
164
- expect(connection.get("/resources").status).to eq(200)
168
+ expect(connection.get('/resources').status).to eq(200)
165
169
  end
166
170
 
167
- it "accepts multiple digest signatures" do # default
171
+ it 'accepts multiple digest signatures' do # default
168
172
  require 'ey-hmac/faraday'
169
173
  Bundler.require(:rack)
170
174
 
171
175
  app = lambda do |env|
172
- authenticated = Ey::Hmac.authenticated?(env, accept_digests: [:sha1, :sha256], adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
176
+ authenticated = Ey::Hmac.authenticated?(env, accept_digests: %i[sha1 sha256],
177
+ adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
173
178
  (auth_id == key_id) && key_secret
174
179
  end
175
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
180
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
176
181
  end
177
182
 
178
183
  connection = Faraday.new do |c|
@@ -180,23 +185,24 @@ describe "faraday" do
180
185
  c.adapter(:rack, app)
181
186
  end
182
187
 
183
- expect(connection.get("/resources").status).to eq(200)
188
+ expect(connection.get('/resources').status).to eq(200)
184
189
  end
185
190
 
186
- it "signs empty request" do
191
+ it 'signs empty request' do
187
192
  require 'ey-hmac/faraday'
188
193
  Bundler.require(:rack)
189
194
 
190
- _key_id, _key_secret = key_id, key_secret
195
+ outer_key_id = key_id
196
+ outer_key_secret = key_secret
191
197
  app = Rack::Builder.new do
192
198
  use Rack::Config do |env|
193
- env["CONTENT_TYPE"] ||= "text/html"
199
+ env['CONTENT_TYPE'] ||= 'text/html'
194
200
  end
195
- run(lambda {|env|
201
+ run(lambda { |env|
196
202
  authenticated = Ey::Hmac.authenticate!(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
197
- (auth_id == _key_id) && _key_secret
203
+ (auth_id == outer_key_id) && outer_key_secret
198
204
  end
199
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
205
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
200
206
  })
201
207
  end
202
208
 
@@ -206,10 +212,10 @@ describe "faraday" do
206
212
  end
207
213
 
208
214
  expect(connection.get do |req|
209
- req.path = "/resource"
215
+ req.path = '/resource'
210
216
  req.body = nil
211
- req.params = {"a" => "1"}
212
- req.headers = {"Content-Type" => "application/x-www-form-urlencoded"}
217
+ req.params = { 'a' => '1' }
218
+ req.headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
213
219
  end.status).to eq(200)
214
220
  end
215
221
  end