ey-hmac 0.0.5 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: