Skip to content

Commit e737d78

Browse files
humitosagjohnson
authored andcommitted
Do not convert to bytes the refresh_token (#3273)
* Do not convert to bytes the `refresh_token` When the body is encoded, a mix of bytes and unicode happens and it fails at this line https://github.com/requests/requests-oauthlib/blob/c472e6bf0468f1bd52b70a64e9aac92bee41c491/requests_oauthlib/oauth2_session.py#L285 (Pdb) body u'grant_type=refresh_token&allow_redirects=True&refresh_token=b%27z9BSRKqf2QD4pcDhqj%27&client_id=CLIENT_ID&client_secret=CLIENT_SECRET' (note those weird `%27` in the string) * Handle OAuth specific exception properly at `paginate` * Do not convert the `acess_token` to bytes * Fix lint :( * Refactor `paginate` to handle exception in Bitbucket and Github `paginate` method is shared between both services and each service implement `get_next_url_to_paginate` and `get_paginated_results`. * Names and attributes fixes
1 parent 5a4fbc9 commit e737d78

File tree

3 files changed

+57
-27
lines changed

3 files changed

+57
-27
lines changed

readthedocs/oauth/services/base.py

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
"""OAuth utility functions"""
1+
"""OAuth utility functions."""
22

33
from __future__ import absolute_import
4-
from builtins import str
54
from builtins import object
65
import logging
76
from datetime import datetime
87

98
from django.conf import settings
109
from requests_oauthlib import OAuth2Session
10+
from requests.exceptions import RequestException
11+
from oauthlib.oauth2.rfc6749.errors import InvalidClientIdError
1112
from allauth.socialaccount.models import SocialAccount
1213

1314

@@ -69,13 +70,13 @@ def create_session(self):
6970
return None
7071

7172
token_config = {
72-
'access_token': str(token.token),
73+
'access_token': token.token,
7374
'token_type': 'bearer',
7475
}
7576
if token.expires_at is not None:
7677
token_expires = (token.expires_at - datetime.now()).total_seconds()
7778
token_config.update({
78-
'refresh_token': str(token.token_secret),
79+
'refresh_token': token.token_secret,
7980
'expires_in': token_expires,
8081
})
8182

@@ -114,6 +115,34 @@ def _updater(data):
114115

115116
return _updater
116117

118+
def paginate(self, url):
119+
"""Recursively combine results from service's pagination.
120+
121+
:param url: start url to get the data from.
122+
:type url: unicode
123+
"""
124+
try:
125+
resp = self.get_session().get(url)
126+
next_url = self.get_next_url_to_paginate(resp)
127+
results = self.get_paginated_results(resp)
128+
if next_url:
129+
results.extend(self.paginate(next_url))
130+
return results
131+
# Catch specific exception related to OAuth
132+
except InvalidClientIdError:
133+
log.error('access_token or refresh_token failed: %s', url)
134+
raise Exception('You should reconnect your account')
135+
# Catch exceptions with request or deserializing JSON
136+
except (RequestException, ValueError):
137+
# Response data should always be JSON, still try to log if not though
138+
try:
139+
debug_data = resp.json()
140+
except ValueError:
141+
debug_data = resp.content
142+
log.debug('paginate failed at %s with response: %s', url, debug_data)
143+
finally:
144+
return []
145+
117146
def sync(self):
118147
raise NotImplementedError
119148

@@ -124,6 +153,22 @@ def create_repository(self, fields, privacy=DEFAULT_PRIVACY_LEVEL,
124153
def create_organization(self, fields):
125154
raise NotImplementedError
126155

156+
def get_next_url_to_paginate(self, response):
157+
"""Return the next url to feed the `paginate` method.
158+
159+
:param response: response from where to get the `next_url` attribute
160+
:type response: requests.Response
161+
"""
162+
raise NotImplementedError
163+
164+
def get_paginated_results(self, response):
165+
"""Return the results for the current response/page.
166+
167+
:param response: response from where to get the results.
168+
:type response: requests.Response
169+
"""
170+
raise NotImplementedError
171+
127172
def setup_webhook(self, project):
128173
raise NotImplementedError
129174

readthedocs/oauth/services/bitbucket.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -164,18 +164,11 @@ def create_organization(self, fields):
164164
organization.save()
165165
return organization
166166

167-
def paginate(self, url):
168-
"""Recursively combine results from Bitbucket pagination
167+
def get_next_url_to_paginate(self, response):
168+
return response.json().get('next')
169169

170-
:param url: start url to get the data from.
171-
"""
172-
resp = self.get_session().get(url)
173-
data = resp.json()
174-
results = data.get('values', [])
175-
next_url = data.get('next')
176-
if next_url:
177-
results.extend(self.paginate(next_url))
178-
return results
170+
def get_paginated_results(self, response):
171+
return response.json().get('values', [])
179172

180173
def get_webhook_data(self, project, integration):
181174
"""Get webhook JSON data to post to the API"""

readthedocs/oauth/services/github.py

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,11 @@ def create_organization(self, fields):
148148
organization.save()
149149
return organization
150150

151-
def paginate(self, url):
152-
"""Combines return from GitHub pagination
151+
def get_next_url_to_paginate(self, response):
152+
return response.links.get('next', {}).get('url')
153153

154-
:param url: start url to get the data from.
155-
156-
See https://developer.github.com/v3/#pagination
157-
"""
158-
resp = self.get_session().get(url)
159-
result = resp.json()
160-
next_url = resp.links.get('next', {}).get('url')
161-
if next_url:
162-
result.extend(self.paginate(next_url))
163-
return result
154+
def get_paginated_results(self, response):
155+
return response.json()
164156

165157
def get_webhook_data(self, project, integration):
166158
"""Get webhook JSON data to post to the API"""

0 commit comments

Comments
 (0)