api-auth 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +17 -0
- data/Appraisals +29 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +42 -11
- data/LICENSE.txt +1 -1
- data/README.md +15 -13
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/api_auth.gemspec +5 -3
- data/gemfiles/rails_23.gemfile +7 -0
- data/gemfiles/rails_23.gemfile.lock +57 -0
- data/gemfiles/rails_30.gemfile +7 -0
- data/gemfiles/rails_30.gemfile.lock +79 -0
- data/gemfiles/rails_31.gemfile +9 -0
- data/gemfiles/rails_31.gemfile.lock +85 -0
- data/gemfiles/rails_32.gemfile +9 -0
- data/gemfiles/rails_32.gemfile.lock +84 -0
- data/gemfiles/rails_4.gemfile +9 -0
- data/gemfiles/rails_4.gemfile.lock +81 -0
- data/lib/api_auth.rb +1 -0
- data/lib/api_auth/base.rb +1 -1
- data/lib/api_auth/headers.rb +4 -0
- data/lib/api_auth/helpers.rb +18 -5
- data/lib/api_auth/request_drivers/action_controller.rb +2 -2
- data/lib/api_auth/request_drivers/httpi.rb +80 -0
- data/lib/api_auth/request_drivers/net_http.rb +3 -1
- data/lib/api_auth/request_drivers/rack.rb +1 -1
- data/lib/api_auth/request_drivers/rest_client.rb +4 -4
- data/spec/api_auth_spec.rb +97 -22
- data/spec/headers_spec.rb +64 -3
- data/spec/railtie_spec.rb +51 -36
- data/spec/spec_helper.rb +2 -1
- metadata +55 -9
@@ -0,0 +1,84 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
api-auth (1.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actionpack (3.2.17)
|
10
|
+
activemodel (= 3.2.17)
|
11
|
+
activesupport (= 3.2.17)
|
12
|
+
builder (~> 3.0.0)
|
13
|
+
erubis (~> 2.7.0)
|
14
|
+
journey (~> 1.0.4)
|
15
|
+
rack (~> 1.4.5)
|
16
|
+
rack-cache (~> 1.2)
|
17
|
+
rack-test (~> 0.6.1)
|
18
|
+
sprockets (~> 2.2.1)
|
19
|
+
activemodel (3.2.17)
|
20
|
+
activesupport (= 3.2.17)
|
21
|
+
builder (~> 3.0.0)
|
22
|
+
activeresource (3.2.17)
|
23
|
+
activemodel (= 3.2.17)
|
24
|
+
activesupport (= 3.2.17)
|
25
|
+
activesupport (3.2.17)
|
26
|
+
i18n (~> 0.6, >= 0.6.4)
|
27
|
+
multi_json (~> 1.0)
|
28
|
+
amatch (0.2.11)
|
29
|
+
tins (~> 0.3)
|
30
|
+
appraisal (0.5.2)
|
31
|
+
bundler
|
32
|
+
rake
|
33
|
+
builder (3.0.4)
|
34
|
+
curb (0.8.5)
|
35
|
+
diff-lcs (1.1.3)
|
36
|
+
erubis (2.7.0)
|
37
|
+
hike (1.2.3)
|
38
|
+
httpi (2.1.0)
|
39
|
+
rack
|
40
|
+
rubyntlm (~> 0.3.2)
|
41
|
+
i18n (0.6.9)
|
42
|
+
journey (1.0.4)
|
43
|
+
mime-types (1.25.1)
|
44
|
+
multi_json (1.9.2)
|
45
|
+
rack (1.4.5)
|
46
|
+
rack-cache (1.2)
|
47
|
+
rack (>= 0.4)
|
48
|
+
rack-test (0.6.2)
|
49
|
+
rack (>= 1.0)
|
50
|
+
rake (10.1.1)
|
51
|
+
rest-client (1.6.7)
|
52
|
+
mime-types (>= 1.16)
|
53
|
+
rspec (2.4.0)
|
54
|
+
rspec-core (~> 2.4.0)
|
55
|
+
rspec-expectations (~> 2.4.0)
|
56
|
+
rspec-mocks (~> 2.4.0)
|
57
|
+
rspec-core (2.4.0)
|
58
|
+
rspec-expectations (2.4.0)
|
59
|
+
diff-lcs (~> 1.1.2)
|
60
|
+
rspec-mocks (2.4.0)
|
61
|
+
rubyntlm (0.3.4)
|
62
|
+
sprockets (2.2.2)
|
63
|
+
hike (~> 1.2)
|
64
|
+
multi_json (~> 1.0)
|
65
|
+
rack (~> 1.0)
|
66
|
+
tilt (~> 1.1, != 1.3.0)
|
67
|
+
tilt (1.4.1)
|
68
|
+
tins (0.13.2)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
actionpack (~> 3.2.17)
|
75
|
+
activeresource (~> 3.2.17)
|
76
|
+
activesupport (~> 3.2.17)
|
77
|
+
amatch
|
78
|
+
api-auth!
|
79
|
+
appraisal
|
80
|
+
curb (~> 0.8.1)
|
81
|
+
httpi
|
82
|
+
rake
|
83
|
+
rest-client (~> 1.6.0)
|
84
|
+
rspec (~> 2.4.0)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ../
|
3
|
+
specs:
|
4
|
+
api-auth (1.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
actionpack (4.0.4)
|
10
|
+
activesupport (= 4.0.4)
|
11
|
+
builder (~> 3.1.0)
|
12
|
+
erubis (~> 2.7.0)
|
13
|
+
rack (~> 1.5.2)
|
14
|
+
rack-test (~> 0.6.2)
|
15
|
+
activemodel (4.0.4)
|
16
|
+
activesupport (= 4.0.4)
|
17
|
+
builder (~> 3.1.0)
|
18
|
+
activeresource (4.0.0)
|
19
|
+
activemodel (~> 4.0)
|
20
|
+
activesupport (~> 4.0)
|
21
|
+
rails-observers (~> 0.1.1)
|
22
|
+
activesupport (4.0.4)
|
23
|
+
i18n (~> 0.6, >= 0.6.9)
|
24
|
+
minitest (~> 4.2)
|
25
|
+
multi_json (~> 1.3)
|
26
|
+
thread_safe (~> 0.1)
|
27
|
+
tzinfo (~> 0.3.37)
|
28
|
+
amatch (0.2.11)
|
29
|
+
tins (~> 0.3)
|
30
|
+
appraisal (0.5.2)
|
31
|
+
bundler
|
32
|
+
rake
|
33
|
+
atomic (1.1.16)
|
34
|
+
builder (3.1.4)
|
35
|
+
curb (0.8.5)
|
36
|
+
diff-lcs (1.1.3)
|
37
|
+
erubis (2.7.0)
|
38
|
+
httpi (2.1.0)
|
39
|
+
rack
|
40
|
+
rubyntlm (~> 0.3.2)
|
41
|
+
i18n (0.6.9)
|
42
|
+
mime-types (2.2)
|
43
|
+
minitest (4.7.5)
|
44
|
+
multi_json (1.9.2)
|
45
|
+
rack (1.5.2)
|
46
|
+
rack-test (0.6.2)
|
47
|
+
rack (>= 1.0)
|
48
|
+
rails-observers (0.1.2)
|
49
|
+
activemodel (~> 4.0)
|
50
|
+
rake (10.1.1)
|
51
|
+
rest-client (1.6.7)
|
52
|
+
mime-types (>= 1.16)
|
53
|
+
rspec (2.4.0)
|
54
|
+
rspec-core (~> 2.4.0)
|
55
|
+
rspec-expectations (~> 2.4.0)
|
56
|
+
rspec-mocks (~> 2.4.0)
|
57
|
+
rspec-core (2.4.0)
|
58
|
+
rspec-expectations (2.4.0)
|
59
|
+
diff-lcs (~> 1.1.2)
|
60
|
+
rspec-mocks (2.4.0)
|
61
|
+
rubyntlm (0.3.4)
|
62
|
+
thread_safe (0.2.0)
|
63
|
+
atomic (>= 1.1.7, < 2)
|
64
|
+
tins (0.13.2)
|
65
|
+
tzinfo (0.3.39)
|
66
|
+
|
67
|
+
PLATFORMS
|
68
|
+
ruby
|
69
|
+
|
70
|
+
DEPENDENCIES
|
71
|
+
actionpack (~> 4.0.4)
|
72
|
+
activeresource (~> 4.0.0)
|
73
|
+
activesupport (~> 4.0.4)
|
74
|
+
amatch
|
75
|
+
api-auth!
|
76
|
+
appraisal
|
77
|
+
curb (~> 0.8.1)
|
78
|
+
httpi
|
79
|
+
rake
|
80
|
+
rest-client (~> 1.6.0)
|
81
|
+
rspec (~> 2.4.0)
|
data/lib/api_auth.rb
CHANGED
@@ -10,6 +10,7 @@ require 'api_auth/request_drivers/rest_client'
|
|
10
10
|
require 'api_auth/request_drivers/action_controller'
|
11
11
|
require 'api_auth/request_drivers/action_dispatch'
|
12
12
|
require 'api_auth/request_drivers/rack'
|
13
|
+
require 'api_auth/request_drivers/httpi'
|
13
14
|
|
14
15
|
require 'api_auth/headers'
|
15
16
|
require 'api_auth/base'
|
data/lib/api_auth/base.rb
CHANGED
@@ -15,7 +15,7 @@ module ApiAuth
|
|
15
15
|
# Signs an HTTP request using the client's access id and secret key.
|
16
16
|
# Returns the HTTP request object with the modified headers.
|
17
17
|
#
|
18
|
-
# request: The request can be a Net::HTTP,
|
18
|
+
# request: The request can be a Net::HTTP, ActionDispatch::Request,
|
19
19
|
# Curb (Curl::Easy) or a RestClient object.
|
20
20
|
#
|
21
21
|
# access_id: The public unique identifier for the client
|
data/lib/api_auth/headers.rb
CHANGED
@@ -29,6 +29,10 @@ module ApiAuth
|
|
29
29
|
@request = RackRequest.new(request)
|
30
30
|
when /ActionController::CgiRequest/
|
31
31
|
@request = ActionControllerRequest.new(request)
|
32
|
+
when /HTTPI::Request/
|
33
|
+
@request = HttpiRequest.new(request)
|
34
|
+
when /Sinatra::Request/
|
35
|
+
@request = RackRequest.new(request)
|
32
36
|
else
|
33
37
|
raise UnknownHTTPRequest, "#{request.class.to_s} is not yet supported."
|
34
38
|
end
|
data/lib/api_auth/helpers.rb
CHANGED
@@ -1,18 +1,31 @@
|
|
1
1
|
module ApiAuth
|
2
2
|
|
3
3
|
module Helpers # :nodoc:
|
4
|
-
|
4
|
+
|
5
5
|
def b64_encode(string)
|
6
|
-
Base64.strict_encode64
|
6
|
+
if Base64.respond_to?(:strict_encode64)
|
7
|
+
Base64.strict_encode64(string)
|
8
|
+
else
|
9
|
+
# Fall back to stripping out newlines on Ruby 1.8.
|
10
|
+
Base64.encode64(string).gsub(/\n/, '')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def md5_base64digest(string)
|
15
|
+
if Digest::MD5.respond_to?(:base64digest)
|
16
|
+
Digest::MD5.base64digest(string)
|
17
|
+
else
|
18
|
+
b64_encode(Digest::MD5.digest(string))
|
19
|
+
end
|
7
20
|
end
|
8
|
-
|
21
|
+
|
9
22
|
# Capitalizes the keys of a hash
|
10
23
|
def capitalize_keys(hsh)
|
11
24
|
capitalized_hash = {}
|
12
25
|
hsh.each_pair {|k,v| capitalized_hash[k.to_s.upcase] = v }
|
13
26
|
capitalized_hash
|
14
27
|
end
|
15
|
-
|
28
|
+
|
16
29
|
end
|
17
|
-
|
30
|
+
|
18
31
|
end
|
@@ -24,7 +24,7 @@ module ApiAuth
|
|
24
24
|
else
|
25
25
|
body = ''
|
26
26
|
end
|
27
|
-
|
27
|
+
md5_base64digest(body)
|
28
28
|
end
|
29
29
|
|
30
30
|
def populate_content_md5
|
@@ -60,7 +60,7 @@ module ApiAuth
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def set_date
|
63
|
-
@request.env['
|
63
|
+
@request.env['HTTP_DATE'] = Time.now.utc.httpdate
|
64
64
|
end
|
65
65
|
|
66
66
|
def timestamp
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module ApiAuth
|
2
|
+
|
3
|
+
module RequestDrivers # :nodoc:
|
4
|
+
|
5
|
+
class HttpiRequest # :nodoc:
|
6
|
+
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
9
|
+
def initialize(request)
|
10
|
+
@request = request
|
11
|
+
@headers = fetch_headers
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_auth_header(header)
|
16
|
+
@request.headers["Authorization"] = header
|
17
|
+
@headers = fetch_headers
|
18
|
+
@request
|
19
|
+
end
|
20
|
+
|
21
|
+
def calculated_md5
|
22
|
+
md5_base64digest(@request.body || '')
|
23
|
+
end
|
24
|
+
|
25
|
+
def populate_content_md5
|
26
|
+
if @request.body
|
27
|
+
@request.headers["Content-MD5"] = calculated_md5
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def md5_mismatch?
|
32
|
+
if @request.body
|
33
|
+
calculated_md5 != content_md5
|
34
|
+
else
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def fetch_headers
|
40
|
+
capitalize_keys @request.headers
|
41
|
+
end
|
42
|
+
|
43
|
+
def content_type
|
44
|
+
value = find_header(%w(CONTENT-TYPE CONTENT_TYPE HTTP_CONTENT_TYPE))
|
45
|
+
value.nil? ? "" : value
|
46
|
+
end
|
47
|
+
|
48
|
+
def content_md5
|
49
|
+
value = find_header(%w(CONTENT-MD5 CONTENT_MD5))
|
50
|
+
value.nil? ? "" : value
|
51
|
+
end
|
52
|
+
|
53
|
+
def request_uri
|
54
|
+
@request.url.request_uri
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_date
|
58
|
+
@request.headers["DATE"] = Time.now.utc.httpdate
|
59
|
+
end
|
60
|
+
|
61
|
+
def timestamp
|
62
|
+
value = find_header(%w(DATE HTTP_DATE))
|
63
|
+
value.nil? ? "" : value
|
64
|
+
end
|
65
|
+
|
66
|
+
def authorization_header
|
67
|
+
find_header %w(Authorization AUTHORIZATION HTTP_AUTHORIZATION)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def find_header(keys)
|
73
|
+
keys.map {|key| @headers[key] }.compact.first
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -4,6 +4,8 @@ module ApiAuth
|
|
4
4
|
|
5
5
|
class NetHttpRequest # :nodoc:
|
6
6
|
|
7
|
+
include ApiAuth::Helpers
|
8
|
+
|
7
9
|
def initialize(request)
|
8
10
|
@request = request
|
9
11
|
@headers = fetch_headers
|
@@ -17,7 +19,7 @@ module ApiAuth
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def calculated_md5
|
20
|
-
|
22
|
+
md5_base64digest(@request.body || '')
|
21
23
|
end
|
22
24
|
|
23
25
|
def populate_content_md5
|
@@ -17,7 +17,6 @@ module ApiAuth
|
|
17
17
|
|
18
18
|
def set_auth_header(header)
|
19
19
|
@request.headers.merge!({ "Authorization" => header })
|
20
|
-
@headers = fetch_headers
|
21
20
|
save_headers # enforce update of processed_headers based on last updated headers
|
22
21
|
@request
|
23
22
|
end
|
@@ -29,7 +28,7 @@ module ApiAuth
|
|
29
28
|
else
|
30
29
|
body = ''
|
31
30
|
end
|
32
|
-
|
31
|
+
md5_base64digest(body)
|
33
32
|
end
|
34
33
|
|
35
34
|
def populate_content_md5
|
@@ -47,7 +46,7 @@ module ApiAuth
|
|
47
46
|
end
|
48
47
|
|
49
48
|
def fetch_headers
|
50
|
-
capitalize_keys @request.
|
49
|
+
capitalize_keys @request.processed_headers
|
51
50
|
end
|
52
51
|
|
53
52
|
def content_type
|
@@ -84,7 +83,8 @@ module ApiAuth
|
|
84
83
|
end
|
85
84
|
|
86
85
|
def save_headers
|
87
|
-
@request.processed_headers = @request.make_headers(@headers)
|
86
|
+
@request.processed_headers = @request.make_headers(@request.headers)
|
87
|
+
@headers = fetch_headers
|
88
88
|
end
|
89
89
|
|
90
90
|
end
|
data/spec/api_auth_spec.rb
CHANGED
@@ -36,8 +36,8 @@ describe "ApiAuth" do
|
|
36
36
|
describe "with Net::HTTP" do
|
37
37
|
|
38
38
|
before(:each) do
|
39
|
-
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
40
|
-
'content-type' => 'text/plain',
|
39
|
+
@request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
40
|
+
'content-type' => 'text/plain',
|
41
41
|
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
42
42
|
'date' => Time.now.utc.httpdate)
|
43
43
|
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
@@ -54,7 +54,7 @@ describe "ApiAuth" do
|
|
54
54
|
'content-type' => 'text/plain',
|
55
55
|
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
56
56
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
57
|
-
signed_request['Content-MD5'].should ==
|
57
|
+
signed_request['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
58
58
|
end
|
59
59
|
|
60
60
|
it "should calculate for real content" do
|
@@ -63,7 +63,7 @@ describe "ApiAuth" do
|
|
63
63
|
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
64
64
|
request.body = "hello\nworld"
|
65
65
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
66
|
-
signed_request['Content-MD5'].should ==
|
66
|
+
signed_request['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
@@ -99,7 +99,7 @@ describe "ApiAuth" do
|
|
99
99
|
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
100
100
|
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
it "should retrieve the access_id" do
|
104
104
|
ApiAuth.access_id(@signed_request).should == "1044"
|
105
105
|
end
|
@@ -112,7 +112,7 @@ describe "ApiAuth" do
|
|
112
112
|
headers = { 'Content-MD5' => "1B2M2Y8AsgTpgAmY7PhCfg==",
|
113
113
|
'Content-Type' => "text/plain",
|
114
114
|
'Date' => Time.now.utc.httpdate }
|
115
|
-
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
115
|
+
@request = RestClient::Request.new(:url => "/resource.xml?foo=bar&bar=foo",
|
116
116
|
:headers => headers,
|
117
117
|
:method => :put)
|
118
118
|
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
@@ -131,7 +131,7 @@ describe "ApiAuth" do
|
|
131
131
|
:headers => headers,
|
132
132
|
:method => :put)
|
133
133
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
134
|
-
signed_request.headers['Content-MD5'].should ==
|
134
|
+
signed_request.headers['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
135
135
|
end
|
136
136
|
|
137
137
|
it "should calculate for real content" do
|
@@ -142,7 +142,7 @@ describe "ApiAuth" do
|
|
142
142
|
:method => :put,
|
143
143
|
:payload => "hellow\nworld")
|
144
144
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
145
|
-
signed_request.headers['Content-MD5'].should ==
|
145
|
+
signed_request.headers['Content-MD5'].should == "G0grublI06013h58g9j8Vw=="
|
146
146
|
end
|
147
147
|
end
|
148
148
|
|
@@ -180,7 +180,7 @@ describe "ApiAuth" do
|
|
180
180
|
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
181
181
|
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
182
182
|
end
|
183
|
-
|
183
|
+
|
184
184
|
it "should retrieve the access_id" do
|
185
185
|
ApiAuth.access_id(@signed_request).should == "1044"
|
186
186
|
end
|
@@ -236,17 +236,19 @@ describe "ApiAuth" do
|
|
236
236
|
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
237
237
|
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
238
238
|
end
|
239
|
-
|
239
|
+
|
240
240
|
it "should retrieve the access_id" do
|
241
241
|
ApiAuth.access_id(@signed_request).should == "1044"
|
242
242
|
end
|
243
243
|
|
244
244
|
end
|
245
245
|
|
246
|
-
describe "with ActionController" do
|
246
|
+
describe "with ActionController/ActionDispatch" do
|
247
|
+
|
248
|
+
let(:request_klass){ ActionDispatch::Request rescue ActionController::Request }
|
247
249
|
|
248
250
|
before(:each) do
|
249
|
-
@request =
|
251
|
+
@request = request_klass.new(
|
250
252
|
'PATH_INFO' => '/resource.xml',
|
251
253
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
252
254
|
'REQUEST_METHOD' => 'PUT',
|
@@ -256,25 +258,25 @@ describe "ApiAuth" do
|
|
256
258
|
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
257
259
|
end
|
258
260
|
|
259
|
-
it "should return a
|
260
|
-
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match(
|
261
|
+
it "should return a ActionDispatch::Request object after signing it" do
|
262
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match(request_klass.to_s)
|
261
263
|
end
|
262
264
|
|
263
265
|
describe "md5 header" do
|
264
266
|
context "not already provided" do
|
265
267
|
it "should calculate for empty string" do
|
266
|
-
request =
|
268
|
+
request = request_klass.new(
|
267
269
|
'PATH_INFO' => '/resource.xml',
|
268
270
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
269
271
|
'REQUEST_METHOD' => 'PUT',
|
270
272
|
'CONTENT_TYPE' => 'text/plain',
|
271
273
|
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT')
|
272
274
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
273
|
-
signed_request.env['Content-MD5'].should ==
|
275
|
+
signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
274
276
|
end
|
275
277
|
|
276
278
|
it "should calculate for real content" do
|
277
|
-
request =
|
279
|
+
request = request_klass.new(
|
278
280
|
'PATH_INFO' => '/resource.xml',
|
279
281
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
280
282
|
'REQUEST_METHOD' => 'PUT',
|
@@ -282,7 +284,7 @@ describe "ApiAuth" do
|
|
282
284
|
'HTTP_DATE' => 'Mon, 23 Jan 1984 03:29:56 GMT',
|
283
285
|
'RAW_POST_DATA' => "hello\nworld")
|
284
286
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
285
|
-
signed_request.env['Content-MD5'].should ==
|
287
|
+
signed_request.env['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
286
288
|
end
|
287
289
|
|
288
290
|
end
|
@@ -305,7 +307,7 @@ describe "ApiAuth" do
|
|
305
307
|
end
|
306
308
|
|
307
309
|
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
308
|
-
request =
|
310
|
+
request = request_klass.new(
|
309
311
|
'PATH_INFO' => '/resource.xml',
|
310
312
|
'QUERY_STRING' => 'foo=bar&bar=foo',
|
311
313
|
'REQUEST_METHOD' => 'PUT',
|
@@ -350,7 +352,7 @@ describe "ApiAuth" do
|
|
350
352
|
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
351
353
|
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put).merge!(headers))
|
352
354
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
353
|
-
signed_request.env['Content-MD5'].should ==
|
355
|
+
signed_request.env['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
354
356
|
end
|
355
357
|
|
356
358
|
it "should calculate for real content" do
|
@@ -358,7 +360,7 @@ describe "ApiAuth" do
|
|
358
360
|
'Date' => "Mon, 23 Jan 1984 03:29:56 GMT" }
|
359
361
|
request = Rack::Request.new(Rack::MockRequest.env_for("/resource.xml?foo=bar&bar=foo", :method => :put, :input => "hellow\nworld").merge!(headers))
|
360
362
|
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
361
|
-
signed_request.env['Content-MD5'].should ==
|
363
|
+
signed_request.env['Content-MD5'].should == "G0grublI06013h58g9j8Vw=="
|
362
364
|
end
|
363
365
|
end
|
364
366
|
|
@@ -395,13 +397,86 @@ describe "ApiAuth" do
|
|
395
397
|
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
396
398
|
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
397
399
|
end
|
398
|
-
|
400
|
+
|
399
401
|
it "should retrieve the access_id" do
|
400
402
|
ApiAuth.access_id(@signed_request).should == "1044"
|
401
403
|
end
|
402
404
|
|
403
405
|
end
|
404
406
|
|
407
|
+
describe "with HTTPI" do
|
408
|
+
before(:each) do
|
409
|
+
@request = HTTPI::Request.new("http://localhost/resource.xml?foo=bar&bar=foo")
|
410
|
+
@request.headers.merge!({
|
411
|
+
'content-type' => 'text/plain',
|
412
|
+
'content-md5' => '1B2M2Y8AsgTpgAmY7PhCfg==',
|
413
|
+
'date' => Time.now.utc.httpdate
|
414
|
+
})
|
415
|
+
@headers = ApiAuth::Headers.new(@request)
|
416
|
+
@signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
417
|
+
end
|
418
|
+
|
419
|
+
it "should return a HTTPI object after signing it" do
|
420
|
+
ApiAuth.sign!(@request, @access_id, @secret_key).class.to_s.should match("HTTPI::Request")
|
421
|
+
end
|
422
|
+
|
423
|
+
describe "md5 header" do
|
424
|
+
context "not already provided" do
|
425
|
+
it "should calculate for empty string" do
|
426
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
427
|
+
'content-type' => 'text/plain',
|
428
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
429
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
430
|
+
signed_request['Content-MD5'].should == "1B2M2Y8AsgTpgAmY7PhCfg=="
|
431
|
+
end
|
432
|
+
|
433
|
+
it "should calculate for real content" do
|
434
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
435
|
+
'content-type' => 'text/plain',
|
436
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
437
|
+
request.body = "hello\nworld"
|
438
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
439
|
+
signed_request['Content-MD5'].should == "kZXQvrKoieG+Be1rsZVINw=="
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
443
|
+
it "should leave the content-md5 alone if provided" do
|
444
|
+
@signed_request.headers['Content-MD5'].should == '1B2M2Y8AsgTpgAmY7PhCfg=='
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
it "should sign the request" do
|
449
|
+
@signed_request.headers['Authorization'].should == "APIAuth 1044:#{hmac(@secret_key, @request)}"
|
450
|
+
end
|
451
|
+
|
452
|
+
it "should authenticate a valid request" do
|
453
|
+
ApiAuth.authentic?(@signed_request, @secret_key).should be_true
|
454
|
+
end
|
455
|
+
|
456
|
+
it "should NOT authenticate a non-valid request" do
|
457
|
+
ApiAuth.authentic?(@signed_request, @secret_key+'j').should be_false
|
458
|
+
end
|
459
|
+
|
460
|
+
it "should NOT authenticate a mismatched content-md5 when body has changed" do
|
461
|
+
request = Net::HTTP::Put.new("/resource.xml?foo=bar&bar=foo",
|
462
|
+
'content-type' => 'text/plain',
|
463
|
+
'date' => "Mon, 23 Jan 1984 03:29:56 GMT")
|
464
|
+
request.body = "hello\nworld"
|
465
|
+
signed_request = ApiAuth.sign!(request, @access_id, @secret_key)
|
466
|
+
signed_request.body = "goodbye"
|
467
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
468
|
+
end
|
469
|
+
|
470
|
+
it "should NOT authenticate an expired request" do
|
471
|
+
@request.headers['Date'] = 16.minutes.ago.utc.httpdate
|
472
|
+
signed_request = ApiAuth.sign!(@request, @access_id, @secret_key)
|
473
|
+
ApiAuth.authentic?(signed_request, @secret_key).should be_false
|
474
|
+
end
|
475
|
+
|
476
|
+
it "should retrieve the access_id" do
|
477
|
+
ApiAuth.access_id(@signed_request).should == "1044"
|
478
|
+
end
|
479
|
+
end
|
405
480
|
end
|
406
481
|
|
407
482
|
end
|