oauthenticator 0.1.4 → 1.0.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.
- checksums.yaml +4 -4
- data/.simplecov +1 -0
- data/README.md +118 -35
- data/Rakefile.rb +11 -0
- data/lib/oauthenticator.rb +5 -6
- data/lib/oauthenticator/config_methods.rb +62 -21
- data/lib/oauthenticator/faraday_signer.rb +66 -0
- data/lib/oauthenticator/parse_authorization.rb +81 -0
- data/lib/oauthenticator/{middleware.rb → rack_authenticator.rb} +37 -9
- data/lib/oauthenticator/signable_request.rb +340 -0
- data/lib/oauthenticator/signed_request.rb +123 -112
- data/lib/oauthenticator/version.rb +3 -1
- data/test/config_methods_test.rb +10 -7
- data/test/faraday_signer_test.rb +65 -0
- data/test/helper.rb +15 -3
- data/test/parse_authorization_test.rb +86 -0
- data/test/{oauthenticator_test.rb → rack_authenticator_test.rb} +264 -136
- data/test/signable_request_test.rb +653 -0
- data/test/signed_request_test.rb +12 -0
- data/test/test_config_methods.rb +67 -0
- metadata +26 -11
@@ -0,0 +1,653 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
proc { |p| $:.unshift(p) unless $:.any? { |lp| File.expand_path(lp) == p } }.call(File.expand_path('.', File.dirname(__FILE__)))
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
describe OAuthenticator::SignableRequest do
|
6
|
+
let :base_example_initialize_attrs do
|
7
|
+
{
|
8
|
+
:request_method => 'get',
|
9
|
+
:uri => 'http://example.com',
|
10
|
+
:media_type => 'text/plain',
|
11
|
+
:body => 'hi there',
|
12
|
+
}
|
13
|
+
end
|
14
|
+
let :example_initialize_attrs do
|
15
|
+
base_example_initialize_attrs.merge({
|
16
|
+
:consumer_key => 'a consumer key',
|
17
|
+
:consumer_secret => 'a consumer secret',
|
18
|
+
:signature_method => 'PLAINTEXT'
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
def example_request(attributes={})
|
23
|
+
OAuthenticator::SignableRequest.new(example_initialize_attrs.reject do |k,_|
|
24
|
+
attributes.keys.any? { |ak| ak.to_s == k.to_s }
|
25
|
+
end.merge(attributes))
|
26
|
+
end
|
27
|
+
|
28
|
+
def example_signed_request(authorization, attributes={})
|
29
|
+
attributes = attributes.merge(:authorization => authorization)
|
30
|
+
OAuthenticator::SignableRequest.new(base_example_initialize_attrs.reject do |k,_|
|
31
|
+
attributes.keys.any? { |ak| ak.to_s == k.to_s }
|
32
|
+
end.merge(attributes))
|
33
|
+
end
|
34
|
+
|
35
|
+
let :rsa_private_key do
|
36
|
+
%q(
|
37
|
+
-----BEGIN PRIVATE KEY-----
|
38
|
+
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
|
39
|
+
A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
|
40
|
+
7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
|
41
|
+
hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
|
42
|
+
X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
|
43
|
+
uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
|
44
|
+
rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
|
45
|
+
zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
|
46
|
+
qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
|
47
|
+
WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
|
48
|
+
cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
|
49
|
+
3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
|
50
|
+
AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
|
51
|
+
Lw03eHTNQghS0A==
|
52
|
+
-----END PRIVATE KEY-----
|
53
|
+
).strip.split("\n").map(&:strip).join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
describe 'initialize' do
|
57
|
+
describe 'default attributes' do
|
58
|
+
describe 'with any signature method' do
|
59
|
+
OAuthenticator::SignableRequest::SIGNATURE_METHODS.keys.each do |signature_method|
|
60
|
+
it("defaults to version 1.0 with #{signature_method}") do
|
61
|
+
request = example_request(:signature_method => signature_method)
|
62
|
+
assert_equal('1.0', request.protocol_params['oauth_version'])
|
63
|
+
end
|
64
|
+
it("lets you omit version if you really want to with #{signature_method}") do
|
65
|
+
request = example_request(:version => nil, :signature_method => signature_method)
|
66
|
+
assert(!request.protocol_params.key?('oauth_version'))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
describe 'not plaintext' do
|
71
|
+
it('generates nonces') do
|
72
|
+
nonces = 2.times.map do
|
73
|
+
example_request(:signature_method => 'HMAC-SHA1').protocol_params['oauth_nonce']
|
74
|
+
end
|
75
|
+
assert_equal(2, nonces.uniq.compact.size)
|
76
|
+
end
|
77
|
+
it 'generates timestamp' do
|
78
|
+
Timecop.freeze Time.at 1391021695
|
79
|
+
request = example_request(:signature_method => 'HMAC-SHA1')
|
80
|
+
assert_equal 1391021695.to_s, request.protocol_params['oauth_timestamp']
|
81
|
+
end
|
82
|
+
end
|
83
|
+
describe 'plaintext' do
|
84
|
+
it('does not generate nonces') do
|
85
|
+
request = example_request(:signature_method => 'PLAINTEXT')
|
86
|
+
assert(!request.protocol_params.key?('oauth_nonce'))
|
87
|
+
end
|
88
|
+
it 'does not generate timestamp' do
|
89
|
+
request = example_request(:signature_method => 'PLAINTEXT')
|
90
|
+
assert(!request.protocol_params.key?('oauth_timestapm'))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'accepts string and symbol' do
|
96
|
+
initialize_attr_variants = [
|
97
|
+
# by string
|
98
|
+
example_initialize_attrs.map { |k,v| {k.to_s => v} }.inject({}, &:update),
|
99
|
+
# by symbol
|
100
|
+
example_initialize_attrs.map { |k,v| {k.to_sym => v} }.inject({}, &:update),
|
101
|
+
# random mix
|
102
|
+
example_initialize_attrs.map { |k,v| {rand(2) == 0 ? k.to_s : k.to_sym => v} }.inject({}, &:update),
|
103
|
+
]
|
104
|
+
authorizations = initialize_attr_variants.map do |attrs|
|
105
|
+
OAuthenticator::SignableRequest.new(attrs).authorization
|
106
|
+
end
|
107
|
+
assert_equal(1, authorizations.uniq.size)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'checks type' do
|
111
|
+
assert_raises(TypeError) { OAuthenticator::SignableRequest.new("hello!") }
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'checks authorization type' do
|
115
|
+
assert_raises(TypeError) { example_request(:authorization => "hello!") }
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'does not allow protocol parameters to be specified when authorization is specified' do
|
119
|
+
OAuthenticator::SignableRequest::PROTOCOL_PARAM_KEYS.map do |key|
|
120
|
+
assert_raises(ArgumentError) do
|
121
|
+
example_signed_request({}, key => 'val')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'required attributes' do
|
127
|
+
it 'complains about missing required params' do
|
128
|
+
err = assert_raises(ArgumentError) { OAuthenticator::SignableRequest.new({}) }
|
129
|
+
%w(request_method uri media_type body consumer_key signature_method).each do |required|
|
130
|
+
assert_match /#{required}/, err.message
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe 'the example in 3.1' do
|
137
|
+
# a request with attributes from the oauth spec
|
138
|
+
def spec_request(attributes={})
|
139
|
+
example_request({
|
140
|
+
:request_method => 'POST',
|
141
|
+
:uri => 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b',
|
142
|
+
:media_type => 'application/x-www-form-urlencoded',
|
143
|
+
:body => 'c2&a3=2+q',
|
144
|
+
:consumer_key => '9djdj82h48djs9d2',
|
145
|
+
:token => 'kkk9d7dh3k39sjv7',
|
146
|
+
:consumer_secret => 'j49sk3j29djd',
|
147
|
+
:token_secret => 'dh893hdasih9',
|
148
|
+
:signature_method => 'HMAC-SHA1',
|
149
|
+
:timestamp => '137131201',
|
150
|
+
:nonce => '7d8f3e4a',
|
151
|
+
:version => nil,
|
152
|
+
:realm => "Example",
|
153
|
+
})
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'has the same signature base string' do
|
157
|
+
spec_signature_base = (
|
158
|
+
"POST&http%3A%2F%2Fexample.com%2Frequest&a2%3Dr%2520b%26a3%3D2%2520q" +
|
159
|
+
"%26a3%3Da%26b5%3D%253D%25253D%26c%2540%3D%26c2%3D%26oauth_consumer_" +
|
160
|
+
"key%3D9djdj82h48djs9d2%26oauth_nonce%3D7d8f3e4a%26oauth_signature_m" +
|
161
|
+
"ethod%3DHMAC-SHA1%26oauth_timestamp%3D137131201%26oauth_token%3Dkkk" +
|
162
|
+
"9d7dh3k39sjv7"
|
163
|
+
)
|
164
|
+
assert_equal(spec_signature_base, spec_request.send(:signature_base))
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'has the same normalized parameters' do
|
168
|
+
spec_normalized_request_params_string = (
|
169
|
+
"a2=r%20b&a3=2%20q&a3=a&b5=%3D%253D&c%40=&c2=&oauth_consumer_key=9dj" +
|
170
|
+
"dj82h48djs9d2&oauth_nonce=7d8f3e4a&oauth_signature_method=HMAC-SHA1" +
|
171
|
+
"&oauth_timestamp=137131201&oauth_token=kkk9d7dh3k39sjv7"
|
172
|
+
)
|
173
|
+
assert_equal(spec_normalized_request_params_string, spec_request.send(:normalized_request_params_string))
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'calculates authorization the same' do
|
178
|
+
# a keen observer may note that the signature is different than the one in the actual spec. the spec is
|
179
|
+
# in error - see http://www.rfc-editor.org/errata_search.php?rfc=5849
|
180
|
+
spec_authorization = OAuthenticator.parse_authorization(%q(OAuth realm="Example",
|
181
|
+
oauth_consumer_key="9djdj82h48djs9d2",
|
182
|
+
oauth_token="kkk9d7dh3k39sjv7",
|
183
|
+
oauth_signature_method="HMAC-SHA1",
|
184
|
+
oauth_timestamp="137131201",
|
185
|
+
oauth_nonce="7d8f3e4a",
|
186
|
+
oauth_signature="r6%2FTJjbCOr97%2F%2BUU0NsvSne7s5g%3D"
|
187
|
+
))
|
188
|
+
assert_equal(spec_authorization, spec_request.signed_protocol_params)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe '#authorization' do
|
193
|
+
it 'has the parameter name followed by an = and a quoted encoded value' do
|
194
|
+
many_characters = %q( !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~Ā)
|
195
|
+
authorization = example_request(:consumer_key => many_characters).authorization
|
196
|
+
# only alphas, numerics, and -._~ remain unencoded per 3.6
|
197
|
+
# hexes are uppercase
|
198
|
+
assert authorization.include?(%q(consumer_key="%20%21%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%C4%80"))
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'generally looks like: OAuth key="quoted-value", anotherkey="anothervalue"' do
|
202
|
+
assert_equal(%q(OAuth ) +
|
203
|
+
%q(oauth_consumer_key="a%20consumer%20key", ) +
|
204
|
+
%q(oauth_signature="a%2520consumer%2520secret%26", ) +
|
205
|
+
%q(oauth_signature_method="PLAINTEXT", ) +
|
206
|
+
%q(oauth_version="1.0"),
|
207
|
+
example_request.authorization
|
208
|
+
)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe 'signature' do
|
213
|
+
describe 'PLAINTEXT' do
|
214
|
+
it 'signs with the consumer and token secrets, encoded and &-joined' do
|
215
|
+
request = example_request(:token => 'a token', :token_secret => 'a token secret', :signature_method => 'PLAINTEXT')
|
216
|
+
assert_equal('a%20consumer%20secret&a%20token%20secret', request.signed_protocol_params['oauth_signature'])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe 'HMAC-SHA1' do
|
221
|
+
it 'signs with a HMAC-SHA1 digest of the signature base' do
|
222
|
+
request = example_request(
|
223
|
+
:token => 'a token',
|
224
|
+
:token_secret => 'a token secret',
|
225
|
+
:signature_method => 'HMAC-SHA1',
|
226
|
+
:nonce => 'a nonce',
|
227
|
+
:timestamp => 1397726597,
|
228
|
+
:hash_body? => false
|
229
|
+
)
|
230
|
+
assert_equal('rVKcy4CgAih1kv4HAMGiNnjmUJk=', request.signed_protocol_params['oauth_signature'])
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe 'RSA-SHA1' do
|
235
|
+
it 'signs with a RSA private key SHA1 signature' do
|
236
|
+
request = example_request(
|
237
|
+
:consumer_secret => rsa_private_key,
|
238
|
+
:token => 'a token',
|
239
|
+
:token_secret => 'a token secret',
|
240
|
+
:signature_method => 'RSA-SHA1',
|
241
|
+
:nonce => 'a nonce',
|
242
|
+
:timestamp => 1397726597,
|
243
|
+
:hash_body? => false
|
244
|
+
)
|
245
|
+
assert_equal(
|
246
|
+
"s3/TkrCJw54tOpsKUHkoQ9PeH1r4wB2fNb70XC2G1ef7Wb/dwwNUOhtjtpGMSDhmYQHzEPt0dAJ+PgeNs1O5NZJQB5JqdsmrhLS3ZdHx2iucxYvZSuDNi0GxaEepz5VS9rg+y5Gmep60BpAKhX0KGnkMY9HIhomTPSrYidAfDOE=",
|
247
|
+
request.signed_protocol_params['oauth_signature']
|
248
|
+
)
|
249
|
+
end
|
250
|
+
|
251
|
+
it 'ignores the token secret' do
|
252
|
+
request_attrs = {
|
253
|
+
:consumer_secret => rsa_private_key,
|
254
|
+
:token => 'a token',
|
255
|
+
:signature_method => 'RSA-SHA1',
|
256
|
+
:nonce => 'a nonce',
|
257
|
+
:timestamp => 1397726597,
|
258
|
+
}
|
259
|
+
request1 = example_request(request_attrs.merge(:token_secret => 'a token secret'))
|
260
|
+
request2 = example_request(request_attrs.merge(:token_secret => 'an entirely different token secret'))
|
261
|
+
assert_equal(request1.signature, request2.signature)
|
262
|
+
assert_equal(request1.authorization, request2.authorization)
|
263
|
+
end
|
264
|
+
|
265
|
+
describe 'with an invalid key' do
|
266
|
+
it 'errors' do
|
267
|
+
assert_raises(OpenSSL::PKey::RSAError) { example_request(:signature_method => 'RSA-SHA1').signature }
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
describe 'protocol_params' do
|
274
|
+
it 'includes given protocol params with an oauth_ prefix' do
|
275
|
+
OAuthenticator::SignableRequest::PROTOCOL_PARAM_KEYS.each do |param_key|
|
276
|
+
assert_equal(example_request(param_key => 'a value').protocol_params["oauth_#{param_key}"], 'a value')
|
277
|
+
end
|
278
|
+
end
|
279
|
+
it 'does not include a calculated signature' do
|
280
|
+
assert !example_request.protocol_params.key?('oauth_signature')
|
281
|
+
end
|
282
|
+
it 'does include the signature of a given authorization' do
|
283
|
+
assert_equal('a signature', example_signed_request('oauth_signature' => 'a signature').protocol_params['oauth_signature'])
|
284
|
+
end
|
285
|
+
it 'does include unknown parameters of a given authorization' do
|
286
|
+
assert_equal('bar', example_signed_request('foo' => 'bar').protocol_params['foo'])
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
describe 'signed_protocol_params' do
|
291
|
+
it 'includes a signature' do
|
292
|
+
assert_equal 'a%20consumer%20secret&', example_request.signed_protocol_params['oauth_signature']
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'has a different signature than the given authorization if the given authorization is wrong' do
|
296
|
+
request = example_signed_request({
|
297
|
+
'oauth_consumer_key' => 'a consumer key',
|
298
|
+
'oauth_signature' => 'wrong%20secret&',
|
299
|
+
'oauth_signature_method' => 'PLAINTEXT',
|
300
|
+
},
|
301
|
+
{:consumer_secret => 'a consumer secret'}
|
302
|
+
)
|
303
|
+
refute_equal(
|
304
|
+
request.protocol_params['oauth_signature'],
|
305
|
+
request.signed_protocol_params['oauth_signature']
|
306
|
+
)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
describe 'uri, per section 3.4.1.2' do
|
311
|
+
it 'lowercases scheme and host' do
|
312
|
+
[
|
313
|
+
'http://example.com/FooBar',
|
314
|
+
'Http://Example.com/FooBar',
|
315
|
+
'HTTP://EXAMPLE.cOM/FooBar',
|
316
|
+
].each do |uri|
|
317
|
+
assert_equal('http://example.com/FooBar', example_request(:uri => uri).send(:base_string_uri))
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'normalizes port' do
|
322
|
+
assert_equal('http://example.com/F', example_request(:uri => 'http://example.com/F').send(:base_string_uri))
|
323
|
+
assert_equal('http://example.com/F', example_request(:uri => 'http://example.com:80/F').send(:base_string_uri))
|
324
|
+
assert_equal('http://example.com:81/F', example_request(:uri => 'http://example.com:81/F').send(:base_string_uri))
|
325
|
+
assert_equal('https://example.com/F', example_request(:uri => 'https://example.com/F').send(:base_string_uri))
|
326
|
+
assert_equal('https://example.com/F', example_request(:uri => 'https://example.com:443/F').send(:base_string_uri))
|
327
|
+
assert_equal('https://example.com:444/F', example_request(:uri => 'https://example.com:444/F').send(:base_string_uri))
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'excludes query and fragment' do
|
331
|
+
assert_equal('http://example.com/FooBar', example_request(:uri => 'http://example.com/FooBar?foo=bar#foobar').send(:base_string_uri))
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
it 'accepts string or symbol request methods' do
|
336
|
+
{'GET' => [:get, :Get, :GET, 'GeT', 'get'], 'OPTIONS' => [:options, 'Options']}.each do |norm, variants|
|
337
|
+
variants.each do |request_method|
|
338
|
+
assert_equal(norm, example_request(:request_method => request_method).send(:normalized_request_method))
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
describe 'body' do
|
344
|
+
it 'takes a string' do
|
345
|
+
assert_equal('abody', example_request(:body => 'abody').send(:read_body))
|
346
|
+
end
|
347
|
+
it 'takes an IO' do
|
348
|
+
assert_equal('abody', example_request(:body => StringIO.new('abody')).send(:read_body))
|
349
|
+
end
|
350
|
+
it 'takes nil' do
|
351
|
+
assert_equal('', example_request(:body => nil).send(:read_body))
|
352
|
+
end
|
353
|
+
it 'rejects something else' do
|
354
|
+
assert_raises(TypeError) { example_request(:body => Object.new).send(:read_body) }
|
355
|
+
end
|
356
|
+
it 'calculates their authorization the same' do
|
357
|
+
request_io_body = example_request(:body => StringIO.new('abody'))
|
358
|
+
request_str_body = example_request(:body => 'abody')
|
359
|
+
assert_equal(request_io_body.authorization, request_str_body.authorization)
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
describe 'signature_base' do
|
364
|
+
it 'includes unrecognized authorization params when calculating signature base' do
|
365
|
+
authorization = %q(OAuth realm="Example",
|
366
|
+
oauth_foo="bar",
|
367
|
+
oauth_consumer_key="9djdj82h48djs9d2",
|
368
|
+
oauth_signature_method="HMAC-SHA1",
|
369
|
+
oauth_timestamp="137131201",
|
370
|
+
oauth_nonce="7d8f3e4a"
|
371
|
+
)
|
372
|
+
assert(example_signed_request(OAuthenticator.parse_authorization(authorization)).send(:signature_base).include?("oauth_foo%3Dbar"))
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'does include body in a formencoded request' do
|
376
|
+
assert(example_request(:media_type => 'application/x-www-form-urlencoded', :body => 'foo=bar').send(:signature_base).include?('foo'))
|
377
|
+
end
|
378
|
+
|
379
|
+
it 'does include body in a formencoded request with alternate capitalization' do
|
380
|
+
assert(example_request(:media_type => 'APPLICATION/X-WWW-FORM-URLENCODED', :body => 'foo=bar').send(:signature_base).include?('foo'))
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'does not include body in a non-formencoded request' do
|
384
|
+
assert(!example_request(:media_type => 'text/plain', :body => 'foo=bar').send(:signature_base).include?('foo'))
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
describe 'normalized request params' do
|
389
|
+
describe 'normalized request params string' do
|
390
|
+
# this is effectively tested by #authorization so we won't here
|
391
|
+
end
|
392
|
+
describe 'protocol params' do
|
393
|
+
it 'does not include realm with a new request' do
|
394
|
+
request = example_request(:realm => 'everywhere')
|
395
|
+
assert(!request.send(:normalized_request_params).any? { |k,v| k.downcase == 'realm' })
|
396
|
+
end
|
397
|
+
it 'does not include realm with a previously-signed request' do
|
398
|
+
request = example_signed_request('realm' => 'somewhere')
|
399
|
+
assert(!request.send(:normalized_request_params).any? { |k,v| k.downcase == 'realm' })
|
400
|
+
end
|
401
|
+
it 'does not include signature' do
|
402
|
+
request = example_signed_request('oauth_signature' => 'totallylegit', 'foo' => 'bar')
|
403
|
+
assert(!request.send(:normalized_request_params).any? { |k,v| k.downcase == 'oauth_signature' })
|
404
|
+
end
|
405
|
+
it 'does include all other given params' do
|
406
|
+
request = example_signed_request(
|
407
|
+
'realm' => 'somewhere',
|
408
|
+
'foo' => 'bar',
|
409
|
+
'oauth_signature' => 'totallylegit',
|
410
|
+
'oauth_timestamp' => '137131201'
|
411
|
+
)
|
412
|
+
[['foo', 'bar'], ['oauth_timestamp', '137131201']].each do |pair|
|
413
|
+
assert(request.send(:normalized_request_params).include?(pair))
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
describe 'query params' do
|
418
|
+
it 'goes into normalized request params' do
|
419
|
+
request = example_request(:uri => 'http://example.com/?a=b&c=d&e=&f')
|
420
|
+
[['a', 'b'], ['c', 'd'], ['e', ''], ['f', nil]].each do |pair|
|
421
|
+
assert(request.send(:normalized_request_params).include?(pair))
|
422
|
+
end
|
423
|
+
end
|
424
|
+
it 'is empty with no query' do
|
425
|
+
request = example_request(:uri => 'http://example.com/')
|
426
|
+
assert_equal([], request.send(:query_params))
|
427
|
+
end
|
428
|
+
it 'decodes a + sign' do
|
429
|
+
request = example_request(:uri => 'http://example.com/?a+key=a+value')
|
430
|
+
assert_equal([['a key', 'a value']], request.send(:query_params))
|
431
|
+
end
|
432
|
+
it 'decodes %-encoded' do
|
433
|
+
request = example_request(:uri => 'http://example.com/?a%20key=a%20value')
|
434
|
+
assert_equal([['a key', 'a value']], request.send(:query_params))
|
435
|
+
end
|
436
|
+
it 'includes form encoded keys with an = sign and no value' do
|
437
|
+
request = example_request(:uri => 'http://example.com/?a=')
|
438
|
+
assert_equal([['a', '']], request.send(:query_params))
|
439
|
+
end
|
440
|
+
it 'includes form encoded keys with no = sign and no value' do
|
441
|
+
request = example_request(:uri => 'http://example.com/?a')
|
442
|
+
assert_equal([['a', nil]], request.send(:query_params))
|
443
|
+
end
|
444
|
+
end
|
445
|
+
describe 'entity params' do
|
446
|
+
it 'goes into normalized request params' do
|
447
|
+
request = example_request(:body => 'a=b&c=d&e=&f', :media_type => 'application/x-www-form-urlencoded')
|
448
|
+
[['a', 'b'], ['c', 'd'], ['e', ''], ['f', nil]].each do |pair|
|
449
|
+
assert(request.send(:normalized_request_params).include?(pair))
|
450
|
+
end
|
451
|
+
end
|
452
|
+
it 'includes all form encoded params' do
|
453
|
+
request = example_request(:body => 'a=b&c=d', :media_type => 'application/x-www-form-urlencoded')
|
454
|
+
assert_equal([['a', 'b'], ['c', 'd']], request.send(:entity_params))
|
455
|
+
end
|
456
|
+
it 'includes no non-form encoded params' do
|
457
|
+
request = example_request(:body => 'a=b&c=d', :media_type => 'text/plain')
|
458
|
+
assert_equal([], request.send(:entity_params))
|
459
|
+
end
|
460
|
+
it 'does not parse nested params' do
|
461
|
+
request = example_request(:body => 'a[b]=c', :media_type => 'application/x-www-form-urlencoded')
|
462
|
+
assert_equal([['a[b]', 'c']], request.send(:entity_params))
|
463
|
+
end
|
464
|
+
it 'decodes a + sign' do
|
465
|
+
request = example_request(:body => 'a+key=a+value', :media_type => 'application/x-www-form-urlencoded')
|
466
|
+
assert_equal([['a key', 'a value']], request.send(:entity_params))
|
467
|
+
end
|
468
|
+
it 'decodes %-encoded keys and values' do
|
469
|
+
request = example_request(:body => 'a%20key=a%20value', :media_type => 'application/x-www-form-urlencoded')
|
470
|
+
assert_equal([['a key', 'a value']], request.send(:entity_params))
|
471
|
+
end
|
472
|
+
it 'includes form encoded keys with an = sign and no value' do
|
473
|
+
request = example_request(:body => 'a=', :media_type => 'application/x-www-form-urlencoded')
|
474
|
+
assert_equal([['a', '']], request.send(:entity_params))
|
475
|
+
end
|
476
|
+
it 'includes form encoded keys with no = sign and no value' do
|
477
|
+
request = example_request(:body => 'a', :media_type => 'application/x-www-form-urlencoded')
|
478
|
+
assert_equal([['a', nil]], request.send(:entity_params))
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
describe 'body hash' do
|
484
|
+
describe 'default inclusion' do
|
485
|
+
it 'includes by default with non-form-encoded and HMAC-SHA1' do
|
486
|
+
request = example_request(:media_type => 'text/plain', :body => 'foo=bar', :signature_method => 'HMAC-SHA1')
|
487
|
+
assert_equal('L7j0ARXdHmlcviPU+Xzlsftpfu4=', request.protocol_params['oauth_body_hash'])
|
488
|
+
end
|
489
|
+
it 'includes by default with non-form-encoded and RSA-SHA1' do
|
490
|
+
request = example_request(:media_type => 'text/plain', :body => 'foo=bar', :signature_method => 'RSA-SHA1', :consumer_secret => rsa_private_key)
|
491
|
+
assert_equal('L7j0ARXdHmlcviPU+Xzlsftpfu4=', request.protocol_params['oauth_body_hash'])
|
492
|
+
end
|
493
|
+
it 'does not include by default with non-form-encoded and PLAINTEXT' do
|
494
|
+
request = example_request(:media_type => 'text/plain', :body => 'foo=bar', :signature_method => 'PLAINTEXT')
|
495
|
+
assert(!request.protocol_params.key?('oauth_body_hash'))
|
496
|
+
end
|
497
|
+
it 'does not include by default with form-encoded and HMAC-SHA1' do
|
498
|
+
request = example_request(:media_type => 'application/x-www-form-urlencoded', :body => 'foo=bar', :signature_method => 'HMAC-SHA1')
|
499
|
+
assert(!request.protocol_params.key?('oauth_body_hash'))
|
500
|
+
end
|
501
|
+
it 'does not include by default with form-encoded and RSA-SHA1' do
|
502
|
+
request = example_request(:media_type => 'application/x-www-form-urlencoded', :body => 'foo=bar', :signature_method => 'RSA-SHA1', :consumer_secret => rsa_private_key)
|
503
|
+
assert(!request.protocol_params.key?('oauth_body_hash'))
|
504
|
+
end
|
505
|
+
it 'does not include by default with form-encoded and PLAINTEXT' do
|
506
|
+
request = example_request(:media_type => 'application/x-www-form-urlencoded', :body => 'foo=bar', :signature_method => 'PLAINTEXT')
|
507
|
+
assert(!request.protocol_params.key?('oauth_body_hash'))
|
508
|
+
end
|
509
|
+
end
|
510
|
+
it 'respects the :hash_body? option' do
|
511
|
+
attributes = {:media_type => 'text/plain', :body => 'foo=bar', :signature_method => 'HMAC-SHA1'}
|
512
|
+
# ensure these would generate the hash by default, without :hash_body?
|
513
|
+
assert_equal('L7j0ARXdHmlcviPU+Xzlsftpfu4=', example_request(attributes).protocol_params['oauth_body_hash'])
|
514
|
+
assert(!example_request(attributes.merge(:hash_body? => false)).protocol_params.key?('oauth_body_hash'))
|
515
|
+
assert_equal('L7j0ARXdHmlcviPU+Xzlsftpfu4=', example_request(attributes.merge(:hash_body? => true)).protocol_params['oauth_body_hash'])
|
516
|
+
end
|
517
|
+
it 'does not generate a body hash when given a authorization' do
|
518
|
+
assert(!example_signed_request({}).protocol_params.key?('oauth_body_hash'))
|
519
|
+
end
|
520
|
+
|
521
|
+
describe '#body_hash' do
|
522
|
+
it 'is the same as goes in protocol params when generated' do
|
523
|
+
request = example_request(:media_type => 'text/plain', :body => 'foo=bar', :signature_method => 'HMAC-SHA1')
|
524
|
+
assert_equal(request.protocol_params['oauth_body_hash'], request.body_hash)
|
525
|
+
end
|
526
|
+
it 'matches the given protocol params for a valid request' do
|
527
|
+
request = example_signed_request(
|
528
|
+
{'oauth_body_hash' => 'Lve95gjOVATpfV8EL5X4nxwjKHE=', 'oauth_signature_method' => 'HMAC-SHA1'},
|
529
|
+
:body => 'Hello World!', :media_type => 'text/plain'
|
530
|
+
)
|
531
|
+
assert_equal(request.protocol_params['oauth_body_hash'], request.body_hash)
|
532
|
+
end
|
533
|
+
it 'is different than the given protocol params for an invalid request' do
|
534
|
+
request = example_signed_request(
|
535
|
+
{'oauth_body_hash' => 'helloooooo?=', 'oauth_signature_method' => 'HMAC-SHA1'},
|
536
|
+
:body => 'Hello World!', :media_type => 'text/plain'
|
537
|
+
)
|
538
|
+
refute_equal(request.protocol_params['oauth_body_hash'], request.body_hash)
|
539
|
+
end
|
540
|
+
it 'returns nil for an unsupported signature method' do
|
541
|
+
assert_equal(nil, example_request(:signature_method => 'PLAINTEXT').body_hash)
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
describe 'example appendix A1' do
|
546
|
+
let :request do
|
547
|
+
OAuthenticator::SignableRequest.new({
|
548
|
+
:request_method => 'PUT',
|
549
|
+
:uri => 'http://www.example.com/resource',
|
550
|
+
:media_type => 'text/plain',
|
551
|
+
:body => 'Hello World!',
|
552
|
+
:signature_method => 'HMAC-SHA1',
|
553
|
+
:token => "token",
|
554
|
+
:consumer_key => "consumer",
|
555
|
+
:timestamp => "1236874236",
|
556
|
+
:nonce => "10369470270925",
|
557
|
+
})
|
558
|
+
end
|
559
|
+
it 'has the same oauth body hash' do
|
560
|
+
assert_equal('Lve95gjOVATpfV8EL5X4nxwjKHE=', request.signed_protocol_params['oauth_body_hash'])
|
561
|
+
end
|
562
|
+
it 'has the same signature base' do
|
563
|
+
assert_equal(
|
564
|
+
%q(PUT&http%3A%2F%2Fwww.example.com%2Fresource&oauth_body_hash%3D) +
|
565
|
+
%q(Lve95gjOVATpfV8EL5X4nxwjKHE%253D%26oauth_consumer_key%3Dconsum) +
|
566
|
+
%q(er%26oauth_nonce%3D10369470270925%26oauth_signature_method%3DH) +
|
567
|
+
%q(MAC-SHA1%26oauth_timestamp%3D1236874236%26oauth_token%3Dtoken%) +
|
568
|
+
%q(26oauth_version%3D1.0),
|
569
|
+
request.send(:signature_base)
|
570
|
+
)
|
571
|
+
end
|
572
|
+
end
|
573
|
+
describe 'example appendix A2' do
|
574
|
+
let :request do
|
575
|
+
OAuthenticator::SignableRequest.new({
|
576
|
+
:request_method => 'GET',
|
577
|
+
:uri => 'http://www.example.com/resource',
|
578
|
+
:media_type => nil,
|
579
|
+
:body => nil,
|
580
|
+
:signature_method => 'HMAC-SHA1',
|
581
|
+
:token => "token",
|
582
|
+
:consumer_key => "consumer",
|
583
|
+
:timestamp => "1238395022",
|
584
|
+
:nonce => "8628868109991",
|
585
|
+
})
|
586
|
+
end
|
587
|
+
it 'has the same oauth body hash' do
|
588
|
+
assert_equal('2jmj7l5rSw0yVb/vlWAYkK/YBwk=', request.signed_protocol_params['oauth_body_hash'])
|
589
|
+
end
|
590
|
+
it 'has the same signature base' do
|
591
|
+
assert_equal(
|
592
|
+
%q(GET&http%3A%2F%2Fwww.example.com%2Fresource&oauth_body_hash%3D2jmj7) +
|
593
|
+
%q(l5rSw0yVb%252FvlWAYkK%252FYBwk%253D%26oauth_consumer_key%3Dconsumer) +
|
594
|
+
%q(%26oauth_nonce%3D8628868109991%26oauth_signature_method%3DHMAC-SHA1) +
|
595
|
+
%q(%26oauth_timestamp%3D1238395022%26oauth_token%3Dtoken%26oauth_versi) +
|
596
|
+
%q(on%3D1.0),
|
597
|
+
request.send(:signature_base)
|
598
|
+
)
|
599
|
+
end
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
it 'reproduces a successful OAuth example GET (lifted from simple oauth)' do
|
604
|
+
request = OAuthenticator::SignableRequest.new(
|
605
|
+
:request_method => :get,
|
606
|
+
:uri => 'http://photos.example.net/photos',
|
607
|
+
:media_type => 'application/x-www-form-urlencoded',
|
608
|
+
:body => 'file=vacaction.jpg&size=original',
|
609
|
+
:consumer_key => 'dpf43f3p2l4k3l03',
|
610
|
+
:consumer_secret => rsa_private_key,
|
611
|
+
:nonce => '13917289812797014437',
|
612
|
+
:signature_method => 'RSA-SHA1',
|
613
|
+
:timestamp => '1196666512'
|
614
|
+
)
|
615
|
+
expected_protocol_params = {
|
616
|
+
"oauth_consumer_key" => "dpf43f3p2l4k3l03",
|
617
|
+
"oauth_nonce" => "13917289812797014437",
|
618
|
+
"oauth_signature" => "jvTp/wX1TYtByB1m+Pbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2/9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW//e+RinhejgCuzoH26dyF8iY2ZZ/5D1ilgeijhV/vBka5twt399mXwaYdCwFYE=",
|
619
|
+
"oauth_signature_method" => "RSA-SHA1",
|
620
|
+
"oauth_timestamp" => "1196666512",
|
621
|
+
"oauth_version" => "1.0",
|
622
|
+
}
|
623
|
+
|
624
|
+
assert_equal(expected_protocol_params, request.signed_protocol_params)
|
625
|
+
end
|
626
|
+
|
627
|
+
it 'reproduces a successful OAuth example GET (lifted from simple oauth)' do
|
628
|
+
request = OAuthenticator::SignableRequest.new(
|
629
|
+
:request_method => :get,
|
630
|
+
:uri => 'http://host.net/resource?name=value',
|
631
|
+
:media_type => 'application/x-www-form-urlencoded',
|
632
|
+
:body => 'name=value',
|
633
|
+
:consumer_key => 'abcd',
|
634
|
+
:consumer_secret => 'efgh',
|
635
|
+
:token => 'ijkl',
|
636
|
+
:token_secret => 'mnop',
|
637
|
+
:nonce => 'oLKtec51GQy',
|
638
|
+
:signature_method => 'PLAINTEXT',
|
639
|
+
:timestamp => '1286977095'
|
640
|
+
)
|
641
|
+
expected_protocol_params = {
|
642
|
+
"oauth_consumer_key" => "abcd",
|
643
|
+
"oauth_nonce" => "oLKtec51GQy",
|
644
|
+
"oauth_signature" => "efgh&mnop",
|
645
|
+
"oauth_signature_method" => "PLAINTEXT",
|
646
|
+
"oauth_timestamp" => "1286977095",
|
647
|
+
"oauth_token" => "ijkl",
|
648
|
+
"oauth_version" => "1.0"
|
649
|
+
}
|
650
|
+
|
651
|
+
assert_equal(expected_protocol_params, request.signed_protocol_params)
|
652
|
+
end
|
653
|
+
end
|