ey-hmac 0.0.5 → 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d66a44d088fc9384dc6e9058dad00bb4264dcbb6
4
+ data.tar.gz: 0cc4a68974d4f73b2f3081c2d58e59d235456445
5
+ SHA512:
6
+ metadata.gz: c3dcc933b1577141c16c55e861fc09bedba9ed6558b517a91e2d75f9a8882b3473b45222473029d4931aa2948e96b11d7396ee2e078adf108f6f4957b3952643
7
+ data.tar.gz: 2aa4b84886e85a7be13db759a22aa337fa566b0ca1cfd9a5f1372872b583e91d15b89179cd3b7ac2dd9779ec0e74ec29b676b17a5e1c01d40613cd66bc14eea8
@@ -6,7 +6,7 @@ class Ey::Hmac::Adapter
6
6
  autoload :Rack, "ey-hmac/adapter/rack"
7
7
  autoload :Faraday, "ey-hmac/adapter/faraday"
8
8
 
9
- attr_reader :request, :options, :authorization_header, :service
9
+ attr_reader :request, :options, :authorization_header, :service, :sign_with, :accept_digests
10
10
 
11
11
  # @param [Object] request signer-specific request implementation
12
12
  # @option options [Integer] :version signature version
@@ -16,7 +16,9 @@ class Ey::Hmac::Adapter
16
16
  @request, @options = request, options
17
17
 
18
18
  @authorization_header = options[:authorization_header] || 'Authorization'
19
- @service = options[:service] || 'EyHmac'
19
+ @service = options[:service] || 'EyHmac'
20
+ @sign_with = options[:sign_with] || :sha256
21
+ @accept_digests = Array(options[:accept_digests] || :sha256)
20
22
  end
21
23
 
22
24
  # In order for the server to correctly authorize the request, the client and server MUST AGREE on this format
@@ -28,17 +30,17 @@ class Ey::Hmac::Adapter
28
30
  end
29
31
 
30
32
  # @param [String] key_secret private HMAC key
33
+ # @param [String] signature digest hash function. Defaults to #sign_with
31
34
  # @return [String] HMAC signature of {#request}
32
- # @todo handle multiple hash functions
33
- def signature(key_secret)
34
- Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), key_secret, canonicalize)).strip
35
+ def signature(key_secret, digest = self.sign_with)
36
+ Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new(digest.to_s), key_secret, canonicalize)).strip
35
37
  end
36
38
 
37
39
  # @param [String] key_id public HMAC key
38
40
  # @param [String] key_secret private HMAC key
39
41
  # @return [String] HMAC header value of {#request}
40
42
  def authorization(key_id, key_secret)
41
- "#{service} #{key_id}:#{signature(key_secret)}"
43
+ "#{service} #{key_id}:#{signature(key_secret, sign_with)}"
42
44
  end
43
45
 
44
46
  # @abstract
@@ -55,7 +57,6 @@ class Ey::Hmac::Adapter
55
57
 
56
58
  # @abstract
57
59
  # Digest of body. Default is MD5.
58
- # @todo support explicit digest methods
59
60
  # @return [String] digest of body
60
61
  def content_digest
61
62
  raise NotImplementedError
@@ -99,7 +100,7 @@ class Ey::Hmac::Adapter
99
100
  # @yieldparam key_id [String] public HMAC key
100
101
  # @return [Boolean] true if block yields matching private key and signature matches, else false
101
102
  # @see #authenticated!
102
- def authenticated?(&block)
103
+ def authenticated?(options={}, &block)
103
104
  authenticated!(&block)
104
105
  rescue Ey::Hmac::Error
105
106
  false
@@ -107,20 +108,22 @@ class Ey::Hmac::Adapter
107
108
 
108
109
  # @see Ey::Hmac#authenticate!
109
110
  def authenticated!(&block)
110
- if authorization_match = AUTHORIZATION_REGEXP.match(authorization_signature)
111
- key_id = authorization_match[1]
112
- signature_value = authorization_match[2]
113
-
114
- if key_secret = block.call(key_id)
115
- calculated_signature = signature(key_secret)
116
- if secure_compare(signature_value, calculated_signature)
117
- else raise(Ey::Hmac::SignatureMismatch, "Calculated siganature #{signature_value} does not match #{calculated_signature} using #{canonicalize.inspect}")
118
- end
119
- else raise(Ey::Hmac::MissingSecret, "Failed to find secret matching #{key_id.inspect}")
120
- end
121
- else
111
+ unless authorization_match = AUTHORIZATION_REGEXP.match(authorization_signature)
122
112
  raise(Ey::Hmac::MissingAuthorization, "Failed to parse authorization_signature #{authorization_signature}")
123
113
  end
114
+
115
+ key_id = authorization_match[1]
116
+ signature_value = authorization_match[2]
117
+
118
+ unless key_secret = block.call(key_id)
119
+ raise(Ey::Hmac::MissingSecret, "Failed to find secret matching #{key_id.inspect}")
120
+ end
121
+
122
+ calculated_signatures = self.accept_digests.map { |ad| signature(key_secret, ad) }
123
+
124
+ unless calculated_signatures.any? { |cs| secure_compare(signature_value, cs) }
125
+ raise(Ey::Hmac::SignatureMismatch, "Calculated siganature #{signature_value} does not match #{calculated_signatures.inspect} using #{canonicalize.inspect}")
126
+ end
124
127
  true
125
128
  end
126
129
  alias authenticate! authenticated!
@@ -4,11 +4,11 @@ class Ey::Hmac::Adapter::Faraday < Ey::Hmac::Adapter
4
4
  end
5
5
 
6
6
  def content_type
7
- %w[CONTENT-TYPE CONTENT_TYPE Content-Type Content_Type].inject(nil){|r, h| r || request[:request_headers][h]}
7
+ %w[CONTENT-TYPE CONTENT_TYPE Content-Type Content_Type].inject(nil) { |r,h| r || request[:request_headers][h] }
8
8
  end
9
9
 
10
10
  def content_digest
11
- if existing = %w[CONTENT-DIGEST CONTENT_DIGEST Content-Digest Content_Digest].inject(nil){|r,h| r || request[:request_headers][h]}
11
+ if existing = %w[CONTENT-DIGEST CONTENT_DIGEST Content-Digest Content_Digest].inject(nil) { |r,h| r || request[:request_headers][h] }
12
12
  existing
13
13
  elsif digestable = body && Digest::MD5.hexdigest(body)
14
14
  request[:request_headers]['Content-Digest'] = digestable
@@ -24,7 +24,7 @@ class Ey::Hmac::Adapter::Faraday < Ey::Hmac::Adapter
24
24
  end
25
25
 
26
26
  def date
27
- existing = %w[DATE Date].inject(nil){|r,h| r || request[h]}
27
+ existing = %w[DATE Date].inject(nil) { |r,h| r || request[h] }
28
28
  existing || (request[:request_headers]['Date'] = Time.now.httpdate)
29
29
  end
30
30
 
@@ -24,7 +24,7 @@ class Ey::Hmac::Adapter::Rack < Ey::Hmac::Adapter
24
24
  request.env['HTTP_CONTENT_DIGEST'] = digest
25
25
  end
26
26
  end
27
-
27
+
28
28
  def body
29
29
  if request.env["rack.input"]
30
30
  request.env["rack.input"].rewind
@@ -9,7 +9,8 @@ class Ey::Hmac::Rack
9
9
  end
10
10
 
11
11
  def call(env)
12
- Ey::Hmac.sign!(env, key_id, key_secret, {adapter: Ey::Hmac::Adapter::Rack}.merge(options))
12
+ Ey::Hmac.sign!(env, key_id, key_secret, { adapter: Ey::Hmac::Adapter::Rack }.merge(options))
13
+
13
14
  @app.call(env)
14
15
  end
15
16
  end
@@ -1,5 +1,5 @@
1
1
  module Ey
2
2
  module Hmac
3
- VERSION = "0.0.5"
3
+ VERSION = "0.1.1"
4
4
  end
5
5
  end
@@ -97,7 +97,27 @@ describe "faraday" do
97
97
  end
98
98
 
99
99
  describe "middleware" do
100
- it "should sign request" do
100
+ it "should accept a SHA1 signature" do
101
+ require 'ey-hmac/faraday'
102
+ Bundler.require(:rack)
103
+
104
+ app = lambda do |env|
105
+ authenticated = Ey::Hmac.authenticated?(env, accept_digests: :sha1, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
106
+ (auth_id == key_id) && key_secret
107
+ end
108
+ [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
109
+ end
110
+
111
+ request_env = nil
112
+ connection = Faraday.new do |c|
113
+ c.request :hmac, key_id, key_secret, sign_with: :sha1
114
+ c.adapter(:rack, app)
115
+ end
116
+
117
+ connection.get("/resources").status.should == 200
118
+ end
119
+
120
+ it "should accept a SHA256 signature" do # default
101
121
  require 'ey-hmac/faraday'
102
122
  Bundler.require(:rack)
103
123
 
@@ -117,6 +137,26 @@ describe "faraday" do
117
137
  connection.get("/resources").status.should == 200
118
138
  end
119
139
 
140
+ it "should accept multiple digest signatures" do # default
141
+ require 'ey-hmac/faraday'
142
+ Bundler.require(:rack)
143
+
144
+ app = lambda do |env|
145
+ authenticated = Ey::Hmac.authenticated?(env, accept_digests: [:sha1, :sha256], adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
146
+ (auth_id == key_id) && key_secret
147
+ end
148
+ [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
149
+ end
150
+
151
+ request_env = nil
152
+ connection = Faraday.new do |c|
153
+ c.request :hmac, key_id, key_secret
154
+ c.adapter(:rack, app)
155
+ end
156
+
157
+ connection.get("/resources").status.should == 200
158
+ end
159
+
120
160
  it "should sign empty request" do
121
161
  require 'ey-hmac/faraday'
122
162
  Bundler.require(:rack)
@@ -3,8 +3,8 @@ require 'spec_helper'
3
3
  describe "rack" do
4
4
  before(:all) { Bundler.require(:rack) }
5
5
 
6
- let!(:key_id) { (0...8).map{ 65.+(rand(26)).chr}.join }
7
- let!(:key_secret) { (0...16).map{ 65.+(rand(26)).chr}.join }
6
+ let!(:key_id) { SecureRandom.hex(8) }
7
+ let!(:key_secret) { SecureRandom.hex(16) }
8
8
 
9
9
  describe "adapter" do
10
10
  let(:adapter) { Ey::Hmac::Adapter::Rack }
@@ -89,7 +89,24 @@ describe "rack" do
89
89
  end
90
90
 
91
91
  describe "middleware" do
92
- it "should sign and read request" do
92
+ it "should accept a SHA1 signature" do
93
+ app = lambda do |env|
94
+ authenticated = Ey::Hmac.authenticated?(env, accept_digests: [:sha1, :sha256], adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
95
+ (auth_id == key_id) && key_secret
96
+ end
97
+ [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
98
+ end
99
+
100
+ _key_id, _key_secret = key_id, key_secret
101
+ client = Rack::Client.new do
102
+ use Ey::Hmac::Rack, _key_id, _key_secret, sign_with: :sha1
103
+ run app
104
+ end
105
+
106
+ client.get("/resource").status.should == 200
107
+ end
108
+
109
+ it "should accept a SHA256 signature" do # default
93
110
  app = lambda do |env|
94
111
  authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
95
112
  (auth_id == key_id) && key_secret
@@ -105,5 +122,25 @@ describe "rack" do
105
122
 
106
123
  client.get("/resource").status.should == 200
107
124
  end
125
+
126
+ it "should accept multiple digest signatures" do # default
127
+ require 'ey-hmac/faraday'
128
+ Bundler.require(:rack)
129
+
130
+ app = lambda do |env|
131
+ authenticated = Ey::Hmac.authenticated?(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
132
+ (auth_id == key_id) && key_secret
133
+ end
134
+ [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
135
+ end
136
+
137
+ request_env = nil
138
+ connection = Faraday.new do |c|
139
+ c.request :hmac, key_id, key_secret, digest: [:sha1, :sha256]
140
+ c.adapter(:rack, app)
141
+ end
142
+
143
+ connection.get("/resources").status.should == 200
144
+ end
108
145
  end
109
146
  end
metadata CHANGED
@@ -1,36 +1,32 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ey-hmac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
5
- prerelease:
4
+ version: 0.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - Josh Lane & Jason Hansen
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-10-18 00:00:00.000000000 Z
11
+ date: 2014-02-19 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: bundler
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ~>
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ~>
44
39
  - !ruby/object:Gem::Version
@@ -71,31 +66,29 @@ files:
71
66
  - spec/spec_helper.rb
72
67
  homepage: ''
73
68
  licenses: []
69
+ metadata: {}
74
70
  post_install_message:
75
71
  rdoc_options: []
76
72
  require_paths:
77
73
  - lib
78
74
  required_ruby_version: !ruby/object:Gem::Requirement
79
- none: false
80
75
  requirements:
81
- - - ! '>='
76
+ - - '>='
82
77
  - !ruby/object:Gem::Version
83
78
  version: '0'
84
79
  required_rubygems_version: !ruby/object:Gem::Requirement
85
- none: false
86
80
  requirements:
87
- - - ! '>='
81
+ - - '>='
88
82
  - !ruby/object:Gem::Version
89
83
  version: '0'
90
84
  requirements: []
91
85
  rubyforge_project:
92
- rubygems_version: 1.8.25
86
+ rubygems_version: 2.0.3
93
87
  signing_key:
94
- specification_version: 3
88
+ specification_version: 4
95
89
  summary: Lightweight HMAC signing libraries and middleware for Farday and Rack
96
90
  test_files:
97
91
  - spec/faraday_spec.rb
98
92
  - spec/rack_spec.rb
99
93
  - spec/shared/authenticated.rb
100
94
  - spec/spec_helper.rb
101
- has_rdoc: