initial commit

This commit is contained in:
2023-01-09 13:03:16 +01:00
commit d0c0668fad
89 changed files with 12472 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
from .routes import router as user_router, get_auth_router
from .models import User, AccessToken

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

71
back/app/user/manager.py Normal file
View File

@@ -0,0 +1,71 @@
import uuid
from typing import Any, Dict, Generic, Optional
from fastapi import Depends
from fastapi_users import BaseUserManager, UUIDIDMixin, models, exceptions, schemas
from .models import User, get_user_db
SECRET = "SECRET"
class UserManager(UUIDIDMixin, BaseUserManager[User, uuid.UUID]):
reset_password_token_secret = SECRET
verification_token_secret = SECRET
async def get_by_email(self, user_email: str) -> models.UP:
return await self.get_by_login(user_email)
async def get_by_login(self, user_login: str) -> models.UP:
"""
Get a user by user_login.
:param user_login: E-mail of the user to retrieve.
:raises UserNotExists: The user does not exist.
:return: A user.
"""
user = await self.user_db.get_by_login(user_login)
if user is None:
raise exceptions.UserNotExists()
return user
async def create(
self,
user_create: schemas.UC,
safe: bool = False
) -> models.UP:
"""
Create a user in database.
Triggers the on_after_register handler on success.
:param user_create: The UserCreate model to create.
:param safe: If True, sensitive values like is_superuser or is_verified
will be ignored during the creation, defaults to False.
:raises UserAlreadyExists: A user already exists with the same e-mail.
:return: A new user.
"""
await self.validate_password(user_create.password, user_create)
existing_user = await self.user_db.get_by_login(user_create.login)
if existing_user is not None:
raise exceptions.UserAlreadyExists()
user_dict = (
user_create.create_update_dict()
if safe
else user_create.create_update_dict_superuser()
)
password = user_dict.pop("password")
user_dict["hashed_password"] = self.password_helper.hash(password)
created_user = await self.user_db.create(user_dict)
await self.on_after_register(created_user)
return created_user
async def get_user_manager(user_db=Depends(get_user_db)):
yield UserManager(user_db)

40
back/app/user/models.py Normal file
View File

@@ -0,0 +1,40 @@
from typing import Optional, TypeVar
from datetime import datetime
from pydantic import BaseModel, Field
from beanie import PydanticObjectId
from fastapi_users.db import BeanieBaseUser, BeanieUserDatabase
from fastapi_users_db_beanie.access_token import BeanieAccessTokenDatabase, BeanieBaseAccessToken
from pymongo import IndexModel
class AccessToken(BeanieBaseAccessToken[PydanticObjectId]):
pass
class User(BeanieBaseUser[PydanticObjectId]):
login: str
created_at: datetime = Field(default=datetime.utcnow(), nullable=False)
updated_at: datetime = Field(default_factory=datetime.utcnow, nullable=False)
class Settings:
indexes = [
IndexModel("login", unique=True),
]
class UserDatabase(BeanieUserDatabase):
async def get_by_login(self, login: str) -> Optional[TypeVar("UP_BEANIE", bound=BeanieBaseUser)]:
"""Get a single user by email."""
return await self.user_model.find_one(
self.user_model.login == login
)
async def get_access_token_db():
yield BeanieAccessTokenDatabase(AccessToken)
async def get_user_db():
yield UserDatabase(User)

97
back/app/user/routes.py Normal file
View File

@@ -0,0 +1,97 @@
import uuid
from typing import Optional
from fastapi import Depends, Request
from fastapi_users import BaseUserManager, FastAPIUsers, UUIDIDMixin, models, exceptions
from fastapi_users.authentication import BearerTransport, AuthenticationBackend
from fastapi_users.authentication.strategy.db import AccessTokenDatabase, DatabaseStrategy
from beanie import PydanticObjectId
from fastapi import APIRouter, HTTPException
from typing import List
from .models import User, AccessToken, get_user_db, get_access_token_db
from .schemas import UserRead, UserUpdate, UserCreate
from .manager import get_user_manager
def get_database_strategy(
access_token_db: AccessTokenDatabase[AccessToken] = Depends(get_access_token_db),
) -> DatabaseStrategy:
return DatabaseStrategy(access_token_db, lifetime_seconds=3600)
bearer_transport = BearerTransport(tokenUrl="auth/jwt/login")
auth_backend = AuthenticationBackend(
name="db",
transport=bearer_transport,
get_strategy=get_database_strategy,
)
fastapi_users = FastAPIUsers[User, uuid.UUID](
get_user_manager,
[auth_backend],
)
def get_auth_router():
return fastapi_users.get_auth_router(auth_backend)
router = APIRouter()
@router.post("/", response_description="User added to the database")
async def create(user: UserCreate, user_manager=Depends(get_user_manager)) -> dict:
await user_manager.create(user, safe=True)
return {"message": "User added successfully"}
@router.get("/{id}", response_description="User record retrieved")
async def read_id(id: PydanticObjectId) -> UserRead:
user = await User.get(id)
return user
@router.get("/", response_model=List[UserRead], response_description="User records retrieved")
async def read_list() -> List[UserRead]:
users = await User.find_all().to_list()
return users
@router.put("/{id}", response_description="User record updated")
async def update(id: PydanticObjectId, req: UserUpdate) -> UserRead:
req = {k: v for k, v in req.dict().items() if v is not None}
update_query = {"$set": {
field: value for field, value in req.items()
}}
user = await User.get(id)
if not user:
raise HTTPException(
status_code=404,
detail="Review record not found!"
)
await user.update(update_query)
return user
@router.delete("/{id}", response_description="User record deleted from the database")
async def delete(id: PydanticObjectId) -> dict:
record = await User.get(id)
if not record:
raise HTTPException(
status_code=404,
detail="Review record not found!"
)
await record.delete()
return {
"message": "Record deleted successfully"
}

35
back/app/user/schemas.py Normal file
View File

@@ -0,0 +1,35 @@
import uuid
from typing import TypeVar
from pydantic import BaseModel, Field
from fastapi_users import schemas
from ..core.schemas import Reader
from .models import User
class UserBase(schemas.CreateUpdateDictModel):
pass
class UserRead(User):
class Config:
fields = {
'_id': {'alias': 'id'},
'hashed_password': {'exclude': True}
}
class UserCreate(UserBase):
login: str
password: str
email: str
class UserUpdate(UserBase):
pass
class PasswordUpdate(BaseModel):
old_password: str
password: str