Skip to content

Commit 5d251f3

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 5d251f3

3 files changed

Lines changed: 60 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: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -64,18 +64,21 @@ def request_callback(request, uri, headers):
6464
for _ in bodies
6565
])
6666

67+
6768
# John Smith profile pages
6869
httpretty.register_uri(httpretty.GET,
6970
ECLIPSE_API_URL + "/account/profile/jsmith",
70-
body=read_file('data/eclipse_jsmith.json'))
71+
body=read_file('data/eclipse_jsmith.json') if not raise_5xx_errors else "",
72+
status=200 if not raise_5xx_errors else 500)
7173
httpretty.register_uri(httpretty.GET,
7274
ECLIPSE_API_URL + "/account/profile/jsmith/employment-history",
7375
body=read_file('data/eclipse_jsmith_employment.json'))
7476

7577
# John Doe profile pages
7678
httpretty.register_uri(httpretty.GET,
7779
ECLIPSE_API_URL + "/account/profile/jdoe",
78-
body=read_file('data/eclipse_jdoe.json'))
80+
body=read_file('data/eclipse_jdoe.json') if not raise_5xx_errors else "",
81+
status=200 if not raise_5xx_errors else 500)
7982
httpretty.register_uri(httpretty.GET,
8083
ECLIPSE_API_URL + "/account/profile/jdoe/employment-history",
8184
body=read_file('data/eclipse_jdoe_employment.json'))
@@ -86,22 +89,8 @@ def request_callback(request, uri, headers):
8689
body=read_file('data/eclipse_jrae.json'))
8790
httpretty.register_uri(httpretty.GET,
8891
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)
92+
body=read_file('data/eclipse_jrae_employment.json') if not raise_5xx_errors else "",
93+
status=200 if not raise_5xx_errors else 500)
10594

10695
return requests, bodies
10796

0 commit comments

Comments
 (0)