1414import time
1515import uuid
1616from typing import List , Optional , Union , Tuple , Iterable
17+ from collections .abc import MutableMapping
1718
1819import cla
1920import falcon
@@ -207,7 +208,7 @@ def user_from_session(self, request, get_redirect_url):
207208 fn = "github_models.user_from_session"
208209 cla .log .debug (f"{ fn } - loading session from request: { request } ..." )
209210 session = self ._get_request_session (request )
210- cla .log .debug (f"{ fn } - session: { session } " )
211+ cla .log .debug (f"{ fn } - session loaded (keys= { list ( session . keys ()) } ) " )
211212
212213 # We can already have token in the session
213214 if "github_oauth2_token" in session :
@@ -222,7 +223,7 @@ def user_from_session(self, request, get_redirect_url):
222223 authorization_url , csrf_token = self .get_authorization_url_and_state (None , None , None , ["user:email" ], state = 'user-from-session' )
223224 cla .log .debug (f"{ fn } - obtained GitHub OAuth2 state from authorization - storing CSRF token in the session..." )
224225 session ["github_oauth2_state" ] = csrf_token
225- cla .log .debug (f"{ fn } - GitHub OAuth2 request with CSRF token { csrf_token } - sending user to { authorization_url } " )
226+ cla .log .debug (f"{ fn } - redirecting user to GitHub OAuth2 authorization URL " )
226227 # We must redirect to GitHub OAuth app for authentication, it will return you to /v2/github/installation which will handle returning user data
227228 if get_redirect_url :
228229 cla .log .debug (f"{ fn } - sending redirect_url via 202 HTTP status JSON payload" )
@@ -246,7 +247,7 @@ def sign_request(self, installation_id, github_repository_id, change_request_id,
246247 # Not sure if we need a different token for each installation ID...
247248 cla .log .debug (f"{ fn } - Loading session from request: { request } ..." )
248249 session = self ._get_request_session (request )
249- cla .log .debug (f"{ fn } - Adding github details to session: { session } which is type: { type (session )} ..." )
250+ cla .log .debug (f"{ fn } - Adding github details to session: { list ( session . keys ()) } which is type: { type (session )} ..." )
250251 session ["github_installation_id" ] = installation_id
251252 session ["github_repository_id" ] = github_repository_id
252253 session ["github_change_request_id" ] = change_request_id
@@ -267,7 +268,7 @@ def sign_request(self, installation_id, github_repository_id, change_request_id,
267268 )
268269 cla .log .debug (f"{ fn } - Obtained GitHub OAuth2 state from authorization - storing state in the session..." )
269270 session ["github_oauth2_state" ] = state
270- cla .log .debug (f"{ fn } - GitHub OAuth2 request with state { state } - sending user to { authorization_url } " )
271+ cla .log .debug (f"{ fn } - redirecting user to GitHub OAuth2 authorization URL " )
271272 raise falcon .HTTPFound (authorization_url )
272273
273274 def _get_request_session (self , request ) -> dict : # pylint: disable=no-self-use
@@ -277,17 +278,31 @@ def _get_request_session(self, request) -> dict: # pylint: disable=no-self-use
277278 fn = "cla.models.github_models._get_request_session"
278279 session = request .context .get ("session" )
279280 if session is None :
280- cla .log .warning (f"Session is empty for request: { request } " )
281- cla .log .debug (f"{ fn } - loaded session: { session } " )
281+ cla .log .warning (f"{ fn } - Session is empty for request: { request } " )
282+ session = {}
283+ request .context ["session" ] = session
282284
283285 # Ensure session is a dict - getting issue where session is a string
284286 if isinstance (session , str ):
285287 # convert string to a dict
286- cla .log .debug (f"{ fn } - session is type: { type (session )} - converting to dict..." )
287- session = json .loads (session )
288- # Reset the session now that we have converted it to a dict
288+ cla .log .warning (f"{ fn } - session context is a string; attempting to parse JSON" )
289+ try :
290+ session = json .loads (session )
291+ except (ValueError , json .JSONDecodeError ) as e :
292+ cla .log .warning (f"{ fn } - unable to parse session string as JSON: { e } " )
293+ session = {}
294+
295+ request .context ["session" ] = session
296+
297+ if not isinstance (session , MutableMapping ):
298+ try :
299+ session = dict (session )
300+ except Exception :
301+ cla .log .warning (f"{ fn } - session context has unsupported type { type (session )} ; resetting to empty dict" )
302+ session = {}
289303 request .context ["session" ] = session
290- cla .log .debug (f"{ fn } - session: { session } which is now type: { type (session )} ..." )
304+
305+ cla .log .debug (f"{ fn } - loaded session (keys={ list (session .keys ())} )" )
291306
292307 return session
293308
@@ -342,7 +357,6 @@ def oauth2_redirect(self, state, code, request): # pylint: disable=too-many-arg
342357 fn = "github_models.oauth2_redirect"
343358 cla .log .debug (f"{ fn } - handling GitHub OAuth2 redirect with request: { dir (request )} " )
344359 session = self ._get_request_session (request ) # request.context['session']
345- cla .log .debug (f"{ fn } - state: { state } , code: { code } , session: { session } " )
346360
347361 if "github_oauth2_state" in session :
348362 session_state = session ["github_oauth2_state" ]
@@ -353,29 +367,35 @@ def oauth2_redirect(self, state, code, request): # pylint: disable=too-many-arg
353367 if state != session_state :
354368 # Eventually handle user-from-session API callback
355369 try :
356- state_data = json .loads (base64 .urlsafe_b64decode (state .encode ()).decode ())
370+ padded_state = state + "=" * (- len (state ) % 4 )
371+ state_data = json .loads (base64 .urlsafe_b64decode (padded_state .encode ()).decode ())
357372 except (ValueError , json .JSONDecodeError , binascii .Error ) as err :
358- cla .log .warning (f"{ fn } - failed to decode state: { state } , error: { err } " )
359- raise falcon .HTTPBadRequest ("Invalid OAuth2 state" , state )
360- state_token = state_data ["csrf" ]
361- value = state_data ["state" ]
373+ cla .log .warning (f"{ fn } - failed to decode state, error: { err } " )
374+ raise falcon .HTTPBadRequest ("Invalid OAuth2 state" , "Invalid OAuth2 state" )
375+
376+ state_token = state_data .get ("csrf" )
377+ value = state_data .get ("state" )
378+ if not state_token or not value :
379+ cla .log .warning (f"{ fn } - invalid OAuth2 state payload while handling callback" )
380+ raise falcon .HTTPBadRequest ("Invalid OAuth2 state" , "Invalid OAuth2 state" )
381+
362382 if value != "user-from-session" :
363- cla .log .warning (f"{ fn } - invalid GitHub OAuth2 state { session_state } expecting { state } , value: { value } " )
364- raise falcon .HTTPBadRequest ("Invalid OAuth2 state" , state )
383+ cla .log .warning (f"{ fn } - invalid GitHub OAuth2 state while handling callback " )
384+ raise falcon .HTTPBadRequest ("Invalid OAuth2 state" , "Invalid OAuth2 state" )
365385 if state_token != session_state :
366- cla .log .warning (f"{ fn } - invalid GitHub OAuth2 state { session_state } expecting { state_token } while handling user-from-session callback" )
367- raise falcon .HTTPBadRequest (f"Invalid OAuth2 state" )
386+ cla .log .warning (f"{ fn } - invalid GitHub OAuth2 state while handling callback" )
387+ raise falcon .HTTPBadRequest (f"Invalid OAuth2 state" , "Invalid OAuth2 state" )
368388 cla .log .debug (f"handling user-from-session callback" )
369389 token_url = cla .conf ["GITHUB_OAUTH_TOKEN_URL" ]
370390 client_id = os .environ ["GH_OAUTH_CLIENT_ID" ]
371- cla .log .debug (f"{ fn } - using client ID { client_id } " )
391+ cla .log .debug (f"{ fn } - using client ID { client_id [ 0 : 5 ] } ... " )
372392 client_secret = os .environ ["GH_OAUTH_SECRET" ]
373393 try :
374394 token = self ._fetch_token (client_id , state , token_url , client_secret , code )
375395 except Exception as err :
376396 cla .log .warning (f"{ fn } - GitHub OAuth2 error: { err } . Likely bad or expired code, returning HTTP 404 state." )
377397 raise falcon .HTTPBadRequest ("OAuth2 code is invalid or expired" )
378- cla .log .debug (f"{ fn } - oauth2 token received for state { state } : { token } - storing token in session" )
398+ cla .log .debug (f"{ fn } - oauth2 token received - storing token in session" )
379399 session ["github_oauth2_token" ] = token
380400 user = self .get_or_create_user (request )
381401 if user is None :
@@ -385,7 +405,7 @@ def oauth2_redirect(self, state, code, request): # pylint: disable=too-many-arg
385405 return user .to_dict ()
386406
387407 # Get session information for this request.
388- cla .log .debug (f"{ fn } - attempting to fetch OAuth2 token for state { state } " )
408+ cla .log .debug (f"{ fn } - attempting to fetch OAuth2 token" )
389409 installation_id = session .get ("github_installation_id" , None )
390410 github_repository_id = session .get ("github_repository_id" , None )
391411 change_request_id = session .get ("github_change_request_id" , None )
@@ -394,12 +414,9 @@ def oauth2_redirect(self, state, code, request): # pylint: disable=too-many-arg
394414 token_url = cla .conf ["GITHUB_OAUTH_TOKEN_URL" ]
395415 client_id = os .environ ["GH_OAUTH_CLIENT_ID" ]
396416 client_secret = os .environ ["GH_OAUTH_SECRET" ]
397- cla .log .debug (
398- f"{ fn } - fetching token using { client_id [0 :5 ]} ... with state={ state } , token_url={ token_url } , "
399- f"client_secret={ client_secret [0 :5 ]} , with code={ code } "
400- )
417+ cla .log .debug (f"{ fn } - fetching oauth2 token with client ID: { client_id [0 :5 ]} ..., token_url: { token_url } " )
401418 token = self ._fetch_token (client_id , state , token_url , client_secret , code )
402- cla .log .debug (f"{ fn } - oauth2 token received for state { state } : { token } - storing token in session" )
419+ cla .log .debug (f"{ fn } - oauth2 token received - storing token in session" )
403420 session ["github_oauth2_token" ] = token
404421 cla .log .debug (f"{ fn } - redirecting the user back to the console: { origin_url } " )
405422 return self .redirect_to_console (installation_id , github_repository_id , change_request_id , origin_url , request )
@@ -1498,7 +1515,7 @@ def get_user_data(self, session, client_id): # pylint: disable=no-self-use
14981515 fn = "cla.models.github_models.get_user_data"
14991516 token = session .get ("github_oauth2_token" )
15001517 if token is None :
1501- cla .log .error (f"{ fn } - unable to load github_oauth2_token from session, session is: { session } " )
1518+ cla .log .error (f"{ fn } - unable to load github_oauth2_token from session (keys= { list ( session . keys ()) } ) " )
15021519 return {"error" : "could not get user data from session" }
15031520
15041521 oauth2 = OAuth2Session (client_id , token = token )
0 commit comments