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