Add users
This commit is contained in:
parent
6700bc9763
commit
12797e3cae
7 changed files with 194 additions and 2 deletions
0
src/infra/__init__.py
Normal file
0
src/infra/__init__.py
Normal file
84
src/infra/users.py
Normal file
84
src/infra/users.py
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
import logging
|
||||
from argon2.exceptions import VerifyMismatchError
|
||||
from src.utils.secret import SecretBox
|
||||
from src.config.email import EmailAddress
|
||||
from sqlalchemy import String, Boolean, VARCHAR, Engine, select
|
||||
from sqlalchemy.orm import mapped_column, Mapped, Session
|
||||
from argon2 import PasswordHasher
|
||||
from src.infra.db import Base
|
||||
from src.services.users import UserRepo, UserDTO, User
|
||||
|
||||
|
||||
class UserModel(Base):
|
||||
__tablename__ = 'user'
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
email: Mapped[str] = mapped_column(String(254), unique=True)
|
||||
email_confirmed: Mapped[bool] = mapped_column(Boolean())
|
||||
password_hash: Mapped[str] = mapped_column(VARCHAR(255))
|
||||
|
||||
|
||||
def model_to_user(db_user: UserModel) -> User:
|
||||
return User(
|
||||
id=db_user.id,
|
||||
email=db_user.email,
|
||||
email_confirmed=db_user.email_confirmed,
|
||||
password_hash=SecretBox(db_user.password_hash),
|
||||
)
|
||||
|
||||
|
||||
class UserRepoImpl(UserRepo):
|
||||
def __init__(self, db: Engine):
|
||||
self._db = db
|
||||
self._hasher = PasswordHasher()
|
||||
self._logger = logging.getLogger('UserRepoImpl')
|
||||
|
||||
def create_user(self, user: UserDTO) -> User:
|
||||
pw = self._hasher.hash(user.raw_password.expose_secret())
|
||||
db_user = UserModel(email=user.email, password_hash=pw)
|
||||
with Session(self._db) as session:
|
||||
session.add(db_user)
|
||||
session.commit()
|
||||
|
||||
return model_to_user(db_user)
|
||||
|
||||
def get_user_by_id(self, user_id: int) -> User | None:
|
||||
with Session(self._db) as session:
|
||||
user = session.get(UserModel, user_id)
|
||||
|
||||
if user:
|
||||
return model_to_user(user)
|
||||
|
||||
def get_user_by_email(self, email: EmailAddress) -> User | None:
|
||||
with Session(self._db) as session:
|
||||
user = session.scalar(select(UserModel).where(UserModel.email == email))
|
||||
|
||||
if user:
|
||||
return model_to_user(user)
|
||||
|
||||
def update_user(self, user: User):
|
||||
with Session(self._db) as session:
|
||||
db_user = session.get(UserModel, user.id)
|
||||
if db_user is None:
|
||||
self._logger.warning(f'Attempted to update non-existing user {user.id}')
|
||||
return
|
||||
|
||||
# These fields can be set directly
|
||||
db_user.email_confirmed = user.email_confirmed
|
||||
db_user.password_hash = user.password_hash.expose_secret()
|
||||
|
||||
# If the email is being updated then it should not be considered confirmed.
|
||||
if db_user.email != user.email:
|
||||
db_user.email = user.email
|
||||
db_user.email_confirmed = False
|
||||
|
||||
session.commit()
|
||||
|
||||
def auth_as_user(self, user: UserDTO) -> User | None:
|
||||
full_user = self.get_user_by_email(user.email)
|
||||
if full_user is None:
|
||||
return None
|
||||
|
||||
try:
|
||||
self._hasher.verify(full_user.password_hash.expose_secret(), user.raw_password.expose_secret())
|
||||
except VerifyMismatchError:
|
||||
return None
|
||||
Loading…
Add table
Add a link
Reference in a new issue