Coverage for app/crud.py: 100%

48 statements  

« prev     ^ index     » next       coverage.py v7.16.1, created at 2026-10-26 10:48 +0000

1import uuid 

2from typing import Any 

3 

4from sqlmodel import Session, select 

5 

6from app.core.security import get_password_hash, verify_password 

7from app.models import Item, ItemCreate, User, UserCreate, UserUpdate 

8 

9 

10def create_user(*, session: Session, user_create: UserCreate) -> User: 

11 db_obj = User.model_validate( 1ajklmnopbstdABueCvDgwxfEFGHIJc

12 user_create, update={"hashed_password": get_password_hash(user_create.password)} 

13 ) 

14 session.add(db_obj) 1ajklmnopbstdABueCvDgwxfEFGHIJc

15 session.commit() 1ajklmnopbstdABueCvDgwxfEFGHIJc

16 session.refresh(db_obj) 1ajklmnopbstdABueCvDgwxfEFGHIJc

17 return db_obj 1ajklmnopbstdABueCvDgwxfEFGHIJc

18 

19 

20def update_user(*, session: Session, db_user: User, user_in: UserUpdate) -> Any: 

21 user_data = user_in.model_dump(exclude_unset=True) 1abgc

22 extra_data = {} 1abgc

23 if "password" in user_data: 1abgc

24 password = user_data["password"] 1abc

25 hashed_password = get_password_hash(password) 1abc

26 extra_data["hashed_password"] = hashed_password 1abc

27 db_user.sqlmodel_update(user_data, update=extra_data) 1abgc

28 session.add(db_user) 1abgc

29 session.commit() 1abgc

30 session.refresh(db_user) 1abgc

31 return db_user 1abgc

32 

33 

34def get_user_by_email(*, session: Session, email: str) -> User | None: 

35 statement = select(User).where(User.email == email) 1aqyrhKLbstMduevNwOxfiz

36 session_user = session.exec(statement).first() 1aqyrhKLbstMduevNwOxfiz

37 return session_user 1aqyrhKLbstMduevNwOxfiz

38 

39 

40# Dummy hash to use for timing attack prevention when user is not found 

41# This is an Argon2 hash of a random password, used to ensure constant-time comparison 

42DUMMY_HASH = "$argon2id$v=19$m=65536,t=3,p=4$MjQyZWE1MzBjYjJlZTI0Yw$YTU4NGM5ZTZmYjE2NzZlZjY0ZWY3ZGRkY2U2OWFjNjk" 

43 

44 

45def authenticate(*, session: Session, email: str, password: str) -> User | None: 

46 db_user = get_user_by_email(session=session, email=email) 1aqyrhbdefiz

47 if not db_user: 1aqyrhbdefiz

48 # Prevent timing attacks by running password verification even when user doesn't exist 

49 # This ensures the response time is similar whether or not the email exists 

50 verify_password(password, DUMMY_HASH) 1z

51 return None 1z

52 verified, updated_password_hash = verify_password(password, db_user.hashed_password) 1aqyrhbdefi

53 if not verified: 1aqyrhbdefi

54 return None 1y

55 if updated_password_hash: 1aqrhbdefi

56 db_user.hashed_password = updated_password_hash 1hi

57 session.add(db_user) 1hi

58 session.commit() 1hi

59 session.refresh(db_user) 1hi

60 return db_user 1aqrhbdefi

61 

62 

63def create_item(*, session: Session, item_in: ItemCreate, owner_id: uuid.UUID) -> Item: 

64 db_item = Item.model_validate(item_in, update={"owner_id": owner_id}) 1jklmnop

65 session.add(db_item) 1jklmnop

66 session.commit() 1jklmnop

67 session.refresh(db_item) 1jklmnop

68 return db_item 1jklmnop