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.
- data/.gitignore +1 -0
- data/README.md +50 -1
- data/lib/rack/signature.rb +4 -0
- data/lib/rack/signature/build_message.rb +38 -7
- data/lib/rack/signature/build_post_body.rb +28 -0
- data/lib/rack/signature/deep_merge.rb +57 -0
- data/lib/rack/signature/sort_query_params.rb +45 -0
- data/lib/rack/signature/test_helpers.rb +19 -14
- data/lib/rack/signature/verify.rb +42 -11
- data/lib/rack/signature/version.rb +2 -2
- data/rack-signature.gemspec +1 -0
- data/test/array_of_hashes.json +32 -0
- data/test/build_message_test.rb +40 -4
- data/test/build_post_body_test.rb +24 -0
- data/test/data.json +61 -0
- data/test/deep_merge_test.rb +76 -0
- data/test/ordered_json_data.json +1 -0
- data/test/sort_query_params_test.rb +253 -0
- data/test/test_helper.rb +59 -0
- data/test/verify_test.rb +237 -57
- metadata +34 -4
data/test/test_helper.rb
CHANGED
@@ -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
|
data/test/verify_test.rb
CHANGED
@@ -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
|
6
|
+
describe "Verifying a signed requests" do
|
5
7
|
include Rack::Signature::TestHelpers
|
6
|
-
|
7
8
|
TOKEN = ::SecureRandom.hex(8)
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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,
|
19
|
+
let(:rack_signature) { Rack::Signature.new(app, klass_options) }
|
20
20
|
let(:mock_request) { Rack::MockRequest.new(rack_signature) }
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
45
|
-
|
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
|
-
|
49
|
-
|
50
|
-
|
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
|
55
|
-
let(:
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
|
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,
|
185
|
+
assert_equal 200, mock_response.status
|
66
186
|
end
|
67
187
|
|
68
|
-
it 'will
|
69
|
-
assert_equal 'Hello World',
|
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
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
85
|
-
|
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 '
|
89
|
-
assert_equal
|
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 '
|
93
|
-
|
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.
|
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:
|
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.
|
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:
|