api-auth 1.1.0 → 1.2.0
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/.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
|