Skip to content

Commit 6e217c3

Browse files
committed
Refresh OAuth2 tokens when expired
The importer was raising 403 errors when the OAuth2 token expired. This patch refreshes the token and retries to fetch again the data. Signed-off-by: Santiago Dueñas <sduenas@bitergia.com>
1 parent 303b56a commit 6e217c3

3 files changed

Lines changed: 59 additions & 24 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
title: OAuth2 token refreshing
3+
category: added
4+
author: Santiago Dueñas <sduenas@bitergia.com>
5+
issue: null
6+
notes: >
7+
OAuth2 token will be refreshed when they expired.

sortinghat/core/importer/backends/eclipse.py

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,9 +206,14 @@ class EclipseFoundationAPIClient:
206206
ECLIPSE_API_URL = "https://api.eclipse.org"
207207
ECLIPSE_ACCOUNTS_URL = "https://accounts.eclipse.org"
208208
OAUTH_TOKEN_ENDPOINT = "https://accounts.eclipse.org/oauth2/token"
209+
ECLIPSE_SCOPE = "eclipsefdn_view_all_profiles"
210+
211+
MAX_RETRIES = 3
209212

210213
def __init__(self):
211214
self.token = None
215+
self.user_id = None
216+
self.password = None
212217

213218
def login(self, user_id, password):
214219
"""Login on the Eclipse platform.
@@ -217,10 +222,12 @@ def login(self, user_id, password):
217222
"eclipsefdn_view_all_profiles" that will allow us to
218223
fetch all the info about profiles/identities.
219224
"""
225+
self.user_id = user_id
226+
self.password = password
220227
self.token = self._authenticate(
221-
user_id,
222-
password,
223-
"eclipsefdn_view_all_profiles",
228+
self.user_id,
229+
self.password,
230+
self.ECLIPSE_SCOPE,
224231
)
225232

226233
def logout(self):
@@ -281,8 +288,7 @@ def _fetch(self, url, params=None):
281288
"""Generic query to Eclipse usr API."""
282289

283290
try:
284-
response = requests.get(url, params=params, auth=self.token)
285-
response.raise_for_status()
291+
data = self._fetch_retry(url, params)
286292
except requests.exceptions.HTTPError as error:
287293
# Ignore 5xx errors
288294
if 500 <= error.response.status_code < 600:
@@ -296,7 +302,41 @@ def _fetch(self, url, params=None):
296302
else:
297303
raise error
298304

299-
return response.json()
305+
return data
306+
307+
def _fetch_retry(self, url, params=None):
308+
"""Fetch URL retrying in case of 403 or 500 errors.
309+
310+
When getting a 403 error, the method will try to authenticate
311+
again in case the OAuth2 token has expired.
312+
"""
313+
retries = 0
314+
max_retries = self.MAX_RETRIES
315+
316+
while retries < max_retries:
317+
response = requests.get(url, params=params, auth=self.token)
318+
319+
if response.status_code == 200:
320+
return response.json()
321+
elif response.status_code == 403:
322+
# Refresh token if needed and try again
323+
if self.token.expires_at <= datetime_utcnow():
324+
self.token = self._authenticate(
325+
self.user_id,
326+
self.password,
327+
self.ECLIPSE_SCOPE,
328+
)
329+
retries += 1
330+
elif 500 <= response.status_code < 600:
331+
# Errors could have been related to server overloading
332+
retries += 1
333+
else:
334+
response.raise_for_status()
335+
336+
response = requests.get(url, params=params, auth=self.token)
337+
response.raise_for_status()
338+
339+
return response
300340

301341
def _authenticate(self, client_id, client_secret, scope):
302342
"""Authenticate using OAuth2.

tests/test_eclipse.py

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,15 +67,17 @@ def request_callback(request, uri, headers):
6767
# John Smith profile pages
6868
httpretty.register_uri(httpretty.GET,
6969
ECLIPSE_API_URL + "/account/profile/jsmith",
70-
body=read_file('data/eclipse_jsmith.json'))
70+
body=read_file('data/eclipse_jsmith.json') if not raise_5xx_errors else "",
71+
status=200 if not raise_5xx_errors else 500)
7172
httpretty.register_uri(httpretty.GET,
7273
ECLIPSE_API_URL + "/account/profile/jsmith/employment-history",
7374
body=read_file('data/eclipse_jsmith_employment.json'))
7475

7576
# John Doe profile pages
7677
httpretty.register_uri(httpretty.GET,
7778
ECLIPSE_API_URL + "/account/profile/jdoe",
78-
body=read_file('data/eclipse_jdoe.json'))
79+
body=read_file('data/eclipse_jdoe.json') if not raise_5xx_errors else "",
80+
status=200 if not raise_5xx_errors else 500)
7981
httpretty.register_uri(httpretty.GET,
8082
ECLIPSE_API_URL + "/account/profile/jdoe/employment-history",
8183
body=read_file('data/eclipse_jdoe_employment.json'))
@@ -86,22 +88,8 @@ def request_callback(request, uri, headers):
8688
body=read_file('data/eclipse_jrae.json'))
8789
httpretty.register_uri(httpretty.GET,
8890
ECLIPSE_API_URL + "/account/profile/jrae/employment-history",
89-
body=read_file('data/eclipse_jrae_employment.json'))
90-
91-
# Overwrite jsmith and jdoe profile replies to raise 5xx errors
92-
if raise_5xx_errors:
93-
httpretty.register_uri(httpretty.GET,
94-
ECLIPSE_API_URL + "/account/profile/jsmith",
95-
body="",
96-
status=500)
97-
httpretty.register_uri(httpretty.GET,
98-
ECLIPSE_API_URL + "/account/profile/jdoe",
99-
body="",
100-
status=504)
101-
httpretty.register_uri(httpretty.GET,
102-
ECLIPSE_API_URL + "/account/profile/jrae/employment-history",
103-
body="",
104-
status=504)
91+
body=read_file('data/eclipse_jrae_employment.json') if not raise_5xx_errors else "",
92+
status=200 if not raise_5xx_errors else 500)
10593

10694
return requests, bodies
10795

0 commit comments

Comments
 (0)