11import datetime
2+ import uuid
23from typing import Dict , Optional
34
45import jwt
56from fastapi import HTTPException , status , Depends
67from fastapi .security import HTTPAuthorizationCredentials , HTTPBearer
8+ from sqlalchemy .exc import IntegrityError
79from sqlalchemy .orm import Session
810from starlette .requests import Request
911
1012from config_rdr import config
13+ from helpers .logger import logger
1114from local_DB .db_dependencies import get_db
1215from local_DB .models import User , BlacklistedToken
16+ from providers .ecotaxa_client import EcoTaxaApiClient
1317
1418
1519class CustomHTTPBearer (HTTPBearer ):
@@ -73,7 +77,7 @@ def get_authorization_scheme_param(authorization_header: Optional[str]):
7377SESSION_COOKIE_NAME = "zoopp_session"
7478
7579
76- def decode_jwt_token (token : str , db : Optional [Session ] = None ) -> Dict :
80+ def decode_jwt_token (token : str , db : Optional [Session ] = None ) -> Dict [ str , str ] :
7781 """
7882 Decode and validate a JWT token.
7983
@@ -143,7 +147,7 @@ def create_jwt_token(data: Dict, expires_delta: Optional[int] = None) -> str:
143147 return encoded_jwt
144148
145149
146- def get_user_from_token (token : str , db : Optional [Session ] = None ) -> Dict :
150+ def get_user_from_token (token : str , db : Optional [Session ] = None ) -> str :
147151 """
148152 Extract user information from a JWT token.
149153
@@ -152,21 +156,28 @@ def get_user_from_token(token: str, db: Optional[Session] = None) -> Dict:
152156 db: Optional database session for checking token blacklist
153157
154158 Returns:
155- User information extracted from the token
159+ User email extracted from the token
156160 """
157161 payload = decode_jwt_token (token , db )
162+ return payload .get ("email" , "" )
158163
159- # In a real application, you might want to validate the user exists in your database
160- # or fetch additional user information
161164
162- return {
163- "id" : payload .get ("sub" , "" ),
164- "name" : payload .get ("name" , "" ),
165- "email" : payload .get ("email" , "" ),
166- }
165+ def get_ecotaxa_token_from_token (token : str , db : Optional [Session ] = None ) -> str :
166+ """
167+ Extract EcoTaxa token in our token
167168
169+ Args:
170+ token: The JWT token
171+ db: Optional database session for checking token blacklist
168172
169- def get_user_from_db (email : str , db ):
173+ Returns:
174+ EcoTaxa token.
175+ """
176+ payload = decode_jwt_token (token , db )
177+ return payload ["token" ]
178+
179+
180+ def get_user_from_db (email : str , db ) -> User :
170181 """
171182 Get a user from the database by email.
172183
@@ -178,7 +189,46 @@ def get_user_from_db(email: str, db):
178189 The user if found, None otherwise
179190 """
180191
181- return db .query (User ).filter (User .email == email ).first ()
192+ return db .query (User ).filter (User .email == email ).first () # type:ignore
193+
194+
195+ def user_from_db (name : str , email : str , db ) -> User :
196+ """
197+ Ensure a user with the given email exists; create it if missing, and return its id.
198+ Lookup is performed by email.
199+
200+ Args:
201+ name: The user's name (EcoTaxa conventions)
202+ email: The user's email address used for lookup and creation.
203+ db: The database session.
204+
205+ Returns:
206+ str: The user's id (existing or newly created).
207+ """
208+ # Try to find existing user by email
209+ user = db .query (User ).filter (User .email == email ).first ()
210+ if user :
211+ return user # type:ignore
212+
213+ # Create a minimal user record if not found
214+ new_user = User (
215+ id = str (uuid .uuid4 ()),
216+ name = name ,
217+ email = email ,
218+ password = "" , # No password stored here (auth handled externally)
219+ )
220+ try :
221+ db .add (new_user )
222+ db .commit ()
223+ db .refresh (new_user )
224+ return new_user # type:ignore
225+ except IntegrityError :
226+ # In case of a race condition where the user was created concurrently
227+ db .rollback ()
228+ user = db .query (User ).filter (User .email == email ).first ()
229+ if user :
230+ return user .id # type:ignore
231+ raise
182232
183233
184234def blacklist_token (token : str , db : Session ):
@@ -257,23 +307,26 @@ def authenticate_user(email: str, password: str, db) -> str:
257307 Raises:
258308 HTTPException: If authentication fails
259309 """
260- # Validate the credentials against the database
261- user = get_user_from_db (email , db )
262-
263- if (
264- not user or user .password != password
265- ): # In a real app, use proper password hashing
310+ # Validate the credentials against EcoTaxa server
311+ client = EcoTaxaApiClient (logger , config .ECOTAXA_SERVER , email , password )
312+ client .token = client .login ()
313+ if client .token is None :
266314 raise HTTPException (
267315 status_code = 401 ,
268316 detail = "Incorrect email or password" ,
269317 headers = {"WWW-Authenticate" : "Bearer" },
270318 )
319+ else :
320+ who = client .whoami ()
321+
322+ user = user_from_db (who .name , who .email , db )
271323
272324 # Create user data for the token
273325 user_data = {
274326 "sub" : user .id ,
275327 "name" : user .name ,
276328 "email" : user .email ,
329+ "token" : client .token ,
277330 }
278331
279332 # Create a JWT token with 30-day expiration
@@ -301,6 +354,51 @@ async def get_current_user_from_credentials(
301354 Raises:
302355 HTTPException: If authentication fails
303356 """
357+ token = await get_token_from_credentials (request , credentials )
358+
359+ # Validate the JWT token and extract user information
360+ user_mail = get_user_from_token (token , db )
361+
362+ # Get the user from the database to ensure they exist
363+ user = get_user_from_db (user_mail , db )
364+
365+ if not user :
366+ raise HTTPException (
367+ status_code = 401 ,
368+ detail = "User not found" ,
369+ headers = {"WWW-Authenticate" : "Bearer" },
370+ )
371+
372+ return user
373+
374+
375+ async def get_ecotaxa_token_from_credentials (
376+ request : Request ,
377+ credentials : HTTPAuthorizationCredentials = Depends (security ),
378+ db : Session = Depends (get_db ),
379+ ) -> str :
380+ """
381+ FastAPI dependency that extracts EcoTaxa token from request.
382+
383+ Args:
384+ request: The request object to access cookies
385+ credentials: The HTTP authorization credentials
386+ db: The database session
387+
388+ Returns:
389+ The EcoTaxa token.
390+
391+ Raises:
392+ HTTPException: If authentication problem
393+ """
394+ token = await get_token_from_credentials (request , credentials )
395+
396+ return get_ecotaxa_token_from_token (token , db )
397+
398+
399+ async def get_token_from_credentials (
400+ request : Request , credentials : HTTPAuthorizationCredentials
401+ ) -> str :
304402 token = None
305403
306404 # Try to extract token from the authorization header
@@ -318,18 +416,4 @@ async def get_current_user_from_credentials(
318416 detail = "Not authenticated" ,
319417 headers = {"WWW-Authenticate" : "Bearer" },
320418 )
321-
322- # Validate the JWT token and extract user information
323- user_data = get_user_from_token (token , db )
324-
325- # Get the user from the database to ensure they exist
326- user = get_user_from_db (user_data ["email" ], db )
327-
328- if not user :
329- raise HTTPException (
330- status_code = 401 ,
331- detail = "User not found" ,
332- headers = {"WWW-Authenticate" : "Bearer" },
333- )
334-
335- return user
419+ return token
0 commit comments