Skip to content

Commit 5dd09fd

Browse files
Woodyajoestump
authored andcommitted
fix for multi-valued parameters. they must be flattened into multiple k/v pairs rather than a single k to array pair
1 parent c040abc commit 5dd09fd

File tree

2 files changed

+75
-48
lines changed

2 files changed

+75
-48
lines changed

oauth2/__init__.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import hmac
3030
import binascii
3131
import httplib2
32+
from types import ListType
3233

3334
try:
3435
from urlparse import parse_qs, parse_qsl
@@ -309,7 +310,7 @@ def to_header(self, realm=''):
309310

310311
def to_postdata(self):
311312
"""Serialize as post data for a POST request."""
312-
return urllib.urlencode(self)
313+
return urllib.urlencode(self.get_parameter_list())
313314

314315
def to_url(self):
315316
"""Serialize as a URL for a GET request."""
@@ -322,9 +323,22 @@ def get_parameter(self, parameter):
322323

323324
return ret
324325

326+
def get_parameter_list(self):
327+
"""
328+
Get the request parameters as a tuple list (flattening out embedded lists),
329+
dict causes issues with urllib Request
330+
"""
331+
items = []
332+
for (k,v) in self.iteritems():
333+
if type(v) != ListType:
334+
items.append((k,v))
335+
else:
336+
items.extend([(k,x) for x in v])
337+
return items
338+
325339
def get_normalized_parameters(self):
326340
"""Return a string that contains the parameters that must be signed."""
327-
items = [(k, v) for k, v in self.items() if k != 'oauth_signature']
341+
items = [(k,v) for k,v in self.get_parameter_list() if k != 'oauth_signature']
328342
encoded_str = urllib.urlencode(sorted(items))
329343
# Encode signature parameters per Oauth Core 1.0 protocol
330344
# spec draft 7, section 3.6

tests/test_oauth.py

Lines changed: 59 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
"""
22
The MIT License
3-
3+
44
Copyright (c) 2009 Vic Fryzel
5-
5+
66
Permission is hereby granted, free of charge, to any person obtaining a copy
77
of this software and associated documentation files (the "Software"), to deal
88
in the Software without restriction, including without limitation the rights
99
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1010
copies of the Software, and to permit persons to whom the Software is
1111
furnished to do so, subject to the following conditions:
12-
12+
1313
The above copyright notice and this permission notice shall be included in
1414
all copies or substantial portions of the Software.
15-
15+
1616
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1717
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1818
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -61,7 +61,7 @@ def test_build_auth_header(self):
6161
self.assertEqual(header['WWW-Authenticate'], 'OAuth realm="%s"' %
6262
realm)
6363
self.assertEqual(len(header), 1)
64-
64+
6565
def test_escape(self):
6666
string = 'http://whatever.com/~someuser/?test=test&other=other'
6767
self.assert_('~' in oauth.escape(string))
@@ -83,7 +83,7 @@ def test_gen_verifier(self):
8383
def test_gen_timestamp(self):
8484
exp = int(time.time())
8585
now = oauth.generate_timestamp()
86-
self.assertEqual(exp, now)
86+
self.assertEqual(exp, now)
8787

8888
class TestConsumer(unittest.TestCase):
8989
def setUp(self):
@@ -252,7 +252,7 @@ def test_url(self):
252252

253253
req = oauth.Request(method, url1)
254254
self.assertEquals(req.url, exp1)
255-
255+
256256
req = oauth.Request(method, url2)
257257
self.assertEquals(req.url, exp2)
258258

@@ -270,10 +270,11 @@ def test_get_nonoauth_parameters(self):
270270
oauth_params = {
271271
'oauth_consumer': 'asdfasdfasdf'
272272
}
273-
273+
274274
other_params = {
275275
'foo': 'baz',
276-
'bar': 'foo'
276+
'bar': 'foo',
277+
'multi': ['FOO','BAR']
277278
}
278279

279280
params = oauth_params
@@ -301,10 +302,10 @@ def test_to_header(self):
301302
parts = value.split('OAuth ')
302303
vars = parts[1].split(', ')
303304
self.assertTrue(len(vars), (len(params) + 1))
304-
305+
305306
res = {}
306307
for v in vars:
307-
var, val = v.split('=')
308+
var, val = v.split('=')
308309
res[var] = urllib.unquote(val.strip('"'))
309310

310311
self.assertEquals(realm, res['realm'])
@@ -319,6 +320,7 @@ def test_to_postdata(self):
319320
realm = "http://sp.example.com/"
320321

321322
params = {
323+
'multi': ['FOO','BAR'],
322324
'oauth_version': "1.0",
323325
'oauth_nonce': "4572616e48616d6d65724c61686176",
324326
'oauth_timestamp': "137131200",
@@ -329,8 +331,11 @@ def test_to_postdata(self):
329331
}
330332

331333
req = oauth.Request("GET", realm, params)
332-
333-
self.assertEquals(params, dict(parse_qsl(req.to_postdata())))
334+
335+
flat = [('multi','FOO'),('multi','BAR')]
336+
del params['multi']
337+
flat.extend(params.items())
338+
self.assertEquals(flat, parse_qsl(req.to_postdata()))
334339

335340
def test_to_url(self):
336341
url = "http://sp.example.com/"
@@ -348,13 +353,13 @@ def test_to_url(self):
348353
req = oauth.Request("GET", url, params)
349354
exp = urlparse.urlparse("%s?%s" % (url, urllib.urlencode(params)))
350355
res = urlparse.urlparse(req.to_url())
351-
self.assertEquals(exp.scheme, res.scheme)
352-
self.assertEquals(exp.netloc, res.netloc)
353-
self.assertEquals(exp.path, res.path)
356+
self.assertEquals(exp.scheme, res.scheme)
357+
self.assertEquals(exp.netloc, res.netloc)
358+
self.assertEquals(exp.path, res.path)
354359

355360
a = parse_qs(exp.query)
356361
b = parse_qs(res.query)
357-
self.assertEquals(a, b)
362+
self.assertEquals(a, b)
358363

359364
def test_get_normalized_parameters(self):
360365
url = "http://sp.example.com/"
@@ -426,7 +431,7 @@ def test_sign_request(self):
426431
req = oauth.Request(method="GET", url=url, parameters=params)
427432

428433
methods = {
429-
'TQ6vGQ5A6IZn8dmeGB4+/Jl3EMI=': oauth.SignatureMethod_HMAC_SHA1(),
434+
'TQ6vGQ5A6IZn8dmeGB4+/Jl3EMI=': oauth.SignatureMethod_HMAC_SHA1(),
430435
'con-test-secret&tok-test-secret': oauth.SignatureMethod_PLAINTEXT()
431436
}
432437

@@ -447,17 +452,17 @@ def test_from_request(self):
447452
'oauth_token': "ad180jjd733klru7",
448453
'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
449454
}
450-
455+
451456
req = oauth.Request("GET", url, params)
452457
headers = req.to_header()
453458

454459
# Test from the headers
455460
req = oauth.Request.from_request("GET", url, headers)
456461
self.assertEquals(req.method, "GET")
457462
self.assertEquals(req.url, url)
458-
463+
459464
self.assertEquals(params, req.copy())
460-
465+
461466
# Test with bad OAuth headers
462467
bad_headers = {
463468
'Authorization' : 'OAuth this is a bad header'
@@ -469,7 +474,7 @@ def test_from_request(self):
469474
# Test getting from query string
470475
qs = urllib.urlencode(params)
471476
req = oauth.Request.from_request("GET", url, query_string=qs)
472-
477+
473478
exp = parse_qs(qs, keep_blank_values=False)
474479
for k, v in exp.iteritems():
475480
exp[k] = urllib.unquote(v[0])
@@ -492,7 +497,7 @@ def test_from_token_and_callback(self):
492497
'oauth_token': "ad180jjd733klru7",
493498
'oauth_signature': "wOJIO9A2W5mFwDgiDvZbTSMK%2FPY%3D",
494499
}
495-
500+
496501
tok = oauth.Token(key="tok-test-key", secret="tok-test-secret")
497502
req = oauth.Request.from_token_and_callback(tok)
498503
self.assertFalse('oauth_callback' in req)
@@ -507,7 +512,7 @@ def test_from_consumer_and_token(self):
507512

508513
tok = oauth.Token(key="tok-test-key", secret="tok-test-secret")
509514
con = oauth.Consumer(key="con-test-key", secret="con-test-secret")
510-
req = oauth.Request.from_consumer_and_token(con, token=tok,
515+
req = oauth.Request.from_consumer_and_token(con, token=tok,
511516
http_method="GET", http_url=url)
512517

513518
self.assertEquals(req['oauth_token'], tok.key)
@@ -532,10 +537,11 @@ def setUp(self):
532537
'oauth_nonce': "4572616e48616d6d65724c61686176",
533538
'oauth_timestamp': int(time.time()),
534539
'bar': 'blerg',
540+
'multi': ['FOO','BAR'],
535541
'foo': 59
536542
}
537543

538-
self.consumer = oauth.Consumer(key="consumer-key",
544+
self.consumer = oauth.Consumer(key="consumer-key",
539545
secret="consumer-secret")
540546
self.token = oauth.Token(key="token-key", secret="token-secret")
541547

@@ -549,7 +555,7 @@ def setUp(self):
549555
def test_init(self):
550556
server = oauth.Server(signature_methods={'HMAC-SHA1' : oauth.SignatureMethod_HMAC_SHA1()})
551557
self.assertTrue('HMAC-SHA1' in server.signature_methods)
552-
self.assertTrue(isinstance(server.signature_methods['HMAC-SHA1'],
558+
self.assertTrue(isinstance(server.signature_methods['HMAC-SHA1'],
553559
oauth.SignatureMethod_HMAC_SHA1))
554560

555561
server = oauth.Server()
@@ -558,15 +564,15 @@ def test_init(self):
558564
def test_add_signature_method(self):
559565
server = oauth.Server()
560566
res = server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
561-
self.assertTrue(len(res) == 1)
567+
self.assertTrue(len(res) == 1)
562568
self.assertTrue('HMAC-SHA1' in res)
563-
self.assertTrue(isinstance(res['HMAC-SHA1'],
569+
self.assertTrue(isinstance(res['HMAC-SHA1'],
564570
oauth.SignatureMethod_HMAC_SHA1))
565571

566572
res = server.add_signature_method(oauth.SignatureMethod_PLAINTEXT())
567-
self.assertTrue(len(res) == 2)
573+
self.assertTrue(len(res) == 2)
568574
self.assertTrue('PLAINTEXT' in res)
569-
self.assertTrue(isinstance(res['PLAINTEXT'],
575+
self.assertTrue(isinstance(res['PLAINTEXT'],
570576
oauth.SignatureMethod_PLAINTEXT))
571577

572578
def test_verify_request(self):
@@ -578,8 +584,10 @@ def test_verify_request(self):
578584

579585
self.assertTrue('bar' in parameters)
580586
self.assertTrue('foo' in parameters)
587+
self.assertTrue('multi' in parameters)
581588
self.assertEquals(parameters['bar'], 'blerg')
582589
self.assertEquals(parameters['foo'], 59)
590+
self.assertEquals(parameters['multi'], ['FOO','BAR'])
583591

584592
def test_no_version(self):
585593
url = "http://sp.example.com/"
@@ -588,10 +596,11 @@ def test_no_version(self):
588596
'oauth_nonce': "4572616e48616d6d65724c61686176",
589597
'oauth_timestamp': int(time.time()),
590598
'bar': 'blerg',
599+
'multi': ['FOO','BAR'],
591600
'foo': 59
592601
}
593602

594-
self.consumer = oauth.Consumer(key="consumer-key",
603+
self.consumer = oauth.Consumer(key="consumer-key",
595604
secret="consumer-secret")
596605
self.token = oauth.Token(key="token-key", secret="token-secret")
597606

@@ -616,10 +625,11 @@ def test_invalid_version(self):
616625
'oauth_nonce': "4572616e48616d6d65724c61686176",
617626
'oauth_timestamp': int(time.time()),
618627
'bar': 'blerg',
628+
'multi': ['foo','bar'],
619629
'foo': 59
620630
}
621631

622-
consumer = oauth.Consumer(key="consumer-key",
632+
consumer = oauth.Consumer(key="consumer-key",
623633
secret="consumer-secret")
624634
token = oauth.Token(key="token-key", secret="token-secret")
625635

@@ -633,7 +643,7 @@ def test_invalid_version(self):
633643
server = oauth.Server()
634644
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
635645

636-
self.assertRaises(oauth.Error, server.verify_request, request,
646+
self.assertRaises(oauth.Error, server.verify_request, request,
637647
consumer, token)
638648

639649
def test_invalid_signature_method(self):
@@ -644,10 +654,11 @@ def test_invalid_signature_method(self):
644654
'oauth_nonce': "4572616e48616d6d65724c61686176",
645655
'oauth_timestamp': int(time.time()),
646656
'bar': 'blerg',
657+
'multi': ['FOO','BAR'],
647658
'foo': 59
648659
}
649660

650-
consumer = oauth.Consumer(key="consumer-key",
661+
consumer = oauth.Consumer(key="consumer-key",
651662
secret="consumer-secret")
652663
token = oauth.Token(key="token-key", secret="token-secret")
653664

@@ -661,7 +672,7 @@ def test_invalid_signature_method(self):
661672
server = oauth.Server()
662673
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
663674

664-
self.assertRaises(oauth.Error, server.verify_request, request,
675+
self.assertRaises(oauth.Error, server.verify_request, request,
665676
consumer, token)
666677

667678
def test_missing_signature(self):
@@ -672,10 +683,11 @@ def test_missing_signature(self):
672683
'oauth_nonce': "4572616e48616d6d65724c61686176",
673684
'oauth_timestamp': int(time.time()),
674685
'bar': 'blerg',
686+
'multi': ['FOO','BAR'],
675687
'foo': 59
676688
}
677689

678-
consumer = oauth.Consumer(key="consumer-key",
690+
consumer = oauth.Consumer(key="consumer-key",
679691
secret="consumer-secret")
680692
token = oauth.Token(key="token-key", secret="token-secret")
681693

@@ -690,15 +702,15 @@ def test_missing_signature(self):
690702
server = oauth.Server()
691703
server.add_signature_method(oauth.SignatureMethod_HMAC_SHA1())
692704

693-
self.assertRaises(oauth.MissingSignature, server.verify_request,
705+
self.assertRaises(oauth.MissingSignature, server.verify_request,
694706
request, consumer, token)
695707

696708

697-
# Request Token: http://oauth-sandbox.sevengoslings.net/request_token
698-
# Auth: http://oauth-sandbox.sevengoslings.net/authorize
699-
# Access Token: http://oauth-sandbox.sevengoslings.net/access_token
700-
# Two-legged: http://oauth-sandbox.sevengoslings.net/two_legged
701-
# Three-legged: http://oauth-sandbox.sevengoslings.net/three_legged
709+
# Request Token: http://oauth-sandbox.sevengoslings.net/request_token
710+
# Auth: http://oauth-sandbox.sevengoslings.net/authorize
711+
# Access Token: http://oauth-sandbox.sevengoslings.net/access_token
712+
# Two-legged: http://oauth-sandbox.sevengoslings.net/two_legged
713+
# Three-legged: http://oauth-sandbox.sevengoslings.net/three_legged
702714
# Key: bd37aed57e15df53
703715
# Secret: 0e9e6413a9ef49510a4f68ed02cd
704716
class TestClient(unittest.TestCase):
@@ -714,18 +726,19 @@ class TestClient(unittest.TestCase):
714726
'two_legged': '/two_legged',
715727
'three_legged': '/three_legged'
716728
}
717-
729+
718730
consumer_key = 'bd37aed57e15df53'
719731
consumer_secret = '0e9e6413a9ef49510a4f68ed02cd'
720732
host = 'http://oauth-sandbox.sevengoslings.net'
721733

722734
def setUp(self):
723-
self.consumer = oauth.Consumer(key=self.consumer_key,
735+
self.consumer = oauth.Consumer(key=self.consumer_key,
724736
secret=self.consumer_secret)
725737

726738
self.body = {
727739
'foo': 'bar',
728740
'bar': 'foo',
741+
'multi': ['FOO','BAR'],
729742
'blah': 599999
730743
}
731744

@@ -735,7 +748,7 @@ def _uri(self, type):
735748
raise KeyError("%s is not a valid OAuth URI type." % type)
736749

737750
return "%s%s" % (self.host, uri)
738-
751+
739752
def test_access_token_get(self):
740753
"""Test getting an access token via GET."""
741754
client = oauth.Client(self.consumer, None)
@@ -757,7 +770,7 @@ def test_access_token_post(self):
757770
def _two_legged(self, method):
758771
client = oauth.Client(self.consumer, None)
759772

760-
return client.request(self._uri('two_legged'), method,
773+
return client.request(self._uri('two_legged'), method,
761774
body=urllib.urlencode(self.body))
762775

763776
def test_two_legged_post(self):

0 commit comments

Comments
 (0)