refactor(backend): ♻️ rename UserInDb -> PrivateUser

This commit is contained in:
hay-kot 2021-08-28 17:31:05 -08:00
parent df002c383c
commit 4a7f8428c5
24 changed files with 151 additions and 115 deletions

View File

@ -9,7 +9,7 @@ from sqlalchemy.orm.session import Session
from mealie.core.config import app_dirs, settings from mealie.core.config import app_dirs, settings
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.schema.user import LongLiveTokenInDB, TokenData, UserInDB from mealie.schema.user import LongLiveTokenInDB, TokenData, PrivateUser
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/token")
oauth2_scheme_soft_fail = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=False) oauth2_scheme_soft_fail = OAuth2PasswordBearer(tokenUrl="/api/auth/token", auto_error=False)
@ -48,7 +48,7 @@ async def is_logged_in(token: str = Depends(oauth2_scheme_soft_fail), session=De
return False return False
async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(generate_session)) -> UserInDB: async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(generate_session)) -> PrivateUser:
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",
@ -75,13 +75,13 @@ async def get_current_user(token: str = Depends(oauth2_scheme), session=Depends(
return user return user
async def get_admin_user(current_user=Depends(get_current_user)) -> UserInDB: async def get_admin_user(current_user=Depends(get_current_user)) -> PrivateUser:
if not current_user.admin: if not current_user.admin:
raise HTTPException(status.HTTP_403_FORBIDDEN) raise HTTPException(status.HTTP_403_FORBIDDEN)
return current_user return current_user
def validate_long_live_token(session: Session, client_token: str, id: int) -> UserInDB: def validate_long_live_token(session: Session, client_token: str, id: int) -> PrivateUser:
tokens: list[LongLiveTokenInDB] = db.api_tokens.get(session, id, "parent_id", limit=9999) tokens: list[LongLiveTokenInDB] = db.api_tokens.get(session, id, "parent_id", limit=9999)

View File

@ -1,6 +1,8 @@
from fastapi import BackgroundTasks, Depends from fastapi import BackgroundTasks, Depends
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from mealie.schema.user.user import PrivateUser
from .dependencies import generate_session, get_current_user, is_logged_in from .dependencies import generate_session, get_current_user, is_logged_in
@ -21,9 +23,9 @@ class ReadDeps:
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
user=Depends(is_logged_in), user=Depends(is_logged_in),
): ):
self.session = session self.session: Session = session
self.background_tasks = background_tasks self.bg_tasks: BackgroundTasks = background_tasks
self.user = user self.user: bool = user
class WriteDeps: class WriteDeps:
@ -34,7 +36,7 @@ class WriteDeps:
Args: Args:
background_tasks: BackgroundTasks background_tasks: BackgroundTasks
session: Session session: Session
user: bool user: UserInDB
""" """
def __init__( def __init__(
@ -43,6 +45,6 @@ class WriteDeps:
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
user=Depends(get_current_user), user=Depends(get_current_user),
): ):
self.session = session self.session: Session = session
self.background_tasks = background_tasks self.bg_task: BackgroundTasks = background_tasks
self.user = user self.user: PrivateUser = user

View File

@ -6,7 +6,7 @@ from passlib.context import CryptContext
from mealie.core.config import settings from mealie.core.config import settings
from mealie.db.database import db from mealie.db.database import db
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
ALGORITHM = "HS256" ALGORITHM = "HS256"
@ -27,8 +27,8 @@ def create_file_token(file_path: Path) -> bool:
return create_access_token(token_data, expires_delta=timedelta(minutes=30)) return create_access_token(token_data, expires_delta=timedelta(minutes=30))
def authenticate_user(session, email: str, password: str) -> UserInDB: def authenticate_user(session, email: str, password: str) -> PrivateUser:
user: UserInDB = db.users.get(session, email, "email", any_case=True) user: PrivateUser = db.users.get(session, email, "email", any_case=True)
if not user: if not user:
user = db.users.get(session, email, "username", any_case=True) user = db.users.get(session, email, "username", any_case=True)
@ -53,7 +53,7 @@ def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password) return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password: str) -> str: def hash_password(password: str) -> str:
"""Takes in a raw password and hashes it. Used prior to saving """Takes in a raw password and hashes it. Used prior to saving
a new password to the database. a new password to the database.

View File

@ -27,7 +27,7 @@ from mealie.schema.recipe import (
RecipeCategoryResponse, RecipeCategoryResponse,
RecipeTagResponse, RecipeTagResponse,
) )
from mealie.schema.user import GroupInDB, LongLiveTokenInDB, SignUpOut, UserInDB from mealie.schema.user import GroupInDB, LongLiveTokenInDB, SignUpOut, PrivateUser
from ._base_access_model import BaseAccessModel from ._base_access_model import BaseAccessModel
from .recipe_access_model import RecipeDataAccessModel from .recipe_access_model import RecipeDataAccessModel
@ -78,7 +78,7 @@ class DatabaseAccessLayer:
self.events = BaseAccessModel(DEFAULT_PK, Event, EventSchema) self.events = BaseAccessModel(DEFAULT_PK, Event, EventSchema)
# Users / Groups # Users / Groups
self.users = UserDataAccessModel(DEFAULT_PK, User, UserInDB) self.users = UserDataAccessModel(DEFAULT_PK, User, PrivateUser)
self.api_tokens = BaseAccessModel(DEFAULT_PK, LongLiveToken, LongLiveTokenInDB) self.api_tokens = BaseAccessModel(DEFAULT_PK, LongLiveToken, LongLiveTokenInDB)
self.groups = GroupDataAccessModel(DEFAULT_PK, Group, GroupInDB) self.groups = GroupDataAccessModel(DEFAULT_PK, Group, GroupInDB)
self.meals = BaseAccessModel(DEFAULT_PK, MealPlan, MealPlanOut) self.meals = BaseAccessModel(DEFAULT_PK, MealPlan, MealPlanOut)

View File

@ -1,10 +1,10 @@
from mealie.db.models.users import User from mealie.db.models.users import User
from mealie.schema.user.user import UserInDB from mealie.schema.user.user import PrivateUser
from ._base_access_model import BaseAccessModel from ._base_access_model import BaseAccessModel
class UserDataAccessModel(BaseAccessModel[UserInDB, User]): class UserDataAccessModel(BaseAccessModel[PrivateUser, User]):
def update_password(self, session, id, password: str): def update_password(self, session, id, password: str):
entry = self._query_one(session=session, match_value=id) entry = self._query_one(session=session, match_value=id)
entry.update_password(password) entry.update_password(password)

View File

@ -2,7 +2,7 @@ from sqlalchemy.orm import Session
from mealie.core import root_logger from mealie.core import root_logger
from mealie.core.config import settings from mealie.core.config import settings
from mealie.core.security import get_password_hash from mealie.core.security import hash_password
from mealie.db.data_initialization.init_units_foods import default_recipe_unit_init from mealie.db.data_initialization.init_units_foods import default_recipe_unit_init
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import create_session, engine from mealie.db.db_setup import create_session, engine
@ -48,7 +48,7 @@ def default_user_init(session: Session):
"full_name": "Change Me", "full_name": "Change Me",
"username": "admin", "username": "admin",
"email": settings.DEFAULT_EMAIL, "email": settings.DEFAULT_EMAIL,
"password": get_password_hash(settings.DEFAULT_PASSWORD), "password": hash_password(settings.DEFAULT_PASSWORD),
"group": settings.DEFAULT_GROUP, "group": settings.DEFAULT_GROUP,
"admin": True, "admin": True,
} }

View File

@ -8,7 +8,7 @@ from mealie.core.dependencies import get_current_user
from mealie.core.security import authenticate_user from mealie.core.security import authenticate_user
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
from mealie.services.events import create_user_event from mealie.services.events import create_user_event
public_router = APIRouter(tags=["Users: Authentication"]) public_router = APIRouter(tags=["Users: Authentication"])
@ -26,7 +26,7 @@ def get_token(
email = data.username email = data.username
password = data.password password = data.password
user: UserInDB = authenticate_user(session, email, password) user: PrivateUser = authenticate_user(session, email, password)
if not user: if not user:
background_tasks.add_task( background_tasks.add_task(
@ -42,7 +42,7 @@ def get_token(
@user_router.get("/refresh") @user_router.get("/refresh")
async def refresh_token(current_user: UserInDB = Depends(get_current_user)): async def refresh_token(current_user: PrivateUser = Depends(get_current_user)):
""" Use a valid token to get another token""" """ Use a valid token to get another token"""
access_token = security.create_access_token(data=dict(sub=current_user.email)) access_token = security.create_access_token(data=dict(sub=current_user.email))
return {"access_token": access_token, "token_type": "bearer"} return {"access_token": access_token, "token_type": "bearer"}

View File

@ -5,7 +5,7 @@ from mealie.core.dependencies import get_current_user
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import AdminAPIRouter, UserAPIRouter from mealie.routes.routers import AdminAPIRouter, UserAPIRouter
from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, UserInDB from mealie.schema.user import GroupBase, GroupInDB, UpdateGroup, PrivateUser
from mealie.services.events import create_group_event from mealie.services.events import create_group_event
admin_router = AdminAPIRouter(prefix="/groups", tags=["Groups: CRUD"]) admin_router = AdminAPIRouter(prefix="/groups", tags=["Groups: CRUD"])
@ -14,11 +14,11 @@ user_router = UserAPIRouter(prefix="/groups", tags=["Groups: CRUD"])
@user_router.get("/self", response_model=GroupInDB) @user_router.get("/self", response_model=GroupInDB)
async def get_current_user_group( async def get_current_user_group(
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns the Group Data for the Current User """ """ Returns the Group Data for the Current User """
current_user: UserInDB current_user: PrivateUser
return db.groups.get(session, current_user.group, "name") return db.groups.get(session, current_user.group, "name")
@ -62,7 +62,7 @@ async def update_group_data(
async def delete_user_group( async def delete_user_group(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
id: int, id: int,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Removes a user group from the database """ """ Removes a user group from the database """

View File

@ -7,7 +7,7 @@ from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.meal_plan import MealPlanIn, MealPlanOut from mealie.schema.meal_plan import MealPlanIn, MealPlanOut
from mealie.schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, PrivateUser
from mealie.services.events import create_group_event from mealie.services.events import create_group_event
from mealie.services.image import image from mealie.services.image import image
from mealie.services.meal_services import get_todays_meal, set_mealplan_dates from mealie.services.meal_services import get_todays_meal, set_mealplan_dates
@ -18,7 +18,7 @@ public_router = APIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
@router.get("/all", response_model=list[MealPlanOut]) @router.get("/all", response_model=list[MealPlanOut])
def get_all_meals( def get_all_meals(
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Returns a list of all available Meal Plan """ """ Returns a list of all available Meal Plan """
@ -27,7 +27,7 @@ def get_all_meals(
@router.get("/this-week", response_model=MealPlanOut) @router.get("/this-week", response_model=MealPlanOut)
def get_this_week(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)): def get_this_week(session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)):
""" Returns the meal plan data for this week """ """ Returns the meal plan data for this week """
plans = db.groups.get_meals(session, current_user.group) plans = db.groups.get_meals(session, current_user.group)
if plans: if plans:
@ -35,7 +35,7 @@ def get_this_week(session: Session = Depends(generate_session), current_user: Us
@router.get("/today", tags=["Meal Plan"]) @router.get("/today", tags=["Meal Plan"])
def get_today(session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user)): def get_today(session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)):
""" """
Returns the recipe slug for the meal scheduled for today. Returns the recipe slug for the meal scheduled for today.
If no meal is scheduled nothing is returned If no meal is scheduled nothing is returned
@ -78,7 +78,7 @@ def create_meal_plan(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
data: MealPlanIn, data: MealPlanIn,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Creates a meal plan database entry """ """ Creates a meal plan database entry """
set_mealplan_dates(data) set_mealplan_dates(data)
@ -94,7 +94,7 @@ def update_meal_plan(
plan_id: str, plan_id: str,
meal_plan: MealPlanIn, meal_plan: MealPlanIn,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Updates a meal plan based off ID """ """ Updates a meal plan based off ID """
set_mealplan_dates(meal_plan) set_mealplan_dates(meal_plan)
@ -113,7 +113,7 @@ def delete_meal_plan(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
plan_id, plan_id,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Removes a meal plan from the database """ """ Removes a meal plan from the database """

View File

@ -8,7 +8,7 @@ from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.meal_plan import ListItem, MealPlanOut, ShoppingListIn, ShoppingListOut from mealie.schema.meal_plan import ListItem, MealPlanOut, ShoppingListIn, ShoppingListOut
from mealie.schema.recipe import Recipe from mealie.schema.recipe import Recipe
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
logger = get_logger() logger = get_logger()
@ -19,7 +19,7 @@ router = UserAPIRouter(prefix="/api/meal-plans", tags=["Meal Plan"])
def get_shopping_list( def get_shopping_list(
id: str, id: str,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
mealplan: MealPlanOut = db.meals.get(session, id) mealplan: MealPlanOut = db.meals.get(session, id)

View File

@ -8,7 +8,7 @@ from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.recipe import CommentOut, CreateComment, SaveComment from mealie.schema.recipe import CommentOut, CreateComment, SaveComment
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
router = UserAPIRouter() router = UserAPIRouter()
@ -18,7 +18,7 @@ async def create_comment(
slug: str, slug: str,
new_comment: CreateComment, new_comment: CreateComment,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Create comment in the Database """ """ Create comment in the Database """
@ -31,7 +31,7 @@ async def update_comment(
id: int, id: int,
new_comment: CreateComment, new_comment: CreateComment,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Update comment in the Database """ """ Update comment in the Database """
old_comment: CommentOut = db.comments.get(session, id) old_comment: CommentOut = db.comments.get(session, id)
@ -44,7 +44,7 @@ async def update_comment(
@router.delete("/{slug}/comments/{id}") @router.delete("/{slug}/comments/{id}")
async def delete_comment( async def delete_comment(
id: int, session: Session = Depends(generate_session), current_user: UserInDB = Depends(get_current_user) id: int, session: Session = Depends(generate_session), current_user: PrivateUser = Depends(get_current_user)
): ):
""" Delete comment from the Database """ """ Delete comment from the Database """
comment: CommentOut = db.comments.get(session, id) comment: CommentOut = db.comments.get(session, id)

View File

@ -6,7 +6,7 @@ from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.meal_plan import ShoppingListIn, ShoppingListOut from mealie.schema.meal_plan import ShoppingListIn, ShoppingListOut
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
router = UserAPIRouter(prefix="/shopping-lists", tags=["Shopping Lists: CRUD"]) router = UserAPIRouter(prefix="/shopping-lists", tags=["Shopping Lists: CRUD"])
@ -14,7 +14,7 @@ router = UserAPIRouter(prefix="/shopping-lists", tags=["Shopping Lists: CRUD"])
@router.post("", response_model=ShoppingListOut) @router.post("", response_model=ShoppingListOut)
async def create_shopping_list( async def create_shopping_list(
list_in: ShoppingListIn, list_in: ShoppingListIn,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Create Shopping List in the Database """ """ Create Shopping List in the Database """

View File

@ -6,7 +6,7 @@ from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import AdminAPIRouter from mealie.routes.routers import AdminAPIRouter
from mealie.schema.admin import SiteSettings from mealie.schema.admin import SiteSettings
from mealie.schema.user import GroupInDB, UserInDB from mealie.schema.user import GroupInDB, PrivateUser
from mealie.utils.post_webhooks import post_webhooks from mealie.utils.post_webhooks import post_webhooks
public_router = APIRouter(prefix="/api/site-settings", tags=["Settings"]) public_router = APIRouter(prefix="/api/site-settings", tags=["Settings"])
@ -31,7 +31,7 @@ def update_settings(
@admin_router.post("/webhooks/test") @admin_router.post("/webhooks/test")
def test_webhooks( def test_webhooks(
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Run the function to test your webhooks """ """ Run the function to test your webhooks """

View File

@ -1,9 +1,9 @@
from fastapi import HTTPException, status from fastapi import HTTPException, status
from mealie.schema.user.user import UserInDB from mealie.schema.user.user import PrivateUser
def assert_user_change_allowed(id: int, current_user: UserInDB): def assert_user_change_allowed(id: int, current_user: PrivateUser):
if current_user.id != id and not current_user.admin: if current_user.id != id and not current_user.admin:
# only admins can edit other users # only admins can edit other users
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="NOT_AN_ADMIN") raise HTTPException(status.HTTP_403_FORBIDDEN, detail="NOT_AN_ADMIN")

View File

@ -9,7 +9,7 @@ from mealie.core.security import create_access_token
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.schema.user import CreateToken, LoingLiveTokenIn, LongLiveTokenInDB, UserInDB from mealie.schema.user import CreateToken, LoingLiveTokenIn, LongLiveTokenInDB, PrivateUser
router = UserAPIRouter() router = UserAPIRouter()
@ -17,7 +17,7 @@ router = UserAPIRouter()
@router.post("/api-tokens", status_code=status.HTTP_201_CREATED) @router.post("/api-tokens", status_code=status.HTTP_201_CREATED)
async def create_api_token( async def create_api_token(
token_name: LoingLiveTokenIn, token_name: LoingLiveTokenIn,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Create api_token in the Database """ """ Create api_token in the Database """
@ -42,7 +42,7 @@ async def create_api_token(
@router.delete("/api-tokens/{token_id}") @router.delete("/api-tokens/{token_id}")
async def delete_api_token( async def delete_api_token(
token_id: int, token_id: int,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Delete api_token from the Database """ """ Delete api_token from the Database """

View File

@ -3,12 +3,12 @@ from sqlalchemy.orm.session import Session
from mealie.core import security from mealie.core import security
from mealie.core.dependencies import get_current_user from mealie.core.dependencies import get_current_user
from mealie.core.security import get_password_hash from mealie.core.security import hash_password
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import AdminAPIRouter, UserAPIRouter from mealie.routes.routers import AdminAPIRouter, UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed from mealie.routes.users._helpers import assert_user_change_allowed
from mealie.schema.user import UserBase, UserIn, UserInDB, UserOut from mealie.schema.user import UserBase, UserIn, PrivateUser, UserOut
from mealie.services.events import create_user_event from mealie.services.events import create_user_event
user_router = UserAPIRouter(prefix="") user_router = UserAPIRouter(prefix="")
@ -24,22 +24,20 @@ async def get_all_users(session: Session = Depends(generate_session)):
async def create_user( async def create_user(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
new_user: UserIn, new_user: UserIn,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
new_user.password = get_password_hash(new_user.password) new_user.password = hash_password(new_user.password)
background_tasks.add_task( background_tasks.add_task(
create_user_event, "User Created", f"Created by {current_user.full_name}", session=session create_user_event, "User Created", f"Created by {current_user.full_name}", session=session
) )
return db.users.create(session, new_user.dict()) return db.users.create(session, new_user.dict())
@admin_router.get("/{id}", response_model=UserOut) @admin_router.get("/{id}", response_model=UserOut)
async def get_user( async def get_user(id: int, session: Session = Depends(generate_session)):
id: int,
session: Session = Depends(generate_session),
):
return db.users.get(session, id) return db.users.get(session, id)
@ -48,7 +46,7 @@ def delete_user(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
id: int, id: int,
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Removes a user from the database. Must be the current user or a super user""" """ Removes a user from the database. Must be the current user or a super user"""
@ -66,7 +64,7 @@ def delete_user(
@user_router.get("/self", response_model=UserOut) @user_router.get("/self", response_model=UserOut)
async def get_logged_in_user( async def get_logged_in_user(
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
return current_user.dict() return current_user.dict()
@ -75,7 +73,7 @@ async def get_logged_in_user(
async def update_user( async def update_user(
id: int, id: int,
new_data: UserBase, new_data: UserBase,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):

View File

@ -6,7 +6,7 @@ from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed from mealie.routes.users._helpers import assert_user_change_allowed
from mealie.schema.user import UserFavorites, UserInDB from mealie.schema.user import UserFavorites, PrivateUser
user_router = UserAPIRouter() user_router = UserAPIRouter()
@ -21,7 +21,7 @@ async def get_favorites(id: str, session: Session = Depends(generate_session)):
@user_router.post("/{id}/favorites/{slug}") @user_router.post("/{id}/favorites/{slug}")
def add_favorite( def add_favorite(
slug: str, slug: str,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Adds a Recipe to the users favorites """ """ Adds a Recipe to the users favorites """
@ -35,7 +35,7 @@ def add_favorite(
@user_router.delete("/{id}/favorites/{slug}") @user_router.delete("/{id}/favorites/{slug}")
def remove_favorite( def remove_favorite(
slug: str, slug: str,
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Adds a Recipe to the users favorites """ """ Adds a Recipe to the users favorites """

View File

@ -8,7 +8,7 @@ from mealie.core.config import app_dirs
from mealie.core.dependencies import get_current_user from mealie.core.dependencies import get_current_user
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed from mealie.routes.users._helpers import assert_user_change_allowed
from mealie.schema.user import UserInDB from mealie.schema.user import PrivateUser
public_router = APIRouter(prefix="", tags=["Users: Images"]) public_router = APIRouter(prefix="", tags=["Users: Images"])
user_router = UserAPIRouter(prefix="", tags=["Users: Images"]) user_router = UserAPIRouter(prefix="", tags=["Users: Images"])
@ -28,7 +28,7 @@ async def get_user_image(id: str):
def update_user_image( def update_user_image(
id: str, id: str,
profile_image: UploadFile = File(...), profile_image: UploadFile = File(...),
current_user: UserInDB = Depends(get_current_user), current_user: PrivateUser = Depends(get_current_user),
): ):
""" Updates a User Image """ """ Updates a User Image """

View File

@ -1,42 +1,25 @@
from fastapi import Depends, HTTPException, status from fastapi import Depends
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from mealie.core.config import settings from mealie.core.config import settings
from mealie.core.dependencies import get_current_user from mealie.core.security import hash_password
from mealie.core.security import get_password_hash, verify_password
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import UserAPIRouter from mealie.routes.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed from mealie.schema.user import ChangePassword
from mealie.schema.user import ChangePassword, UserInDB from mealie.services.user.user_service import UserService
user_router = UserAPIRouter(prefix="") user_router = UserAPIRouter(prefix="")
@user_router.put("/{id}/reset-password") @user_router.put("/{id}/reset-password")
async def reset_user_password( async def reset_user_password(id: int, session: Session = Depends(generate_session)):
id: int, new_password = hash_password(settings.DEFAULT_PASSWORD)
session: Session = Depends(generate_session),
):
new_password = get_password_hash(settings.DEFAULT_PASSWORD)
db.users.update_password(session, id, new_password) db.users.update_password(session, id, new_password)
@user_router.put("/{id}/password") @user_router.put("/{id}/password")
def update_password( def update_password(password_change: ChangePassword, user_service: UserService = Depends(UserService.write_existing)):
id: int,
password_change: ChangePassword,
current_user: UserInDB = Depends(get_current_user),
session: Session = Depends(generate_session),
):
""" Resets the User Password""" """ Resets the User Password"""
assert_user_change_allowed(id, current_user) return user_service.change_password(password_change)
match_passwords = verify_password(password_change.current_password, current_user.password)
if not (match_passwords):
raise HTTPException(status.HTTP_400_BAD_REQUEST)
new_password = get_password_hash(password_change.new_password)
db.users.update_password(session, id, new_password)

View File

@ -4,11 +4,11 @@ from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from sqlalchemy.orm.session import Session from sqlalchemy.orm.session import Session
from mealie.core.dependencies import get_admin_user from mealie.core.dependencies import get_admin_user
from mealie.core.security import get_password_hash from mealie.core.security import hash_password
from mealie.db.database import db from mealie.db.database import db
from mealie.db.db_setup import generate_session from mealie.db.db_setup import generate_session
from mealie.routes.routers import AdminAPIRouter from mealie.routes.routers import AdminAPIRouter
from mealie.schema.user import SignUpIn, SignUpOut, SignUpToken, UserIn, UserInDB from mealie.schema.user import SignUpIn, SignUpOut, SignUpToken, UserIn, PrivateUser
from mealie.services.events import create_user_event from mealie.services.events import create_user_event
public_router = APIRouter(prefix="/sign-ups") public_router = APIRouter(prefix="/sign-ups")
@ -16,9 +16,7 @@ admin_router = AdminAPIRouter(prefix="/sign-ups")
@admin_router.get("", response_model=list[SignUpOut]) @admin_router.get("", response_model=list[SignUpOut])
async def get_all_open_sign_ups( async def get_all_open_sign_ups(session: Session = Depends(generate_session)):
session: Session = Depends(generate_session),
):
""" Returns a list of open sign up links """ """ Returns a list of open sign up links """
return db.sign_ups.get_all(session) return db.sign_ups.get_all(session)
@ -28,7 +26,7 @@ async def get_all_open_sign_ups(
async def create_user_sign_up_key( async def create_user_sign_up_key(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks,
key_data: SignUpIn, key_data: SignUpIn,
current_user: UserInDB = Depends(get_admin_user), current_user: PrivateUser = Depends(get_admin_user),
session: Session = Depends(generate_session), session: Session = Depends(generate_session),
): ):
""" Generates a Random Token that a new user can sign up with """ """ Generates a Random Token that a new user can sign up with """
@ -47,10 +45,7 @@ async def create_user_sign_up_key(
@public_router.post("/{token}") @public_router.post("/{token}")
async def create_user_with_token( async def create_user_with_token(
background_tasks: BackgroundTasks, background_tasks: BackgroundTasks, token: str, new_user: UserIn, session: Session = Depends(generate_session)
token: str,
new_user: UserIn,
session: Session = Depends(generate_session),
): ):
""" Creates a user with a valid sign up token """ """ Creates a user with a valid sign up token """
@ -61,7 +56,7 @@ async def create_user_with_token(
# Create User # Create User
new_user.admin = db_entry.admin new_user.admin = db_entry.admin
new_user.password = get_password_hash(new_user.password) new_user.password = hash_password(new_user.password)
db.users.create(session, new_user.dict()) db.users.create(session, new_user.dict())
# DeleteToken # DeleteToken
@ -72,9 +67,6 @@ async def create_user_with_token(
@admin_router.delete("/{token}") @admin_router.delete("/{token}")
async def delete_token( async def delete_token(token: str, session: Session = Depends(generate_session)):
token: str,
session: Session = Depends(generate_session),
):
""" Removed a token from the database """ """ Removed a token from the database """
db.sign_ups.delete(session, token) db.sign_ups.delete(session, token)

View File

@ -107,7 +107,7 @@ class UserFavorites(UserBase):
} }
class UserInDB(UserOut): class PrivateUser(UserOut):
password: str password: str
class Config: class Config:
@ -142,7 +142,7 @@ class GroupInDB(UpdateGroup):
class LongLiveTokenInDB(CreateToken): class LongLiveTokenInDB(CreateToken):
id: int id: int
user: UserInDB user: PrivateUser
class Config: class Config:
orm_mode = True orm_mode = True

View File

@ -22,7 +22,7 @@ from mealie.schema.admin import (
) )
from mealie.schema.events import EventNotificationIn from mealie.schema.events import EventNotificationIn
from mealie.schema.recipe import CommentOut, Recipe from mealie.schema.recipe import CommentOut, Recipe
from mealie.schema.user import UpdateGroup, UserInDB from mealie.schema.user import UpdateGroup, PrivateUser
from mealie.services.image import minify from mealie.services.image import minify
@ -215,7 +215,7 @@ class ImportDatabase:
def import_users(self): def import_users(self):
users_file = self.import_dir.joinpath("users", "users.json") users_file = self.import_dir.joinpath("users", "users.json")
users = ImportDatabase.read_models_file(users_file, UserInDB) users = ImportDatabase.read_models_file(users_file, PrivateUser)
user_imports = [] user_imports = []
for user in users: for user in users:
if user.id == 1: # Update Default User if user.id == 1: # Update Default User

View File

@ -13,7 +13,7 @@ from mealie.core.root_logger import get_logger
from mealie.db.database import get_database from mealie.db.database import get_database
from mealie.db.db_setup import SessionLocal from mealie.db.db_setup import SessionLocal
from mealie.schema.recipe.recipe import CreateRecipe, Recipe from mealie.schema.recipe.recipe import CreateRecipe, Recipe
from mealie.schema.user.user import UserInDB from mealie.schema.user.user import PrivateUser
from mealie.services.events import create_recipe_event from mealie.services.events import create_recipe_event
logger = get_logger(module=__name__) logger = get_logger(module=__name__)
@ -29,7 +29,7 @@ class RecipeService:
recipe: Recipe # Required for proper type hints recipe: Recipe # Required for proper type hints
def __init__(self, session: Session, user: UserInDB, background_tasks: BackgroundTasks = None) -> None: def __init__(self, session: Session, user: PrivateUser, background_tasks: BackgroundTasks = None) -> None:
self.session = session or SessionLocal() self.session = session or SessionLocal()
self.user = user self.user = user
self.background_tasks = background_tasks self.background_tasks = background_tasks
@ -58,7 +58,7 @@ class RecipeService:
Returns: Returns:
RecipeService: The Recipe Service class with a populated recipe attribute RecipeService: The Recipe Service class with a populated recipe attribute
""" """
new_class = cls(deps.session, deps.user, deps.background_tasks) new_class = cls(deps.session, deps.user, deps.bg_tasks)
new_class.assert_existing(slug) new_class.assert_existing(slug)
return new_class return new_class
@ -80,7 +80,7 @@ class RecipeService:
Returns: Returns:
RecipeService: The Recipe Service class with a populated recipe attribute RecipeService: The Recipe Service class with a populated recipe attribute
""" """
new_class = cls(deps.session, deps.user, deps.background_tasks) new_class = cls(deps.session, deps.user, deps.bg_task)
new_class.assert_existing(slug) new_class.assert_existing(slug)
return new_class return new_class
@ -92,7 +92,7 @@ class RecipeService:
HTTPException: 400 Bad Request HTTPException: 400 Bad Request
""" """
return cls(deps.session, deps.user, deps.background_tasks) return cls(deps.session, deps.user, deps.bg_task)
def pupulate_recipe(self, slug: str) -> Recipe: def pupulate_recipe(self, slug: str) -> Recipe:
"""Populates the recipe attribute with the recipe from the database. """Populates the recipe attribute with the recipe from the database.

View File

@ -0,0 +1,61 @@
from fastapi import BackgroundTasks, Depends, HTTPException, status
from sqlalchemy.orm.session import Session
from mealie.core.config import get_app_dirs, get_settings
from mealie.core.dependencies import WriteDeps
from mealie.core.root_logger import get_logger
from mealie.core.security import hash_password, verify_password
from mealie.db.database import get_database
from mealie.db.db_setup import SessionLocal
from mealie.schema.recipe.recipe import Recipe
from mealie.schema.user.user import ChangePassword, PrivateUser
from mealie.services.events import create_user_event
logger = get_logger(module=__name__)
class UserService:
""""""
def __init__(self, session: Session, acting_user: PrivateUser, background_tasks: BackgroundTasks = None) -> None:
self.session = session or SessionLocal()
self.acting_user = acting_user
self.background_tasks = background_tasks
self.recipe: Recipe = None
# Global Singleton Dependency Injection
self.db = get_database()
self.app_dirs = get_app_dirs()
self.settings = get_settings()
@classmethod
def write_existing(cls, id: int, deps: WriteDeps = Depends()):
new_instance = cls(session=deps.session, acting_user=deps.user, background_tasks=deps.bg_task)
new_instance._populate_target_user(id)
new_instance._assert_user_change_allowed()
return new_instance
def _assert_user_change_allowed(self) -> None:
if self.acting_user.id != self.target_user.id and not self.acting_user.admin:
# only admins can edit other users
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="NOT_AN_ADMIN")
def _populate_target_user(self, id: int = None):
if id:
self.target_user = self.db.users.get(self.session, id)
if not self.target_user:
raise HTTPException(status.HTTP_404_NOT_FOUND)
else:
self.target_user = self.acting_user
def _create_event(self, title: str, message: str) -> None:
self.background_tasks.add_task(create_user_event, title, message, self.session)
def change_password(self, password_change: ChangePassword) -> PrivateUser:
""""""
if not verify_password(password_change.current_password, self.target_user.password):
raise HTTPException(status.HTTP_400_BAD_REQUEST)
self.target_user.password = hash_password(password_change.new_password)
return self.db.users.update_password(self.session, self.target_user.id, self.target_user.password)