updated user registration, user info

This commit is contained in:
Alexander Karpov 2022-12-08 12:18:59 +03:00
parent 8ac1c37e5d
commit 5961d7dbe0
9 changed files with 99 additions and 43 deletions

View File

@ -9,14 +9,14 @@ from . import schemas, services
from .schemas import User
async def get_user(session: AsyncSession, username: str) -> User | None:
r = await session.execute(select(UserScheme).where(UserScheme.username == username))
async def get_user_by_email(session: AsyncSession, email: str) -> User | None:
r = await session.execute(select(UserScheme).where(UserScheme.email == email))
user = r.scalars().first()
return user
async def get_user_by_email(session: AsyncSession, email: str) -> User | None:
r = await session.execute(select(UserScheme).where(UserScheme.email == email))
async def get_user(session: AsyncSession, pk: int) -> User | None:
r = await session.execute(select(UserScheme).where(UserScheme.id == pk))
user = r.scalars().first()
return user
@ -37,17 +37,15 @@ async def get_users(
async def create_user(session: AsyncSession, user: schemas.UserCreate) -> UserScheme:
if await get_user(session, user.username):
raise HTTPException(status_code=400, detail="Username already taken")
if await get_user_by_email(session, user.email):
raise HTTPException(status_code=400, detail="Email already taken")
raise HTTPException(status_code=422, detail="Email already taken")
hashed_password = services.get_password_hash(user.password)
db_user = UserScheme(
email=user.email,
username=user.username,
fullname=user.fullname,
gender=user.gender,
born=user.born.date(),
hashed_password=hashed_password,
disabled=False,
)

View File

@ -1,3 +1,5 @@
from datetime import datetime
from pydantic import BaseModel, EmailStr
@ -7,11 +9,11 @@ class Token(BaseModel):
class TokenData(BaseModel):
username: str
email: EmailStr
class UserBase(BaseModel):
username: str
email: EmailStr
class UserLogin(UserBase):
@ -20,13 +22,13 @@ class UserLogin(UserBase):
class UserCreate(UserBase):
password: str
email: EmailStr
fullname: str
gender: str
born: datetime
class UserPublicInfo(UserBase):
id: int
email: EmailStr
fullname: str | None
disabled: bool
@ -36,8 +38,7 @@ class UserPublicInfo(UserBase):
class User(UserBase):
id: int
email: EmailStr
fullname: str | None
fullname: str
hashed_password: str
disabled: bool
is_manager: bool

View File

@ -32,8 +32,8 @@ def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
async def get_user(session: AsyncSession, username: str) -> User:
db_user = await crud.get_user(session, username=username)
async def get_user(session: AsyncSession, email: str) -> User:
db_user = await crud.get_user_by_email(session, email=email)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user
@ -78,13 +78,13 @@ async def get_current_user(
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[JWT_ALGORITHM])
username: str = payload.get("sub")
if username is None:
email: str = payload.get("sub")
if email is None:
raise credentials_exception
token_data = TokenData(username=username)
token_data = TokenData(email=email)
except JWTError:
raise credentials_exception
user = await get_user(session, token_data.username)
user = await get_user(session, token_data.email)
if user is None:
raise credentials_exception
return user
@ -96,3 +96,16 @@ async def get_current_active_user(
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
async def get_current_active_manager(
current_user: User = Depends(get_current_user),
) -> User:
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
if not current_user.is_manager:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="You are not allowed to access this info",
)
return current_user

View File

@ -23,7 +23,7 @@ async def login_for_access_token(
data: UserLogin,
session: AsyncSession = Depends(get_db_session),
) -> Dict[str, str]:
user = await authenticate_user(session, data.username, data.password)
user = await authenticate_user(session, data.email, data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
@ -32,7 +32,7 @@ async def login_for_access_token(
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username},
data={"sub": user.email},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}

View File

@ -1,5 +1,7 @@
from datetime import date
from pydantic import EmailStr
from sqlalchemy import Boolean, Column, Integer, String
from sqlalchemy import Boolean, Column, Date, Integer, String
from med_backend.db.base import Base
@ -10,12 +12,11 @@ class UserScheme(Base):
__tablename__ = "users"
id: int = Column(Integer, primary_key=True, index=True)
username: str = Column(String, unique=True, index=True, nullable=False)
email: EmailStr = Column(String, unique=True, index=True, nullable=False)
fullname: str = Column(String, default="")
hashed_password: str = Column(String)
gender: str = Column(String, default="Не выбран")
age: int = Column(Integer, default=0)
born: date = Column(Date, nullable=False)
latest_form_result: str = Column(String, default="ok")
is_manager: bool = Column(Boolean, default=False)

View File

@ -1,12 +1,34 @@
from pydantic import EmailStr
from datetime import date, datetime
from med_backend.auth.schemas import UserBase
from dateutil.relativedelta import relativedelta
from pydantic import BaseModel, EmailStr, root_validator
class ExtendedUser(UserBase):
class ExtendedUser(BaseModel):
id: int
fullname: str
age: int
gender: str
born: date
@root_validator(pre=False)
def _set_fields(cls, values):
"""This is a validator that sets the field values based on the
the user's account type.
Args:
values (dict): Stores the attributes of the User object.
Returns:
dict: The attributes of the user object with the user's fields.
"""
values["key"] = values["id"]
values["fio"] = values["fullname"]
values["age"] = relativedelta(datetime.now(), values["born"]).years
values.pop("id")
values.pop("fullname")
values.pop("born")
return values
class ListUser(ExtendedUser):
@ -17,7 +39,6 @@ class ListUser(ExtendedUser):
class FullUser(ListUser):
gender: str
email: EmailStr
class Config:

View File

@ -1,12 +1,11 @@
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from med_backend.auth.crud import get_users
from med_backend.auth import crud
from med_backend.auth.schemas import User
from med_backend.auth.services import get_current_active_user
from med_backend.auth.services import get_current_active_manager
from med_backend.db.dependencies import get_db_session
from med_backend.users.schemas import ListUser
from med_backend.users.schemas import FullUser, ListUser
router = APIRouter()
@ -15,13 +14,20 @@ router = APIRouter()
async def get_all_users(
skip: int = 0,
limit: int = 100,
current_user: User = Depends(get_current_active_user),
current_user: User = Depends(get_current_active_manager),
session: AsyncSession = Depends(get_db_session),
):
if not current_user.is_manager:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="You are not allowed to access this info",
)
users = await get_users(session, skip, limit)
users = await crud.get_users(session, skip, limit)
return users
@router.get("/{key}", response_model=FullUser)
async def get_user(
key: int,
current_user: User = Depends(get_current_active_manager),
session: AsyncSession = Depends(get_db_session),
):
user = await crud.get_user(session, key)
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user

17
poetry.lock generated
View File

@ -983,6 +983,17 @@ python-versions = "*"
[package.dependencies]
pytest = ">=2.6.0"
[[package]]
name = "python-dateutil"
version = "2.8.2"
description = "Extensions to the standard Python datetime module"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
[package.dependencies]
six = ">=1.5"
[[package]]
name = "python-decouple"
version = "3.6"
@ -1399,7 +1410,7 @@ tokenize-rt = ">=2.1"
[metadata]
lock-version = "1.1"
python-versions = "^3.9"
content-hash = "ead3885108db4ad12e4fadd52509671806884feda6607c3364807ebb210f69a2"
content-hash = "322a94830a4e40dca70295059b87f26843820996227f5294bb417b00ff86a0f2"
[metadata.files]
anyio = [
@ -2201,6 +2212,10 @@ pytest-cov = [
pytest-env = [
{file = "pytest-env-0.6.2.tar.gz", hash = "sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"},
]
python-dateutil = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
]
python-decouple = [
{file = "python-decouple-3.6.tar.gz", hash = "sha256:2838cdf77a5cf127d7e8b339ce14c25bceb3af3e674e039d4901ba16359968c7"},
{file = "python_decouple-3.6-py3-none-any.whl", hash = "sha256:6cf502dc963a5c642ea5ead069847df3d916a6420cad5599185de6bab11d8c2e"},

View File

@ -26,6 +26,7 @@ python-decouple = "^3.6"
python-jose = {extras = ["cryptography"], version = "^3.3.0"}
passlib = {extras = ["bcrypt"], version = "^1.7.4"}
python-multipart = "^0.0.5"
python-dateutil = "^2.8.2"
[tool.poetry.dev-dependencies]
pytest = "^7.1.3"