ey-hmac 2.0.2 → 2.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,21 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ey-hmac'
2
4
  require 'faraday'
3
5
 
4
- class Ey::Hmac::Faraday < Faraday::Response::Middleware
5
- dependency("ey-hmac")
6
+ class Ey::Hmac::Faraday < Faraday::Middleware
7
+ dependency('ey-hmac') if respond_to?(:dependency)
6
8
 
7
9
  attr_reader :key_id, :key_secret, :options
8
10
 
9
11
  def initialize(app, key_id, key_secret, options = {})
10
12
  super(app)
11
- @key_id, @key_secret = key_id, key_secret
13
+ @key_id = key_id
14
+ @key_secret = key_secret
12
15
  @options = options
13
16
  end
14
17
 
15
18
  def call(env)
16
- Ey::Hmac.sign!(env, key_id, key_secret, {adapter: Ey::Hmac::Adapter::Faraday}.merge(options))
19
+ Ey::Hmac.sign!(env, key_id, key_secret, { adapter: Ey::Hmac::Adapter::Faraday }.merge(options))
17
20
  @app.call(env)
18
21
  end
19
22
  end
20
23
 
21
- Faraday::Middleware.register_middleware :hmac => Ey::Hmac::Faraday
24
+ Faraday::Middleware.register_middleware hmac: Ey::Hmac::Faraday
data/lib/ey-hmac/rack.rb CHANGED
@@ -1,10 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Request middleware that performs HMAC request signing
2
4
  class Ey::Hmac::Rack
3
5
  attr_reader :key_id, :key_secret, :options
4
6
 
5
7
  def initialize(app, key_id, key_secret, options = {})
6
8
  @app = app
7
- @key_id, @key_secret = key_id, key_secret
9
+ @key_id = key_id
10
+ @key_secret = key_secret
8
11
  @options = options
9
12
  end
10
13
 
@@ -1,5 +1,7 @@
1
- module Ey
1
+ # frozen_string_literal: true
2
+
3
+ module Ey # rubocop:disable Style/ClassAndModuleChildren
2
4
  module Hmac
3
- VERSION = "2.0.2"
5
+ VERSION = '2.3.1'
4
6
  end
5
7
  end
data/lib/ey-hmac.rb CHANGED
@@ -1,102 +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
-
16
- autoload :Adapter, "ey-hmac/adapter"
17
- autoload :Faraday, "ey-hmac/faraday"
18
- autoload :Rack, "ey-hmac/rack"
19
-
20
- def self.default_adapter=(default_adapter)
21
- @default_adapter = default_adapter
22
- end
23
-
24
- def self.default_adapter
25
- @default_adapter ||= begin
26
- if defined?(Rack) || defined?(Rails)
27
- Ey::Hmac::Adapter::Rack
28
- elsif defined?(Faraday)
29
- Ey::Hmac::Adapter::Rails
30
- end
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 match {Ey::Hmac::Adapter#signature}
91
- # @raise [MissingSecret] if the block does not return a private key matching +key_id+
92
- # @raise [MissingAuthorization] if the value of {Ey::Hmac::Adapter#authorization_signature} is nil
93
- # @return [TrueClass] if authentication was successful
94
- def self.authenticate!(request, options={}, &block)
95
- adapter = options[:adapter] || Ey::Hmac.default_adapter
96
-
97
- raise ArgumentError, "Missing adapter and Ey::Hmac.default_adapter" unless adapter
98
-
99
- adapter.new(request, options).authenticate!(&block)
100
- 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)
101
101
  end
102
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,117 +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.new.tap { |r|
42
- r.method = :get
43
- r.path = "/auth"
44
- r.body = "{1: 2}"
45
- r.headers = {"Content-Type" => "application/xml"}
46
- }.to_env(
47
- 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')
48
49
  )
49
50
 
50
51
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
51
52
 
52
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
53
- expect(request[:request_headers]['Content-Digest']).to eq(Digest::MD5.hexdigest(request[:body]))
53
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
54
+ expect(request[:request_headers]['Content-Digest']).to eq(Digest::MD5.hexdigest(request[:body]))
54
55
  expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
55
56
 
56
57
  yielded = false
57
58
 
58
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
59
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
59
60
  expect(key_id).to eq(key_id)
60
61
  yielded = true
61
62
  key_secret
62
- end).to be_truthy
63
+ end
63
64
 
64
65
  expect(yielded).to be_truthy
65
66
  end
66
67
 
67
- it "does not set Content-Digest if body is nil" do
68
- request = Faraday::Request.new.tap { |r|
69
- r.method = :get
70
- 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'
71
71
  r.body = nil
72
- r.headers = {"Content-Type" => "application/xml"}
73
- }.to_env(
74
- Faraday::Connection.new("http://localhost")
72
+ r.headers = { 'Content-Type' => 'application/xml' }
73
+ end.to_env(
74
+ Faraday::Connection.new('http://localhost')
75
75
  )
76
76
 
77
77
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
78
78
 
79
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
79
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
80
80
  expect(request[:request_headers]).not_to have_key('Content-Digest')
81
81
  expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
82
82
 
83
83
  yielded = false
84
84
 
85
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
85
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
86
86
  expect(key_id).to eq(key_id)
87
87
  yielded = true
88
88
  key_secret
89
- end).to be_truthy
89
+ end
90
90
 
91
91
  expect(yielded).to be_truthy
92
92
  end
93
93
 
94
- it "does not set Content-Digest if body is empty" do
95
- request = Faraday::Request.new.tap do |r|
96
- r.method = :get
97
- r.path = "/auth"
98
- r.body = ""
99
- r.headers = {"Content-Type" => "application/xml"}
100
- end.to_env(Faraday::Connection.new("http://localhost"))
94
+ it 'does not set Content-Digest if body is empty' do
95
+ request = Faraday::Request.create(:get) do |r|
96
+ r.path = '/auth'
97
+ r.body = ''
98
+ r.headers = { 'Content-Type' => 'application/xml' }
99
+ end.to_env(Faraday::Connection.new('http://localhost'))
101
100
 
102
101
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
103
102
 
104
- expect(request[:request_headers]['Authorization']).to start_with("EyHmac")
103
+ expect(request[:request_headers]['Authorization']).to start_with('EyHmac')
105
104
  expect(request[:request_headers]).not_to have_key('Content-Digest')
106
- #expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
105
+ # expect(Time.parse(request[:request_headers]['Date'])).not_to be_nil
107
106
 
108
107
  yielded = false
109
108
 
110
- expect(Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
109
+ expect(Ey::Hmac).to be_authenticated(request, adapter: adapter) do |key_id|
111
110
  expect(key_id).to eq(key_id)
112
111
  yielded = true
113
112
  key_secret
114
- end).to be_truthy
113
+ end
115
114
 
116
115
  expect(yielded).to be_truthy
117
116
  end
118
117
 
119
- context "with a request" do
118
+ context 'with a request' do
120
119
  let!(:request) do
121
- Faraday::Request.new.tap do |r|
122
- r.method = :get
123
- r.path = "/auth"
124
- r.body = "{1: 2}"
125
- r.headers = {"Content-Type" => "application/xml"}
126
- end.to_env(Faraday::Connection.new("http://localhost"))
120
+ Faraday::Request.create(:get) do |r|
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'))
127
125
  end
128
- include_examples "authentication"
126
+
127
+ include_examples 'authentication'
129
128
  end
130
129
  end
131
130
 
132
- describe "middleware" do
133
- it "accepts a SHA1 signature" do
131
+ describe 'middleware' do
132
+ it 'accepts a SHA1 signature' do
134
133
  require 'ey-hmac/faraday'
135
134
  Bundler.require(:rack)
136
135
 
137
136
  app = lambda do |env|
138
- 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|
139
139
  (auth_id == key_id) && key_secret
140
140
  end
141
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
141
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
142
142
  end
143
143
 
144
144
  connection = Faraday.new do |c|
@@ -146,10 +146,10 @@ describe "faraday" do
146
146
  c.adapter(:rack, app)
147
147
  end
148
148
 
149
- expect(connection.get("/resources").status).to eq(200)
149
+ expect(connection.get('/resources').status).to eq(200)
150
150
  end
151
151
 
152
- it "accepts a SHA256 signature" do # default
152
+ it 'accepts a SHA256 signature' do # default
153
153
  require 'ey-hmac/faraday'
154
154
  Bundler.require(:rack)
155
155
 
@@ -157,7 +157,7 @@ describe "faraday" do
157
157
  authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
158
158
  (auth_id == key_id) && key_secret
159
159
  end
160
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
160
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
161
161
  end
162
162
 
163
163
  connection = Faraday.new do |c|
@@ -165,18 +165,19 @@ describe "faraday" do
165
165
  c.adapter(:rack, app)
166
166
  end
167
167
 
168
- expect(connection.get("/resources").status).to eq(200)
168
+ expect(connection.get('/resources').status).to eq(200)
169
169
  end
170
170
 
171
- it "accepts multiple digest signatures" do # default
171
+ it 'accepts multiple digest signatures' do # default
172
172
  require 'ey-hmac/faraday'
173
173
  Bundler.require(:rack)
174
174
 
175
175
  app = lambda do |env|
176
- 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|
177
178
  (auth_id == key_id) && key_secret
178
179
  end
179
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
180
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
180
181
  end
181
182
 
182
183
  connection = Faraday.new do |c|
@@ -184,23 +185,24 @@ describe "faraday" do
184
185
  c.adapter(:rack, app)
185
186
  end
186
187
 
187
- expect(connection.get("/resources").status).to eq(200)
188
+ expect(connection.get('/resources').status).to eq(200)
188
189
  end
189
190
 
190
- it "signs empty request" do
191
+ it 'signs empty request' do
191
192
  require 'ey-hmac/faraday'
192
193
  Bundler.require(:rack)
193
194
 
194
- _key_id, _key_secret = key_id, key_secret
195
+ outer_key_id = key_id
196
+ outer_key_secret = key_secret
195
197
  app = Rack::Builder.new do
196
198
  use Rack::Config do |env|
197
- env["CONTENT_TYPE"] ||= "text/html"
199
+ env['CONTENT_TYPE'] ||= 'text/html'
198
200
  end
199
- run(lambda {|env|
201
+ run(lambda { |env|
200
202
  authenticated = Ey::Hmac.authenticate!(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
201
- (auth_id == _key_id) && _key_secret
203
+ (auth_id == outer_key_id) && outer_key_secret
202
204
  end
203
- [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
205
+ [(authenticated ? 200 : 401), { 'Content-Type' => 'text/plain' }, []]
204
206
  })
205
207
  end
206
208
 
@@ -210,10 +212,10 @@ describe "faraday" do
210
212
  end
211
213
 
212
214
  expect(connection.get do |req|
213
- req.path = "/resource"
215
+ req.path = '/resource'
214
216
  req.body = nil
215
- req.params = {"a" => "1"}
216
- req.headers = {"Content-Type" => "application/x-www-form-urlencoded"}
217
+ req.params = { 'a' => '1' }
218
+ req.headers = { 'Content-Type' => 'application/x-www-form-urlencoded' }
217
219
  end.status).to eq(200)
218
220
  end
219
221
  end