Coverage for app/crud.py: 100%
48 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-26 10:48 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-10-26 10:48 +0000
1import uuid
2from typing import Any
4from sqlmodel import Session, select
6from app.core.security import get_password_hash, verify_password
7from app.models import Item, ItemCreate, User, UserCreate, UserUpdate
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
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
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
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"
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
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