rack-signature 0.0.8 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,65 @@
1
+ # encoding: UTF-8
1
2
  require 'minitest/autorun'
2
3
  require 'rack/test'
3
4
  require 'rack/mock'
4
5
  require 'digest/sha2'
5
6
  require 'securerandom'
7
+ require 'pathname'
8
+ require 'json'
6
9
  require_relative '../lib/rack/signature/test_helpers'
10
+
11
+ module TestHelper
12
+ def read_json(filename)
13
+ datapath.join("#{filename}.json").read
14
+ end
15
+
16
+ def datapath
17
+ Pathname.new('.').dirname.expand_path.join('test')
18
+ end
19
+
20
+ def sorted_nested_string
21
+ "company=Datsun&maintenance=false&record[address][street]=123 Here&record[person][age]=22&record[person][area]=everywhere&record[person][name]=John&record[person][occupation][length_of_employment]=12 years&record[person][occupation][name]=Janitor&record[relatives][][parent_siblings]=aunts, uncles&record[relatives][][parent_siblings_children]=cousins&record[relatives][][siblings]=brothers, sisters"
22
+ end
23
+
24
+ def sorted_nested_hash
25
+ {
26
+ "company" => "Datsun",
27
+ "maintenance" => false,
28
+ "record" => {
29
+ "address" => { "street" => "123 Here" },
30
+ "person" => {
31
+ "age" => 22,
32
+ "area" => "everywhere",
33
+ "name" => "John",
34
+ "occupation" => {
35
+ "length_of_employment" => "12 years",
36
+ "name" => "Janitor",
37
+ }
38
+ },
39
+
40
+ "relatives" => [
41
+ { "parent_siblings" => "aunts, uncles" },
42
+ { "parent_siblings_children" => "cousins" },
43
+ { "siblings" => "brothers, sisters" }
44
+ ]
45
+ },
46
+ }
47
+ end
48
+
49
+ def nested_hash
50
+ { "record" => {
51
+ "person" => {"name" => "John",
52
+ "age" => 22,
53
+ "occupation" => {"name" => "Janitor",
54
+ "length_of_employment" => "12 years"},
55
+ "area" => "everywhere" },
56
+ "relatives" => [{ "siblings" => "brothers, sisters" },
57
+ { "parent_siblings" => "aunts, uncles" },
58
+ { "parent_siblings_children" => "cousins" }],
59
+ "address" => { "street" => "123 Here" }
60
+ },
61
+ "company" => "Datsun",
62
+ "maintenance" => false
63
+ }
64
+ end
65
+ end
@@ -1,98 +1,278 @@
1
+ # encoding: UTF-8
1
2
  require_relative '../lib/rack/signature'
2
3
  require 'test_helper'
4
+ require 'active_support/json'
3
5
 
4
- describe "Verifying a signed request" do
6
+ describe "Verifying a signed requests" do
5
7
  include Rack::Signature::TestHelpers
6
-
7
8
  TOKEN = ::SecureRandom.hex(8)
8
- def setup
9
- @klass_options = {klass: DemoClass, method: :get_shared_token, header_token: 'LOCKER-API-KEY'}
10
- @uri, @headers, @params, @env, @sig, @msg= setup_request("http://example.com/api/login",
11
- {"Content-Type" => "application/json",
12
- "REQUEST_METHOD" => "POST",
13
- "LOCKER-API-KEY" => '123',
14
- input: "password=123456&email=me@home.com&name=me&age=1"
15
- }, TOKEN)
9
+ class DemoClass
10
+ def self.get_shared_token(token = '')
11
+ return "1" if token.nil?
12
+ TOKEN
13
+ end
14
+ end
15
+ let(:klass_options) do
16
+ {klass: DemoClass, method: :get_shared_token, header_token: 'HTTP_LOCKER_API_KEY', debug: false}
16
17
  end
17
-
18
18
  let(:app) { lambda { |env| [200, {}, ['Hello World']] } }
19
- let(:rack_signature) { Rack::Signature.new(app, @klass_options) }
19
+ let(:rack_signature) { Rack::Signature.new(app, klass_options) }
20
20
  let(:mock_request) { Rack::MockRequest.new(rack_signature) }
21
21
 
22
- class DemoClass
23
- def self.get_shared_token(token = '')
24
- TOKEN if token
22
+ describe "when it is a POST via JSON" do
23
+ let(:uri) { "http://example.com/api/login" }
24
+ let(:body) do
25
+ { password: '123456', email: 'me@home.com', name: 'some dude', age: 1 }
25
26
  end
26
- end
27
27
 
28
- let(:uri) { @uri }
29
- let(:signature) { @sig }
30
- let(:headers) { @headers }
31
- let(:query_params) { @params }
32
- let(:env) { @env }
28
+ def request_array
29
+ setup_request(uri, {
30
+ "CONTENT_TYPE" => "application/json",
31
+ "REQUEST_METHOD" => "POST",
32
+ "HTTP_LOCKER_API_KEY" => "123",
33
+ input: convert_hash_to_string(body)
34
+ }, TOKEN)
35
+ end
36
+ let(:expected_env) { request_array[:env] }
37
+ let(:expected_msg) { request_array[:message] }
38
+ let(:expected_sig) { request_array[:signature] }
39
+
40
+ describe "a successful JSON POST" do
41
+ let(:mock_response) do
42
+ mock_request.post(uri,{
43
+ "CONTENT_TYPE" => "application/json",
44
+ "REQUEST_METHOD" => "POST",
45
+ "HTTP_LOCKER_API_KEY" => "123",
46
+ "HTTP_X_AUTH_SIG" => expected_sig,
47
+ input: convert_hash_to_string(body)
48
+ })
49
+ end
33
50
 
34
- describe "when a request is made without a signature" do
35
- before {
36
- @response = mock_request.get '/api/login?password=123456&email=me@home.com'
37
- }
38
- let(:response) { @response }
51
+ it 'will return a 200 Status' do
52
+ assert_equal 200, mock_response.status
53
+ end
39
54
 
40
- it 'returns a 403 status' do
41
- assert_equal 403, response.status
55
+ it 'will have a response body' do
56
+ assert_equal 'Hello World', mock_response.body
57
+ end
58
+
59
+ it 'has the correct built message' do
60
+ received = 'POST/api/loginexample.com{"age":"1","email":"me@home.com","name":"some dude","password":"123456"}'
61
+ assert_equal received, expected_msg
62
+ end
42
63
  end
43
64
 
44
- it 'returns "Invalid Signature" as the response body' do
45
- assert_equal 'Invalid Signature', response.body
65
+ describe "when the query string is tampered with during a JSON POST" do
66
+ let(:mock_response) do
67
+ mock_request.post(uri,{
68
+ "CONTENT_TYPE" => "application/json",
69
+ "REQUEST_METHOD" => "POST",
70
+ "HTTP_LOCKER_API_KEY" => "123",
71
+ "HTTP_X_AUTH_SIG" => expected_sig,
72
+ input: convert_hash_to_string(body.merge(email: 'attacker@home.com'))
73
+ })
74
+ end
75
+
76
+ it 'will return a 401 Status' do
77
+ assert_equal 401, mock_response.status
78
+ end
79
+
80
+ it 'will have a response body denying access' do
81
+ assert_equal 'Access Denied', mock_response.body
82
+ end
83
+
84
+ it 'has the correct built message' do
85
+ assert_match "some dude", expected_msg
86
+ end
87
+ end
88
+ end
89
+
90
+ describe "when it is a GET via JSON" do
91
+ let(:uri) { "http://example.com/api/login" }
92
+ let(:body) do
93
+ { password: '123456', email: 'me@home.com', name: 'some dude', age: 1 }
94
+ end
95
+
96
+ def request_array
97
+ setup_request(uri, {
98
+ "CONTENT_TYPE" => "application/json",
99
+ "REQUEST_METHOD" => "GET",
100
+ "HTTP_LOCKER_API_KEY" => "123",
101
+ "QUERY_STRING" => convert_hash_to_string(body)
102
+ }, TOKEN)
103
+ end
104
+ let(:expected_env) { request_array[:env] }
105
+ let(:expected_msg) { request_array[:message] }
106
+ let(:expected_sig) { request_array[:signature] }
107
+
108
+ describe "a successful JSON GET request" do
109
+ let(:mock_response) do
110
+ mock_request.post(uri,{
111
+ "CONTENT_TYPE" => "application/json",
112
+ "REQUEST_METHOD" => "GET",
113
+ "HTTP_LOCKER_API_KEY" => "123",
114
+ "QUERY_STRING" => convert_hash_to_string(body),
115
+ "HTTP_X_AUTH_SIG" => expected_sig
116
+ })
117
+ end
118
+
119
+ it 'will return a 200 Status' do
120
+ assert_equal 200, mock_response.status
121
+ end
122
+
123
+ it 'will have a response body' do
124
+ assert_equal 'Hello World', mock_response.body
125
+ end
126
+
127
+ it 'has the correct built message' do
128
+ assert_match "some dude", expected_msg
129
+ end
46
130
  end
47
131
 
48
- it 'returns the correct header' do
49
- expected_header = {"Content-Type"=>"text/html", "Content-Length"=>"17"}
50
- assert_equal expected_header, response.header
132
+ describe "when the query string is tampered with during a JSON GET request" do
133
+ let(:mock_response) do
134
+ mock_request.post(uri,{
135
+ "CONTENT_TYPE" => "application/json",
136
+ "REQUEST_METHOD" => "GET",
137
+ "HTTP_LOCKER_API_KEY" => "123",
138
+ "QUERY_STRING" => convert_hash_to_string(body.merge(password: 654321)),
139
+ "HTTP_X_AUTH_SIG" => expected_sig
140
+ })
141
+ end
142
+
143
+ it 'will return a 401 Status' do
144
+ assert_equal 401, mock_response.status
145
+ end
146
+
147
+ it 'will have a response body denying access' do
148
+ assert_equal 'Access Denied', mock_response.body
149
+ end
150
+
151
+ it 'has the correct built message' do
152
+ assert_match "some dude", expected_msg
153
+ end
51
154
  end
52
155
  end
53
156
 
54
- describe "when a requests is sent with a valid signature" do
55
- let(:response) do
56
- mock_request.post(uri,
57
- "Content-Type" => "application/json",
157
+ describe "when using unicode json with ActiveSupport involvement" do
158
+ let(:uri) { "http://example.com/api/register" }
159
+ let(:body) do
160
+ { password: '123456', email: "namệ@hõmệ.com", name: "sõmệ dưdệ:", age: 1 }
161
+ end
162
+
163
+ def request_array
164
+ setup_request(uri, {
165
+ "CONTENT_TYPE" => "application/json; charset=utf-8",
58
166
  "REQUEST_METHOD" => "POST",
59
- "LOCKER-API-KEY" => '123',
60
- "X-Auth-Sig" => signature,
61
- input: "password=123456&email=me@home.com&name=me&age=1")
167
+ input: ActiveSupport::JSON.encode(body)
168
+ }, "1")
169
+ end
170
+ let(:expected_env) { request_array[:env] }
171
+ let(:expected_msg) { request_array[:message] }
172
+ let(:expected_sig) { request_array[:signature] }
173
+ let(:expected_key) { request_array[:key] }
174
+
175
+ let(:mock_response) do
176
+ mock_request.post(uri,{
177
+ "CONTENT_TYPE" => "application/json; charset=utf-8",
178
+ "REQUEST_METHOD" => "POST",
179
+ "HTTP_X_AUTH_SIG" => expected_sig,
180
+ input: ActiveSupport::JSON.encode(body)
181
+ })
62
182
  end
63
183
 
64
184
  it 'will return a 200 status' do
65
- assert_equal 200, response.status
185
+ assert_equal 200, mock_response.status
66
186
  end
67
187
 
68
- it 'will call the next rack app' do
69
- assert_equal 'Hello World', response.body
188
+ it 'will have a response body' do
189
+ assert_equal 'Hello World', mock_response.body
190
+ end
191
+
192
+ it 'has the correct built message' do
193
+ assert_equal "POST/api/registerexample.com{\"age\":1,\"email\":\"namệ@hõmệ.com\",\"name\":\"sõmệ dưdệ:\",\"password\":\"123456\"}", expected_msg
70
194
  end
71
195
  end
72
196
 
197
+ describe "when using unicode json without ActiveSupport involvement" do
198
+ let(:uri) { "http://example.com/api/register" }
199
+ let(:body) do
200
+ { password: '123456', email: "namệ@hõmệ.com", name: "sõmệ dưdệ:", age: 1 }
201
+ end
73
202
 
74
- describe "when a requests is sent with a tampered signature" do
75
- let(:response) do
76
- mock_request.post(uri,
77
- {"Content-Type" => "application/json",
203
+ def request_array
204
+ setup_request(uri, {
205
+ "CONTENT_TYPE" => "application/json; charset=utf-8",
78
206
  "REQUEST_METHOD" => "POST",
79
- "LOCKER-API-KEY" => '123',
80
- "X-Auth-Sig" => signature,
81
- input: "password=1234567&email=me@home.com&name=me&age=1"})
207
+ input: body.to_json
208
+ }, "1")
82
209
  end
210
+ let(:expected_env) { request_array[:env] }
211
+ let(:expected_msg) { request_array[:message] }
212
+ let(:expected_sig) { request_array[:signature] }
213
+ let(:expected_key) { request_array[:key] }
83
214
 
84
- it 'returns a 403 status' do
85
- assert_equal 403, response.status
215
+ let(:mock_response) do
216
+ mock_request.post(uri,{
217
+ "CONTENT_TYPE" => "application/json; charset=utf-8",
218
+ "REQUEST_METHOD" => "POST",
219
+ "HTTP_X_AUTH_SIG" => expected_sig,
220
+ input: body.to_json
221
+ })
86
222
  end
87
223
 
88
- it 'returns "Invalid Signature" as the response body' do
89
- assert_equal 'Invalid Signature', response.body
224
+ it 'will return a 200 status' do
225
+ assert_equal 200, mock_response.status
226
+ end
227
+
228
+ it 'will have a response body' do
229
+ assert_equal 'Hello World', mock_response.body
90
230
  end
91
231
 
92
- it 'returns the correct header' do
93
- expected_header = {"Content-Type"=>"text/html", "Content-Length"=>"17"}
94
- assert_equal expected_header, response.header
232
+ it 'has the correct built message' do
233
+ assert_equal "POST/api/registerexample.com{\"age\":1,\"email\":\"namệ@hõmệ.com\",\"name\":\"sõmệ dưdệ:\",\"password\":\"123456\"}", expected_msg
95
234
  end
96
235
  end
97
236
 
237
+ describe "when there is not token" do
238
+ let(:uri) { "http://example.com/api/register" }
239
+ let(:body) do
240
+ { password: '123456', email: 'me@home.com', name: 'some dude', age: 1 }
241
+ end
242
+
243
+ def request_array
244
+ setup_request(uri, {
245
+ "CONTENT_TYPE" => "application/json",
246
+ "REQUEST_METHOD" => "POST",
247
+ "QUERY_STRING" => convert_hash_to_string(body)
248
+ }, "1")
249
+ end
250
+ let(:expected_env) { request_array[:env] }
251
+ let(:expected_msg) { request_array[:message] }
252
+ let(:expected_sig) { request_array[:signature] }
253
+ let(:expected_key) { request_array[:key] }
254
+
255
+ describe "when no token is required, but the request must still be signed" do
256
+ let(:mock_response) do
257
+ mock_request.post(uri,{
258
+ "CONTENT_TYPE" => "application/json",
259
+ "REQUEST_METHOD" => "POST",
260
+ "QUERY_STRING" => convert_hash_to_string(body),
261
+ "HTTP_X_AUTH_SIG" => expected_sig
262
+ })
263
+ end
264
+
265
+ it 'will return a 200 status' do
266
+ assert_equal 200, mock_response.status
267
+ end
268
+
269
+ it 'will have a response body' do
270
+ assert_equal 'Hello World', mock_response.body
271
+ end
272
+
273
+ it 'has the correct built message' do
274
+ assert_equal "POST/api/registerexample.comage=1&email=me@home.com&name=some dude&password=123456", expected_msg
275
+ end
276
+ end
277
+ end
98
278
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-signature
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2013-01-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -75,6 +75,22 @@ dependencies:
75
75
  - - ! '>='
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: active_support
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
78
94
  description: Rack Middleware for verifying signed requests
79
95
  email:
80
96
  - robert@codewranglers.org
@@ -90,13 +106,22 @@ files:
90
106
  - Rakefile
91
107
  - lib/rack/signature.rb
92
108
  - lib/rack/signature/build_message.rb
109
+ - lib/rack/signature/build_post_body.rb
110
+ - lib/rack/signature/deep_merge.rb
93
111
  - lib/rack/signature/hmac_signature.rb
112
+ - lib/rack/signature/sort_query_params.rb
94
113
  - lib/rack/signature/test_helpers.rb
95
114
  - lib/rack/signature/verify.rb
96
115
  - lib/rack/signature/version.rb
97
116
  - rack-signature.gemspec
117
+ - test/array_of_hashes.json
98
118
  - test/build_message_test.rb
119
+ - test/build_post_body_test.rb
120
+ - test/data.json
121
+ - test/deep_merge_test.rb
99
122
  - test/hmac_signature_test.rb
123
+ - test/ordered_json_data.json
124
+ - test/sort_query_params_test.rb
100
125
  - test/test_helper.rb
101
126
  - test/verify_test.rb
102
127
  homepage: ''
@@ -119,13 +144,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
144
  version: '0'
120
145
  requirements: []
121
146
  rubyforge_project:
122
- rubygems_version: 1.8.24
147
+ rubygems_version: 1.8.23
123
148
  signing_key:
124
149
  specification_version: 3
125
150
  summary: Rack Middleware for verifying signed requests
126
151
  test_files:
152
+ - test/array_of_hashes.json
127
153
  - test/build_message_test.rb
154
+ - test/build_post_body_test.rb
155
+ - test/data.json
156
+ - test/deep_merge_test.rb
128
157
  - test/hmac_signature_test.rb
158
+ - test/ordered_json_data.json
159
+ - test/sort_query_params_test.rb
129
160
  - test/test_helper.rb
130
161
  - test/verify_test.rb
131
- has_rdoc: