mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-12-13 05:45:22 -05:00
feat(backend): ✨ refactor/fix group management for admins (#838)
* fix(frontend): 🐛 update dialog implementation to simplify state management * test(backend): ✅ refactor test fixtures + admin group tests * chore(backend): 🔨 add launcher.json for python debugging (tests) * fix typing * feat(backend): ✨ refactor/fix group management for admins * feat(frontend): ✨ add/fix admin group management * add LDAP checker Co-authored-by: hay-kot <hay-kot@pm.me>
This commit is contained in:
@@ -16,7 +16,7 @@ pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
ALGORITHM = "HS256"
|
||||
|
||||
|
||||
def create_access_token(data: dict(), expires_delta: timedelta = None) -> str:
|
||||
def create_access_token(data: dict, expires_delta: timedelta = None) -> str:
|
||||
settings = get_app_settings()
|
||||
|
||||
to_encode = data.copy()
|
||||
@@ -78,7 +78,7 @@ def user_from_ldap(db: Database, session, username: str, password: str) -> Priva
|
||||
return user
|
||||
|
||||
|
||||
def authenticate_user(session, email: str, password: str) -> PrivateUser | False:
|
||||
def authenticate_user(session, email: str, password: str) -> PrivateUser | bool:
|
||||
settings = get_app_settings()
|
||||
|
||||
db = get_database(session)
|
||||
|
||||
@@ -99,8 +99,8 @@ class AppSettings(BaseSettings):
|
||||
self.LDAP_BIND_TEMPLATE,
|
||||
self.LDAP_ADMIN_FILTER,
|
||||
}
|
||||
|
||||
return "" not in required and None not in required and self.LDAP_AUTH_ENABLED
|
||||
not_none = None not in required
|
||||
return self.LDAP_AUTH_ENABLED and not_none
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from mealie.routes.routers import AdminAPIRouter
|
||||
from mealie.services._base_http_service.router_factory import RouterFactory
|
||||
from mealie.services.admin.admin_group_service import AdminGroupService
|
||||
from mealie.services.admin.admin_user_service import AdminUserService
|
||||
|
||||
from . import admin_about, admin_email, admin_group, admin_log, admin_server_tasks
|
||||
from . import admin_about, admin_email, admin_log, admin_server_tasks
|
||||
|
||||
router = AdminAPIRouter(prefix="/admin")
|
||||
|
||||
router.include_router(admin_about.router, tags=["Admin: About"])
|
||||
router.include_router(admin_log.router, tags=["Admin: Log"])
|
||||
router.include_router(admin_group.router, tags=["Admin: Group"])
|
||||
router.include_router(RouterFactory(AdminUserService, prefix="/users", tags=["Admin: Users"]))
|
||||
router.include_router(RouterFactory(AdminGroupService, prefix="/groups", tags=["Admin: Groups"]))
|
||||
router.include_router(admin_email.router, tags=["Admin: Email"])
|
||||
router.include_router(admin_server_tasks.router, tags=["Admin: Server Tasks"])
|
||||
|
||||
@@ -47,5 +47,6 @@ async def check_app_config():
|
||||
|
||||
return CheckAppConfig(
|
||||
email_ready=settings.SMTP_ENABLE,
|
||||
ldap_ready=settings.LDAP_ENABLED,
|
||||
base_url_set=url_set,
|
||||
)
|
||||
|
||||
@@ -5,11 +5,10 @@ from fastapi.responses import JSONResponse
|
||||
from mealie.core.config import get_app_settings
|
||||
from mealie.core.root_logger import get_logger
|
||||
|
||||
logger = get_logger(__name__)
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
def log_wrapper(request: Request, e):
|
||||
|
||||
logger.error("Start 422 Error".center(60, "-"))
|
||||
logger.error(f"{request.method} {request.url}")
|
||||
logger.error(f"error is {e}")
|
||||
|
||||
@@ -27,4 +27,5 @@ class AdminAboutInfo(AppInfo):
|
||||
|
||||
class CheckAppConfig(CamelModel):
|
||||
email_ready: bool = False
|
||||
ldap_ready: bool = False
|
||||
base_url_set: bool = False
|
||||
|
||||
9
mealie/schema/group/group.py
Normal file
9
mealie/schema/group/group.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from fastapi_camelcase import CamelModel
|
||||
|
||||
from .group_preferences import UpdateGroupPreferences
|
||||
|
||||
|
||||
class GroupAdminUpdate(CamelModel):
|
||||
id: int
|
||||
name: str
|
||||
preferences: UpdateGroupPreferences
|
||||
18
mealie/schema/mapper.py
Normal file
18
mealie/schema/mapper.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
T = TypeVar("T", bound=BaseModel)
|
||||
U = TypeVar("U", bound=BaseModel)
|
||||
|
||||
|
||||
def mapper(source: U, dest: T, **kwargs) -> Generic[T]:
|
||||
"""
|
||||
Map a source model to a destination model. Only top-level fields are mapped.
|
||||
"""
|
||||
|
||||
for field in source.__fields__:
|
||||
if field in dest.__fields__:
|
||||
setattr(dest, field, getattr(source, field))
|
||||
|
||||
return dest
|
||||
59
mealie/services/admin/admin_group_service.py
Normal file
59
mealie/services/admin/admin_group_service.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import cached_property
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
|
||||
from mealie.schema.group.group import GroupAdminUpdate
|
||||
from mealie.schema.mapper import mapper
|
||||
from mealie.schema.response import ErrorResponse
|
||||
from mealie.schema.user.user import GroupBase, GroupInDB
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import AdminHttpService
|
||||
from mealie.services.events import create_group_event
|
||||
from mealie.services.group_services.group_utils import create_new_group
|
||||
|
||||
|
||||
class AdminGroupService(
|
||||
CrudHttpMixins[GroupBase, GroupInDB, GroupAdminUpdate],
|
||||
AdminHttpService[int, GroupInDB],
|
||||
):
|
||||
event_func = create_group_event
|
||||
_schema = GroupInDB
|
||||
|
||||
@cached_property
|
||||
def dal(self):
|
||||
return self.db.groups
|
||||
|
||||
def populate_item(self, id: int) -> GroupInDB:
|
||||
self.item = self.dal.get_one(id)
|
||||
return self.item
|
||||
|
||||
def get_all(self) -> list[GroupInDB]:
|
||||
return self.dal.get_all()
|
||||
|
||||
def create_one(self, data: GroupBase) -> GroupInDB:
|
||||
return create_new_group(self.db, data)
|
||||
|
||||
def update_one(self, data: GroupAdminUpdate, item_id: int = None) -> GroupInDB:
|
||||
target_id = item_id or data.id
|
||||
|
||||
if data.preferences:
|
||||
preferences = self.db.group_preferences.get_one(value=target_id, key="group_id")
|
||||
preferences = mapper(data.preferences, preferences)
|
||||
self.item.preferences = self.db.group_preferences.update(preferences.id, preferences)
|
||||
|
||||
if data.name not in ["", self.item.name]:
|
||||
self.item.name = data.name
|
||||
self.item = self.dal.update(target_id, self.item)
|
||||
|
||||
return self.item
|
||||
|
||||
def delete_one(self, id: int = None) -> GroupInDB:
|
||||
if len(self.item.users) > 0:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ErrorResponse(message="Cannot delete group with users").dict(),
|
||||
)
|
||||
|
||||
return self._delete_one(id)
|
||||
@@ -5,14 +5,14 @@ from functools import cached_property
|
||||
from mealie.schema.user.user import UserIn, UserOut
|
||||
from mealie.services._base_http_service.crud_http_mixins import CrudHttpMixins
|
||||
from mealie.services._base_http_service.http_services import AdminHttpService
|
||||
from mealie.services.events import create_recipe_event
|
||||
from mealie.services.events import create_user_event
|
||||
|
||||
|
||||
class AdminUserService(
|
||||
CrudHttpMixins[UserOut, UserIn, UserIn],
|
||||
AdminHttpService[int, UserOut],
|
||||
):
|
||||
event_func = create_recipe_event
|
||||
event_func = create_user_event
|
||||
_schema = UserOut
|
||||
|
||||
@cached_property
|
||||
|
||||
Reference in New Issue
Block a user