Fix an issue with non-dict values in flow exchange.

The OAuth2 for Devices implementation picked up an incidental change that
failed to treat dicts and dict-like objects the same way. This is particularly
painful in environments like App Engine, where `self.request.params` is a
common dict-like value to pass in.

This restores the previous behavior, and adds a test.
This commit is contained in:
Craig Citro
2014-10-31 12:49:25 -07:00
parent dc8b40ef06
commit 486ee8139f
3 changed files with 35 additions and 5 deletions

View File

@@ -1767,10 +1767,10 @@ class OAuth2WebServerFlow(Flow):
Args:
code: string, dict or None. For a non-device flow, this is
either the response code as a string, or a dictionary of
query parameters to the redirect_uri. For a device flow,
this should be None.
code: string, a dict-like object, or None. For a non-device
flow, this is either the response code as a string, or a
dictionary of query parameters to the redirect_uri. For a
device flow, this should be None.
http: httplib2.Http, optional http instance to use when fetching
credentials.
device_flow_info: DeviceFlowInfo, return value from step1 in the
@@ -1793,7 +1793,7 @@ class OAuth2WebServerFlow(Flow):
if code is None:
code = device_flow_info.device_code
elif isinstance(code, dict):
elif not isinstance(code, basestring):
if 'code' not in code:
raise FlowExchangeError(code.get(
'error', 'No code was supplied in the query parameters.'))

View File

@@ -90,6 +90,7 @@ class HttpMockSequence(object):
"""
self._iterable = iterable
self.follow_redirects = True
self.requests = []
def request(self, uri,
method='GET',
@@ -98,6 +99,7 @@ class HttpMockSequence(object):
redirections=1,
connection_type=None):
resp, content = self._iterable.pop(0)
self.requests.append({'uri': uri, 'body': body, 'headers': headers})
if content == 'echo_request_headers':
content = headers
elif content == 'echo_request_headers_as_json':

View File

@@ -875,6 +875,34 @@ class OAuth2WebServerFlowTest(unittest.TestCase):
self.assertEqual('8xLOxBtZp8', credentials.refresh_token)
self.assertEqual('dummy_revoke_uri', credentials.revoke_uri)
def test_exchange_dictlike(self):
class FakeDict(object):
def __init__(self, d):
self.d = d
def __getitem__(self, name):
return self.d[name]
def __contains__(self, name):
return name in self.d
code = 'some random code'
not_a_dict = FakeDict({'code': code})
http = HttpMockSequence([
({'status': '200'},
"""{ "access_token":"SlAV32hkKG",
"expires_in":3600,
"refresh_token":"8xLOxBtZp8" }"""),
])
credentials = self.flow.step2_exchange(not_a_dict, http=http)
self.assertEqual('SlAV32hkKG', credentials.access_token)
self.assertNotEqual(None, credentials.token_expiry)
self.assertEqual('8xLOxBtZp8', credentials.refresh_token)
self.assertEqual('dummy_revoke_uri', credentials.revoke_uri)
request_code = urlparse.parse_qs(http.requests[0]['body'])['code'][0]
self.assertEqual(code, request_code)
def test_urlencoded_exchange_success(self):
http = HttpMockSequence([
({'status': '200'}, 'access_token=SlAV32hkKG&expires_in=3600'),