fix: prevent users from updating their own household privileges (#4928)

Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com>
This commit is contained in:
Kuchenpirat
2025-01-22 17:06:41 +01:00
committed by GitHub
parent 8cd2da0abb
commit bf616f9db5
7 changed files with 133 additions and 28 deletions

View File

@@ -76,6 +76,9 @@ class HouseholdSelfServiceController(BaseUserController):
if target_user.household_id != self.household_id:
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="User is not a member of this household")
if target_user.id == self.user.id:
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="User is not allowed to change their own permissions")
target_user.can_invite = permissions.can_invite
target_user.can_manage = permissions.can_manage
target_user.can_manage_household = permissions.can_manage_household

View File

@@ -1,10 +1,49 @@
from fastapi import HTTPException, status
from pydantic import UUID4
from mealie.schema.user.user import PrivateUser
from mealie.schema.response.responses import ErrorResponse
from mealie.schema.user.user import PrivateUser, UserBase
permission_attrs = ["can_invite", "can_manage", "can_manage_household", "can_organize", "admin"]
def assert_user_change_allowed(id: UUID4, current_user: PrivateUser):
if current_user.id != id and not current_user.admin:
# only admins can edit other users
raise HTTPException(status.HTTP_403_FORBIDDEN, detail="NOT_AN_ADMIN")
def _assert_non_admin_user_change_allowed(user_id: UUID4, current_user: PrivateUser, new_data: UserBase):
if current_user.id != user_id:
# User is trying to edit another user
raise HTTPException(status.HTTP_403_FORBIDDEN, ErrorResponse.respond("User cannot edit other users"))
if any(getattr(current_user, p) != getattr(new_data, p) for p in permission_attrs):
# User is trying to change their own permissions
raise HTTPException(
status.HTTP_403_FORBIDDEN,
ErrorResponse.respond("User cannot change their own permissions"),
)
if current_user.group != new_data.group:
# prevent a regular user from changing their group
raise HTTPException(
status.HTTP_403_FORBIDDEN, ErrorResponse.respond("User doesn't have permission to change their group")
)
if current_user.household != new_data.household:
# prevent a regular user from changing their household
raise HTTPException(
status.HTTP_403_FORBIDDEN,
ErrorResponse.respond("User doesn't have permission to change their household"),
)
def assert_user_change_allowed(user_id: UUID4, current_user: PrivateUser, new_data: UserBase):
if not current_user.admin:
_assert_non_admin_user_change_allowed(user_id, current_user, new_data)
return
if current_user.id != user_id:
raise HTTPException(status.HTTP_403_FORBIDDEN, ErrorResponse.respond("Use the Admin API to update other users"))
# Admin is trying to edit themselves
if any(getattr(current_user, p) != getattr(new_data, p) for p in permission_attrs):
# prevent an admin from excalating their own permissions
raise HTTPException(
status.HTTP_403_FORBIDDEN, ErrorResponse.respond("Admins can't change their own permissions")
)

View File

@@ -46,13 +46,6 @@ class AdminUserController(BaseAdminController):
@admin_router.delete("/{item_id}")
def delete_user(self, item_id: UUID4):
"""Removes a user from the database. Must be the current user or a super user"""
assert_user_change_allowed(item_id, self.user)
if item_id == 1: # TODO: identify super_user
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="SUPER_USER")
self.mixins.delete_one(item_id)
@@ -106,19 +99,7 @@ class UserController(BaseUserController):
@user_router.put("/{item_id}")
def update_user(self, item_id: UUID4, new_data: UserBase):
assert_user_change_allowed(item_id, self.user)
if not self.user.admin and (new_data.admin or self.user.group != new_data.group):
# prevent a regular user from doing admin tasks on themself
raise HTTPException(
status.HTTP_403_FORBIDDEN, ErrorResponse.respond("User doesn't have permission to change group")
)
if self.user.id == item_id and self.user.admin and not new_data.admin:
# prevent an admin from demoting themself
raise HTTPException(
status.HTTP_403_FORBIDDEN, ErrorResponse.respond("User doesn't have permission to change group")
)
assert_user_change_allowed(item_id, self.user, new_data)
try:
self.repos.users.update(item_id, new_data.model_dump())

View File

@@ -23,7 +23,7 @@ class UserImageController(BaseUserController):
):
"""Updates a User Image"""
with get_temporary_path() as temp_path:
assert_user_change_allowed(id, self.user)
assert_user_change_allowed(id, self.user, self.user)
temp_img = temp_path.joinpath(profile.filename)
with temp_img.open("wb") as buffer:

View File

@@ -54,7 +54,7 @@ class UserRatingsController(BaseUserController):
@router.post("/{id}/ratings/{slug}")
def set_rating(self, id: UUID4, slug: str, data: UserRatingUpdate):
"""Sets the user's rating for a recipe"""
assert_user_change_allowed(id, self.user)
assert_user_change_allowed(id, self.user, self.user)
recipe = self.get_recipe_or_404(slug)
user_rating = self.repos.user_ratings.get_by_user_and_recipe(id, recipe.id)