ey-hmac 0.0.2 → 0.0.3

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.
@@ -4,14 +4,18 @@ 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[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
- existing = %w[CONTENT-DIGEST CONTENT_DIGEST Content-Digest Content_Digest].inject(nil){|r,h| r || request[:request_headers][h]}
12
- existing || (request[:request_headers]['Content-Digest'] = (body && Digest::MD5.hexdigest(body)))
11
+ if existing = %w[CONTENT-DIGEST CONTENT_DIGEST Content-Digest Content_Digest].inject(nil){|r,h| r || request[:request_headers][h]}
12
+ existing
13
+ elsif digestable = body && Digest::MD5.hexdigest(body)
14
+ request[:request_headers]['Content-Digest'] = digestable
15
+ else nil
16
+ end
13
17
  end
14
-
18
+
15
19
  def body
16
20
  if request[:body] && request[:body].to_s != ""
17
21
  request[:body]
@@ -15,7 +15,11 @@ class Ey::Hmac::Adapter::Rack < Ey::Hmac::Adapter
15
15
  end
16
16
 
17
17
  def content_digest
18
- request.env['HTTP_CONTENT_DIGEST'] ||= body && Digest::MD5.hexdigest(body)
18
+ if existing = request.env['HTTP_CONTENT_DIGEST']
19
+ existing
20
+ elsif digest = body && Digest::MD5.hexdigest(body)
21
+ request.env['HTTP_CONTENT_DIGEST'] = digest
22
+ end
19
23
  end
20
24
 
21
25
  def body
@@ -76,7 +76,7 @@ class Ey::Hmac::Adapter
76
76
 
77
77
  # @abstract
78
78
  # @return [String] value of the Date header in {#request}.
79
- # @see {Time#http_date}
79
+ # @see Time#http_date
80
80
  def date
81
81
  raise NotImplementedError
82
82
  end
@@ -89,6 +89,8 @@ class Ey::Hmac::Adapter
89
89
 
90
90
  # @abstract
91
91
  # Add {#signature} header to request. Typically this is 'Authorization' or 'WWW-Authorization'
92
+ # @return [String] calculated {#authorization}
93
+ # @see Ey::Hmac#sign!
92
94
  def sign!(key_id, key_secret)
93
95
  raise NotImplementedError
94
96
  end
@@ -96,17 +98,14 @@ class Ey::Hmac::Adapter
96
98
  # Check {#authorization_signature} against calculated {#signature}
97
99
  # @yieldparam key_id [String] public HMAC key
98
100
  # @return [Boolean] true if block yields matching private key and signature matches, else false
99
- # @see {#authenticated!}
101
+ # @see #authenticated!
100
102
  def authenticated?(&block)
101
103
  authenticated!(&block)
102
104
  rescue Ey::Hmac::Error
103
105
  false
104
106
  end
105
107
 
106
- # Check {#authorization_signature} against calculated {#signature}
107
- # @yieldparam key_id [String] public HMAC key
108
- # @return [Boolean] true if block yields matching private key
109
- # @raise [Ey::Hmac::Error] if authentication fails
108
+ # @see Ey::Hmac#authenticate!
110
109
  def authenticated!(&block)
111
110
  if authorization_match = AUTHORIZATION_REGEXP.match(authorization_signature)
112
111
  key_id = authorization_match[1]
@@ -1,5 +1,5 @@
1
1
  module Ey
2
2
  module Hmac
3
- VERSION = "0.0.2"
3
+ VERSION = "0.0.3"
4
4
  end
5
5
  end
data/lib/ey-hmac.rb CHANGED
@@ -30,15 +30,19 @@ module Ey
30
30
  end
31
31
  end
32
32
 
33
+ # Signs request by calculating signature and adding it to the specified header
33
34
  # @example
34
35
  # Ey::Hmac.sign!(env, @key_id, @key_secret)
35
36
  #
37
+ # @see Ey::Hmac::Adapter#sign!
38
+ #
36
39
  # @param request [Hash] request environment
37
40
  # @option options [Ey::Hmac::Adapter] :adapter (#{default_adapter}) adapter to sign request with
38
41
  # @option options [Integer] :version (nil) signature version
39
42
  # @option options [String] :authorization_header ('Authorization') Authorization header key.
40
- # @option options [String] :server ('EyHmac') service name prefixed to {#authorization}
41
- # @see {Ey::Hmac::Adapter#sign!}
43
+ # @option options [String] :service ('EyHmac') service name prefixed to {Ey::Hmac::Adapter#authorization}
44
+ #
45
+ # @return [String] authorization signature
42
46
  def self.sign!(request, key_id, key_secret, options={})
43
47
  adapter = options[:adapter] || Ey::Hmac.default_adapter
44
48
 
@@ -52,9 +56,15 @@ module Ey
52
56
  # @consumer = Consumer.where(auth_id: key_id).first
53
57
  # @consumer && @consumer.auth_key
54
58
  # end
59
+ #
60
+ # @see Ey::Hmac::Adapter#authenticated?
61
+ # @see Ey::Hmac#authenticate!
62
+ #
55
63
  # @param request [Hash] request environment
56
64
  # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
57
- # @see {Ey::Hmac::Adapter#authenticated?}
65
+ # @yieldparam key_id [String] public HMAC key
66
+ #
67
+ # @return [Boolean] success of authentication
58
68
  def self.authenticated?(request, options={}, &block)
59
69
  adapter = options[:adapter] || Ey::Hmac.default_adapter
60
70
 
@@ -63,14 +73,23 @@ module Ey
63
73
  adapter.new(request, options).authenticated?(&block)
64
74
  end
65
75
 
76
+ # Check {Ey::Hmac::Adapter#authorization_signature} against calculated {Ey::Hmac::Adapter#signature}
66
77
  # @example
67
78
  # Ey::Hmac.authenticate! do |key_id|
68
79
  # @consumer = Consumer.where(auth_id: key_id).first
69
80
  # @consumer && @consumer.auth_key
70
81
  # end
82
+ #
83
+ # @see Ey::Hmac::Adapter#authenticate!
84
+ #
71
85
  # @param request [Hash] request environment
86
+ # @yieldparam key_id [String] public HMAC key
72
87
  # @option options [Ey::Hmac::Adapter] :adapter ({#default_adapter}) adapter to verify request with
73
- # @see {Ey::Hmac::Adapter#authenticate!}
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
74
93
  def self.authenticate!(request, options={}, &block)
75
94
  adapter = options[:adapter] || Ey::Hmac.default_adapter
76
95
 
data/spec/faraday_spec.rb CHANGED
@@ -7,16 +7,15 @@ describe "faraday" do
7
7
 
8
8
  describe "adapter" do
9
9
  let!(:adapter) { Ey::Hmac::Adapter::Faraday }
10
- let!(:request) do
11
- Faraday::Request.new.tap do |r|
10
+
11
+ it "should sign and read request" do
12
+ request = Faraday::Request.new.tap do |r|
12
13
  r.method = :get
13
14
  r.path = "/auth"
14
15
  r.body = "{1: 2}"
15
16
  r.headers = {"Content-Type" => "application/xml"}
16
- end.to_env(Faraday::Connection.new("http://localhost"))
17
- end
17
+ end.to_env(Faraday::Connection.new("http://localhost"))
18
18
 
19
- it "should sign and read request" do
20
19
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
21
20
 
22
21
  request[:request_headers]['Authorization'].should start_with("EyHmac")
@@ -34,7 +33,67 @@ describe "faraday" do
34
33
  yielded.should be_true
35
34
  end
36
35
 
37
- include_examples "authentication"
36
+ it "should not set Content-Digest if body is nil" do
37
+ request = Faraday::Request.new.tap do |r|
38
+ r.method = :get
39
+ r.path = "/auth"
40
+ r.body = nil
41
+ r.headers = {"Content-Type" => "application/xml"}
42
+ end.to_env(Faraday::Connection.new("http://localhost"))
43
+
44
+ Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
45
+
46
+ request[:request_headers]['Authorization'].should start_with("EyHmac")
47
+ request[:request_headers].should_not have_key('Content-Digest')
48
+ Time.parse(request[:request_headers]['Date']).should_not be_nil
49
+
50
+ yielded = false
51
+
52
+ Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
53
+ key_id.should == key_id
54
+ yielded = true
55
+ key_secret
56
+ end.should be_true
57
+
58
+ yielded.should be_true
59
+ end
60
+
61
+ it "should not set Content-Digest if body is empty" do
62
+ request = Faraday::Request.new.tap do |r|
63
+ r.method = :get
64
+ r.path = "/auth"
65
+ r.body = ""
66
+ r.headers = {"Content-Type" => "application/xml"}
67
+ end.to_env(Faraday::Connection.new("http://localhost"))
68
+
69
+ Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
70
+
71
+ request[:request_headers]['Authorization'].should start_with("EyHmac")
72
+ request[:request_headers].should_not have_key('Content-Digest')
73
+ Time.parse(request[:request_headers]['Date']).should_not be_nil
74
+
75
+ yielded = false
76
+
77
+ Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
78
+ key_id.should == key_id
79
+ yielded = true
80
+ key_secret
81
+ end.should be_true
82
+
83
+ yielded.should be_true
84
+ end
85
+
86
+ context "with a request" do
87
+ let!(:request) do
88
+ Faraday::Request.new.tap do |r|
89
+ r.method = :get
90
+ r.path = "/auth"
91
+ r.body = "{1: 2}"
92
+ r.headers = {"Content-Type" => "application/xml"}
93
+ end.to_env(Faraday::Connection.new("http://localhost"))
94
+ end
95
+ include_examples "authentication"
96
+ end
38
97
  end
39
98
 
40
99
  describe "middleware" do
@@ -57,5 +116,36 @@ describe "faraday" do
57
116
 
58
117
  connection.get("/resources").status.should == 200
59
118
  end
119
+
120
+ it "should sign empty request" do
121
+ require 'ey-hmac/faraday'
122
+ Bundler.require(:rack)
123
+
124
+ _key_id, _key_secret = key_id, key_secret
125
+ app = Rack::Builder.new do
126
+ use Rack::Config do |env|
127
+ env["CONTENT_TYPE"] ||= "text/html"
128
+ end
129
+ run(lambda {|env|
130
+ authenticated = Ey::Hmac.authenticate!(env, adapter: Ey::Hmac::Adapter::Rack) do |auth_id|
131
+ (auth_id == _key_id) && _key_secret
132
+ end
133
+ [(authenticated ? 200 : 401), {"Content-Type" => "text/plain"}, []]
134
+ })
135
+ end
136
+
137
+ request_env = nil
138
+ connection = Faraday.new do |c|
139
+ c.request :hmac, key_id, key_secret
140
+ c.adapter(:rack, app)
141
+ end
142
+
143
+ connection.get do |req|
144
+ req.path = "/resource"
145
+ req.body = nil
146
+ req.params = {"a" => "1"}
147
+ req.headers = {"Content-Type" => "application/x-www-form-urlencoded"}
148
+ end.status.should == 200
149
+ end
60
150
  end
61
151
  end
data/spec/rack_spec.rb CHANGED
@@ -8,14 +8,12 @@ describe "rack" do
8
8
 
9
9
  describe "adapter" do
10
10
  let(:adapter) { Ey::Hmac::Adapter::Rack }
11
- let(:request) {
12
- Rack::Request.new({
13
- "rack.input" => StringIO.new("{1: 2}"),
14
- "HTTP_CONTENT_TYPE" => "application/json",
15
- })
16
- }
17
11
 
18
12
  it "should sign and read request" do
13
+ request = Rack::Request.new(
14
+ "rack.input" => StringIO.new("{1: 2}"),
15
+ "HTTP_CONTENT_TYPE" => "application/json",
16
+ )
19
17
  Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
20
18
 
21
19
  request.env['HTTP_AUTHORIZATION'].should start_with("EyHmac")
@@ -33,7 +31,61 @@ describe "rack" do
33
31
  yielded.should be_true
34
32
  end
35
33
 
36
- include_examples "authentication"
34
+ it "should not set Content-Digest if body is nil" do
35
+ request = Rack::Request.new(
36
+ "HTTP_CONTENT_TYPE" => "application/json",
37
+ )
38
+
39
+ Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
40
+
41
+ request.env['HTTP_AUTHORIZATION'].should start_with("EyHmac")
42
+ request.env.should_not have_key('HTTP_CONTENT_DIGEST')
43
+ Time.parse(request.env['HTTP_DATE']).should_not be_nil
44
+
45
+ yielded = false
46
+
47
+ Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
48
+ key_id.should == key_id
49
+ yielded = true
50
+ key_secret
51
+ end.should be_true
52
+
53
+ yielded.should be_true
54
+ end
55
+
56
+ it "should not set Content-Digest if body is empty" do
57
+ request = Rack::Request.new(
58
+ "rack.input" => StringIO.new(""),
59
+ "HTTP_CONTENT_TYPE" => "application/json",
60
+ )
61
+
62
+ Ey::Hmac.sign!(request, key_id, key_secret, adapter: adapter)
63
+
64
+ request.env['HTTP_AUTHORIZATION'].should start_with("EyHmac")
65
+ request.env.should_not have_key('HTTP_CONTENT_DIGEST')
66
+ Time.parse(request.env['HTTP_DATE']).should_not be_nil
67
+
68
+ yielded = false
69
+
70
+ Ey::Hmac.authenticated?(request, adapter: adapter) do |key_id|
71
+ key_id.should == key_id
72
+ yielded = true
73
+ key_secret
74
+ end.should be_true
75
+
76
+ yielded.should be_true
77
+ end
78
+
79
+ context "with a request" do
80
+ let(:request) {
81
+ Rack::Request.new(
82
+ "rack.input" => StringIO.new("{1: 2}"),
83
+ "HTTP_CONTENT_TYPE" => "application/json",
84
+ )
85
+ }
86
+
87
+ include_examples "authentication"
88
+ end
37
89
  end
38
90
 
39
91
  describe "middleware" do
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ey-hmac
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
5
4
  prerelease:
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Josh Lane & Jason Hansen
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-05 00:00:00.000000000 Z
12
+ date: 2013-02-06 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Lightweight HMAC signing libraries and middleware for Farday and Rack
15
15
  email: