Skip to content

Commit e243ade

Browse files
committed
refactor code
1 parent 87c8e74 commit e243ade

File tree

6 files changed

+202
-1033
lines changed

6 files changed

+202
-1033
lines changed

api/auth/oauth_handlers.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,14 @@ def google_logged_in(blueprint, token): # pylint: disable=unused-argument
3030
email = google_user.get("email")
3131
name = google_user.get("name")
3232

33+
# Validate required fields
34+
if not user_id or not email:
35+
logging.error("Missing required fields from Google OAuth response")
36+
return False
37+
3338
if user_id and email:
3439
# Check if identity exists in Organizations graph, create if new
35-
is_new_user, _ = ensure_user_in_organizations(
40+
_, _ = ensure_user_in_organizations(
3641
user_id, email, name, "google", google_user.get("picture")
3742
)
3843

@@ -41,10 +46,10 @@ def google_logged_in(blueprint, token): # pylint: disable=unused-argument
4146

4247
# Normalize user info structure for session
4348
user_info_session = {
44-
"id": user_id,
45-
"name": name,
49+
"id": str(user_id), # Ensure string type
50+
"name": name or "",
4651
"email": email,
47-
"picture": google_user.get("picture"),
52+
"picture": google_user.get("picture", ""),
4853
"provider": "google"
4954
}
5055
session["user_info"] = user_info_session
@@ -85,9 +90,14 @@ def github_logged_in(blueprint, token): # pylint: disable=unused-argument
8590
user_id = str(github_user.get("id"))
8691
name = github_user.get("name") or github_user.get("login")
8792

93+
# Validate required fields
94+
if not user_id or not email:
95+
logging.error("Missing required fields from GitHub OAuth response")
96+
return False
97+
8898
if user_id and email:
8999
# Check if identity exists in Organizations graph, create if new
90-
is_new_user, _ = ensure_user_in_organizations(
100+
_, _ = ensure_user_in_organizations(
91101
user_id, email, name, "github", github_user.get("avatar_url")
92102
)
93103

@@ -97,9 +107,9 @@ def github_logged_in(blueprint, token): # pylint: disable=unused-argument
97107
# Normalize user info structure for session
98108
user_info_session = {
99109
"id": user_id,
100-
"name": name,
110+
"name": name or "",
101111
"email": email,
102-
"picture": github_user.get("avatar_url"),
112+
"picture": github_user.get("avatar_url", ""),
103113
"provider": "github"
104114
}
105115
session["user_info"] = user_info_session

api/auth/user_management.py

Lines changed: 144 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,23 @@ def ensure_user_in_organizations(provider_user_id, email, name, provider, pictur
1919
Uses MERGE for atomic operations and better performance.
2020
Returns (is_new_user, user_info)
2121
"""
22+
# Input validation
23+
if not provider_user_id or not email or not provider:
24+
logging.error("Missing required parameters: provider_user_id=%s, email=%s, provider=%s",
25+
provider_user_id, email, provider)
26+
return False, None
27+
28+
# Validate email format (basic check)
29+
if "@" not in email or "." not in email:
30+
logging.error("Invalid email format: %s", email)
31+
return False, None
32+
33+
# Validate provider is in allowed list
34+
allowed_providers = ["google", "github"]
35+
if provider not in allowed_providers:
36+
logging.error("Invalid provider: %s", provider)
37+
return False, None
38+
2239
try:
2340
# Select the Organizations graph
2441
organizations_graph = db.select_graph("Organizations")
@@ -98,13 +115,28 @@ def ensure_user_in_organizations(provider_user_id, email, name, provider, pictur
98115
logging.error("Failed to create/update identity and user: email=%s", email)
99116
return False, None
100117

101-
except Exception as e:
118+
except (AttributeError, ValueError, KeyError) as e:
102119
logging.error("Error managing user in Organizations graph: %s", e)
103120
return False, None
121+
except Exception as e:
122+
logging.error("Unexpected error managing user in Organizations graph: %s", e)
123+
return False, None
104124

105125

106126
def update_identity_last_login(provider, provider_user_id):
107127
"""Update the last login timestamp for an existing identity"""
128+
# Input validation
129+
if not provider or not provider_user_id:
130+
logging.error("Missing required parameters: provider=%s, provider_user_id=%s",
131+
provider, provider_user_id)
132+
return
133+
134+
# Validate provider is in allowed list
135+
allowed_providers = ["google", "github"]
136+
if provider not in allowed_providers:
137+
logging.error("Invalid provider: %s", provider)
138+
return
139+
108140
try:
109141
organizations_graph = db.select_graph("Organizations")
110142
update_query = """
@@ -118,9 +150,12 @@ def update_identity_last_login(provider, provider_user_id):
118150
})
119151
logging.info("Updated last login for identity: provider=%s, provider_user_id=%s",
120152
provider, provider_user_id)
121-
except Exception as e:
153+
except (AttributeError, ValueError, KeyError) as e:
122154
logging.error("Error updating last login for identity %s/%s: %s",
123155
provider, provider_user_id, e)
156+
except Exception as e:
157+
logging.error("Unexpected error updating last login for identity %s/%s: %s",
158+
provider, provider_user_id, e)
124159

125160

126161
def validate_and_cache_user():
@@ -129,92 +164,122 @@ def validate_and_cache_user():
129164
Returns (user_info, is_authenticated) tuple.
130165
Supports both Google and GitHub OAuth.
131166
"""
132-
# Check for cached user info from either provider
133-
user_info = session.get("user_info")
134-
token_validated_at = session.get("token_validated_at", 0)
135-
current_time = time.time()
136-
137-
# Use cached user info if it's less than 15 minutes old
138-
if user_info and (current_time - token_validated_at) < 900: # 15 minutes
139-
return user_info, True
140-
141-
# Check Google OAuth first
142-
if google.authorized:
143-
try:
144-
resp = google.get("/oauth2/v2/userinfo")
145-
if resp.ok:
146-
google_user = resp.json()
147-
# Normalize user info structure
148-
user_info = {
149-
"id": google_user.get("id"),
150-
"name": google_user.get("name"),
151-
"email": google_user.get("email"),
152-
"picture": google_user.get("picture"),
153-
"provider": "google"
154-
}
155-
session["user_info"] = user_info
156-
session["token_validated_at"] = current_time
157-
return user_info, True
158-
except (requests.RequestException, KeyError, ValueError) as e:
159-
logging.warning("Google OAuth validation error: %s", e)
160-
161-
# Check GitHub OAuth
162-
if github.authorized:
163-
try:
164-
# Get user profile
165-
resp = github.get("/user")
166-
if resp.ok:
167-
github_user = resp.json()
168-
169-
# Get user email (GitHub may require separate call for email)
170-
email_resp = github.get("/user/emails")
171-
email = None
172-
if email_resp.ok:
173-
emails = email_resp.json()
174-
# Find primary email
175-
for email_obj in emails:
176-
if email_obj.get("primary", False):
177-
email = email_obj.get("email")
178-
break
179-
180-
# If no primary email found, use the first one
181-
if not email and emails:
182-
email = emails[0].get("email")
183-
184-
# Normalize user info structure
185-
user_info = {
186-
"id": str(github_user.get("id")), # Convert to string for consistency
187-
"name": github_user.get("name") or github_user.get("login"),
188-
"email": email,
189-
"picture": github_user.get("avatar_url"),
190-
"provider": "github"
191-
}
192-
session["user_info"] = user_info
193-
session["token_validated_at"] = current_time
194-
return user_info, True
195-
except (requests.RequestException, KeyError, ValueError) as e:
196-
logging.warning("GitHub OAuth validation error: %s", e)
197-
198-
# If no valid authentication found, clear session
199-
session.clear()
200-
return None, False
167+
try:
168+
# Check for cached user info from either provider
169+
user_info = session.get("user_info")
170+
token_validated_at = session.get("token_validated_at", 0)
171+
current_time = time.time()
172+
173+
# Use cached user info if it's less than 15 minutes old
174+
if user_info and (current_time - token_validated_at) < 900: # 15 minutes
175+
return user_info, True
176+
177+
# Check Google OAuth first
178+
if google.authorized:
179+
try:
180+
resp = google.get("/oauth2/v2/userinfo")
181+
if resp.ok:
182+
google_user = resp.json()
183+
# Validate required fields
184+
if not google_user.get("id") or not google_user.get("email"):
185+
logging.warning("Invalid Google user data received")
186+
session.clear()
187+
return None, False
188+
189+
# Normalize user info structure
190+
user_info = {
191+
"id": str(google_user.get("id")), # Ensure string type
192+
"name": google_user.get("name", ""),
193+
"email": google_user.get("email"),
194+
"picture": google_user.get("picture", ""),
195+
"provider": "google"
196+
}
197+
session["user_info"] = user_info
198+
session["token_validated_at"] = current_time
199+
return user_info, True
200+
except (requests.RequestException, KeyError, ValueError) as e:
201+
logging.warning("Google OAuth validation error: %s", e)
202+
session.clear()
203+
204+
# Check GitHub OAuth
205+
if github.authorized:
206+
try:
207+
# Get user profile
208+
resp = github.get("/user")
209+
if resp.ok:
210+
github_user = resp.json()
211+
212+
# Validate required fields
213+
if not github_user.get("id"):
214+
logging.warning("Invalid GitHub user data received")
215+
session.clear()
216+
return None, False
217+
218+
# Get user email (GitHub may require separate call for email)
219+
email_resp = github.get("/user/emails")
220+
email = None
221+
if email_resp.ok:
222+
emails = email_resp.json()
223+
# Find primary email
224+
for email_obj in emails:
225+
if email_obj.get("primary", False):
226+
email = email_obj.get("email")
227+
break
228+
229+
# If no primary email found, use the first one
230+
if not email and emails:
231+
email = emails[0].get("email")
232+
233+
if not email:
234+
logging.warning("No email found for GitHub user")
235+
session.clear()
236+
return None, False
237+
238+
# Normalize user info structure
239+
user_info = {
240+
"id": str(github_user.get("id")), # Convert to string for consistency
241+
"name": github_user.get("name") or github_user.get("login", ""),
242+
"email": email,
243+
"picture": github_user.get("avatar_url", ""),
244+
"provider": "github"
245+
}
246+
session["user_info"] = user_info
247+
session["token_validated_at"] = current_time
248+
return user_info, True
249+
except (requests.RequestException, KeyError, ValueError) as e:
250+
logging.warning("GitHub OAuth validation error: %s", e)
251+
session.clear()
252+
253+
# If no valid authentication found, clear session
254+
session.clear()
255+
return None, False
256+
257+
except Exception as e:
258+
logging.error("Unexpected error in validate_and_cache_user: %s", e)
259+
session.clear()
260+
return None, False
201261

202262

203263
def token_required(f):
204264
"""Decorator to protect routes with token authentication"""
205265

206266
@wraps(f)
207267
def decorated_function(*args, **kwargs):
208-
user_info, is_authenticated = validate_and_cache_user()
268+
try:
269+
user_info, is_authenticated = validate_and_cache_user()
209270

210-
if not is_authenticated:
211-
return jsonify(message="Unauthorized - Please log in"), 401
271+
if not is_authenticated:
272+
return jsonify(message="Unauthorized - Please log in"), 401
212273

213-
g.user_id = user_info.get("id")
214-
if not g.user_id:
215-
session.clear()
216-
return jsonify(message="Unauthorized - Invalid user"), 401
274+
g.user_id = user_info.get("id")
275+
if not g.user_id:
276+
session.clear()
277+
return jsonify(message="Unauthorized - Invalid user"), 401
217278

218-
return f(*args, **kwargs)
279+
return f(*args, **kwargs)
280+
except Exception as e:
281+
logging.error("Unexpected error in token_required: %s", e)
282+
session.clear()
283+
return jsonify(message="Unauthorized - Authentication error"), 401
219284

220285
return decorated_function

0 commit comments

Comments
 (0)