From a97605c478ac37cf2da47931f25aec5013cd34b2 Mon Sep 17 00:00:00 2001 From: dev1lfreak Date: Mon, 15 Jun 2026 11:24:33 +0300 Subject: [PATCH] feat: add set password by email and by id --- src/application/commands/set_password.py | 16 ++++++++++++++-- src/presentation/routing/search.py | 2 +- src/presentation/routing/users.py | 12 ++++++++++++ src/presentation/schemas/password.py | 1 + 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/application/commands/set_password.py b/src/application/commands/set_password.py index af960db..c5547e9 100644 --- a/src/application/commands/set_password.py +++ b/src/application/commands/set_password.py @@ -1,6 +1,6 @@ from src.application.abstractions import IUnitOfWork from src.application.contracts import IHashService, ILogger -from src.application.domain.exceptions import ApplicationException +from src.application.domain.exceptions import ApplicationException, BadRequestException from src.infrastructure.database.decorators import transactional @@ -16,9 +16,21 @@ class SetPasswordCommand: self._logger = logger @transactional - async def __call__(self, user_id: str, password: str) -> bool: + async def __call__( + self, + password: str, + email: str | None = None, + user_id: str | None = None, + ) -> bool: try: password_hash = await self._hash_service.hash(password) + + if email == None and user_id == None: + raise BadRequestException('An email is required') + + if email != None: + user = await self._unit_of_work.user_repository.get_user_by_email(email) + user_id = user.id await self._unit_of_work.user_repository.set_password( user_id=user_id, password_hash=password_hash, diff --git a/src/presentation/routing/search.py b/src/presentation/routing/search.py index c19fae5..fb64b6d 100644 --- a/src/presentation/routing/search.py +++ b/src/presentation/routing/search.py @@ -23,7 +23,7 @@ async def search_parties( q: str = Query(min_length=1, max_length=255), limit: int = Query(default=50, ge=1, le=200), offset: int = Query(default=0, ge=0), -# auth: AdminAuthContext = Depends(require_admin_access), + auth: AdminAuthContext = Depends(require_admin_access), command: SearchPartiesCommand = Depends(get_search_parties_command), ): items, total = await command(query=q, limit=limit, offset=offset) diff --git a/src/presentation/routing/users.py b/src/presentation/routing/users.py index a68e60a..82fb992 100644 --- a/src/presentation/routing/users.py +++ b/src/presentation/routing/users.py @@ -104,6 +104,18 @@ async def get_user_secret_keys( for k in keys ] + +@users_router.patch(path='/change_password', response_class=ORJSONResponse, status_code=status.HTTP_200_OK) +async def change_password( + request: Request, + body: SetPasswordRequest, + auth: AdminAuthContext = Depends(require_admin_role('compliance', 'superadmin')), + command: SetPasswordCommand = Depends(get_set_password_command), +): + await command(email=body.email, password=body.password) + return ORJSONResponse(content={'message': 'Password updated successfully'}) + + @users_router.patch(path='/{user_id}/set_password', response_class=ORJSONResponse, status_code=status.HTTP_200_OK) async def set_password( user_id: str, diff --git a/src/presentation/schemas/password.py b/src/presentation/schemas/password.py index bf0be5b..3be5e7c 100644 --- a/src/presentation/schemas/password.py +++ b/src/presentation/schemas/password.py @@ -4,6 +4,7 @@ from src.application.domain.password_policy import validate_password_strength class SetPasswordRequest(BaseModel): + email: str | None = None password: str @field_validator('password')