ey-hmac 0.0.2 → 0.0.3

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