rack-signature 0.0.8 → 0.1.2

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.
@@ -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: