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.
- data/lib/ey-hmac/adapter/faraday.rb +8 -4
- data/lib/ey-hmac/adapter/rack.rb +5 -1
- data/lib/ey-hmac/adapter.rb +5 -6
- data/lib/ey-hmac/version.rb +1 -1
- data/lib/ey-hmac.rb +23 -4
- data/spec/faraday_spec.rb +96 -6
- data/spec/rack_spec.rb +59 -7
- metadata +2 -2
@@ -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
|
-
|
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]
|
data/lib/ey-hmac/adapter/rack.rb
CHANGED
@@ -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']
|
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
|
data/lib/ey-hmac/adapter.rb
CHANGED
@@ -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
|
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
|
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
|
-
#
|
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]
|
data/lib/ey-hmac/version.rb
CHANGED
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] :
|
41
|
-
#
|
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
|
-
# @
|
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
|
-
#
|
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
|
-
|
11
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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-
|
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:
|