From 497e6080a3cb703621ae9b17f3ca2b0e1c4c3672 Mon Sep 17 00:00:00 2001 From: haolou Date: Mon, 20 Oct 2025 14:51:38 +0800 Subject: [PATCH 1/2] fix: create apis for magicleaps password things avoiding calling code depot --- .../backend/application/signin_hub.py | 6 ++ .../backend/business/signin_manager.py | 17 ++++++ .../backend/infra/auth/user_auth_handler.py | 25 +++++++++ .../services/auth/user_auth_service.py | 7 +++ .../webapi/routes/signin/__init__.py | 2 + .../signin/update_magicleaps_user_password.py | 55 +++++++++++++++++++ 6 files changed, 112 insertions(+) create mode 100644 apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py diff --git a/apps/authentication/backend/application/signin_hub.py b/apps/authentication/backend/application/signin_hub.py index 5455c4f..b5055d6 100644 --- a/apps/authentication/backend/application/signin_hub.py +++ b/apps/authentication/backend/application/signin_hub.py @@ -95,6 +95,12 @@ class SignInHub: user_id=user_id, password=password ) + @log_entry_exit_async + async def update_magicleaps_user_password(self, user_id: str, password: str) -> dict[str, any]: + return await self.signin_manager.update_magicleaps_user_password( + user_id=user_id, password=password + ) + @log_entry_exit_async async def send_email_code(self, sender_id: str, email: str) -> dict[str, any]: result = await self.signin_manager.send_email_code(sender_id, email) diff --git a/apps/authentication/backend/business/signin_manager.py b/apps/authentication/backend/business/signin_manager.py index 460efa0..c7c5403 100644 --- a/apps/authentication/backend/business/signin_manager.py +++ b/apps/authentication/backend/business/signin_manager.py @@ -368,6 +368,23 @@ class SignInManager: ) return {"succeeded": True} + async def update_magicleaps_user_password(self, user_id: str, password: str) -> dict[str, any]: + error_message = """ + Password does not pass complexity requirements: + - At least one lowercase character + - At least one uppercase character + - At least one digit + - At least one special character (punctuation, brackets, quotes, etc.) + """ + if not check_password_complexity(password): + raise InvalidDataError(error_message) + + user_flid = await self.user_auth_service.get_user_flid(user_id) + await self.user_auth_service.save_magicleaps_password_auth_method( + user_id, user_flid, password + ) + return {"succeeded": True} + async def send_email_code(self, sender_id: str, email: str) -> bool: mail_code = await self.user_auth_service.generate_auth_code_for_object( email, AuthType.EMAIL diff --git a/apps/authentication/backend/infra/auth/user_auth_handler.py b/apps/authentication/backend/infra/auth/user_auth_handler.py index 2661fcb..37afd5f 100644 --- a/apps/authentication/backend/infra/auth/user_auth_handler.py +++ b/apps/authentication/backend/infra/auth/user_auth_handler.py @@ -235,6 +235,31 @@ class UserAuthHandler: if not result: raise Exception("Failed to update user password in code depot") + async def save_magicleaps_password_auth_method(self, user_id: str, user_flid, password: str): + """save password auth method to user_password doc for MagicLeaps users (skips depot service) + + Args: + user_id (str): user id + password (str): user password + """ + password_hashed = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) + + user_password = await UserPasswordDoc.find( + UserPasswordDoc.user_id == user_id + ).first_or_none() + + if user_password is None: + new_user_password = UserPasswordDoc( + user_id=user_id, password=password_hashed + ) + await new_user_password.create() + else: + user_password.password = password_hashed + await user_password.save() + + # Skip depot service call for MagicLeaps users + # MagicLeaps users don't exist in Gitea, so we don't update depot password + async def reset_password(self, user_id: str): """clean password auth method from user_password doc diff --git a/apps/authentication/backend/services/auth/user_auth_service.py b/apps/authentication/backend/services/auth/user_auth_service.py index bab173f..cd529a3 100644 --- a/apps/authentication/backend/services/auth/user_auth_service.py +++ b/apps/authentication/backend/services/auth/user_auth_service.py @@ -51,3 +51,10 @@ class UserAuthService: return await self.user_auth_handler.save_password_auth_method( user_id, user_flid, password ) + + async def save_magicleaps_password_auth_method( + self, user_id: str, user_flid: str, password: str + ): + return await self.user_auth_handler.save_magicleaps_password_auth_method( + user_id, user_flid, password + ) diff --git a/apps/authentication/webapi/routes/signin/__init__.py b/apps/authentication/webapi/routes/signin/__init__.py index 2ee91b6..9294111 100644 --- a/apps/authentication/webapi/routes/signin/__init__.py +++ b/apps/authentication/webapi/routes/signin/__init__.py @@ -5,6 +5,7 @@ from .signin_with_email_and_password import router as se_router from .signin_with_email_and_code import router as sw_router from .signin_with_magicleaps_email_and_code import router as swm_router from .update_user_password import router as up_router +from .update_magicleaps_user_password import router as ump_router from .update_new_user_flid import router as uu_router from .reset_password_through_email import router as rp_router from .sign_out import router as so_router @@ -17,6 +18,7 @@ router.include_router(tms_router, prefix="/signin", tags=["signin"]) router.include_router(sw_router, prefix="/signin", tags=["signin"]) router.include_router(swm_router, prefix="/signin", tags=["signin"]) router.include_router(up_router, prefix="/signin", tags=["signin"]) +router.include_router(ump_router, prefix="/signin", tags=["signin"]) router.include_router(se_router, prefix="/signin", tags=["signin"]) router.include_router(so_router, prefix="/signin", tags=["signin"]) router.include_router(rp_router, prefix="/signin", tags=["signin"]) diff --git a/apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py b/apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py new file mode 100644 index 0000000..66ec03d --- /dev/null +++ b/apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py @@ -0,0 +1,55 @@ +from backend.application.signin_hub import SignInHub +from pydantic import BaseModel +from fastapi import APIRouter, Security, HTTPException +from common.token.token_manager import TokenManager +from fastapi.encoders import jsonable_encoder +from fastapi.responses import JSONResponse +from starlette.status import HTTP_401_UNAUTHORIZED +from jose import jwt +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from common.config.app_settings import app_settings + +router = APIRouter() +token_manager = TokenManager() +# Web API +# update_magicleaps_user_password +# + + +class RequestIn(BaseModel): + password: str + password2: str + + +@router.post( + "/update-magicleaps-user-password", + operation_id="magicleaps_update_user_password", + summary="update MagicLeaps user's sign-in password", + description="Update the MagicLeaps user's sign-in password without updating code depot. If the password was not set yet, this will enable the user to log in using the password", + response_description="signin_type:0 meaning simplified(using email) signin, \ + 1 meaning standard(using FLID and passward) signin", +) +async def update_magicleaps_user_password( + item: RequestIn, + credentials: HTTPAuthorizationCredentials = Security(HTTPBearer()), +): + payload = jwt.decode( + credentials.credentials, + app_settings.JWT_SECRET_KEY, + algorithms=[app_settings.JWT_ALGORITHM], + ) + + user_id = payload.get("subject").get("id") + if not user_id: + raise HTTPException( + status_code=HTTP_401_UNAUTHORIZED, detail="Could not validate credentials" + ) + if item.password != item.password2: + return JSONResponse( + content=jsonable_encoder( + {"error": "password and password2 are not the same"} + ) + ) + else: + result = await SignInHub().update_magicleaps_user_password(user_id, item.password) + return JSONResponse(content=jsonable_encoder(result)) From 2036c4b9ae5bd9d1d7b00daead0e047b6d9e4ebb Mon Sep 17 00:00:00 2001 From: haolou Date: Mon, 20 Oct 2025 15:03:59 +0800 Subject: [PATCH 2/2] fix: change name to more general ones --- .../backend/application/signin_hub.py | 4 ++-- .../backend/business/signin_manager.py | 4 ++-- .../backend/infra/auth/user_auth_handler.py | 7 +++---- .../backend/services/auth/user_auth_service.py | 4 ++-- .../webapi/routes/signin/__init__.py | 4 ++-- ...assword.py => update_user_password_no_depot.py} | 14 +++++++------- 6 files changed, 18 insertions(+), 19 deletions(-) rename apps/authentication/webapi/routes/signin/{update_magicleaps_user_password.py => update_user_password_no_depot.py} (74%) diff --git a/apps/authentication/backend/application/signin_hub.py b/apps/authentication/backend/application/signin_hub.py index b5055d6..834f302 100644 --- a/apps/authentication/backend/application/signin_hub.py +++ b/apps/authentication/backend/application/signin_hub.py @@ -96,8 +96,8 @@ class SignInHub: ) @log_entry_exit_async - async def update_magicleaps_user_password(self, user_id: str, password: str) -> dict[str, any]: - return await self.signin_manager.update_magicleaps_user_password( + async def update_user_password_no_depot(self, user_id: str, password: str) -> dict[str, any]: + return await self.signin_manager.update_user_password_no_depot( user_id=user_id, password=password ) diff --git a/apps/authentication/backend/business/signin_manager.py b/apps/authentication/backend/business/signin_manager.py index c7c5403..2469024 100644 --- a/apps/authentication/backend/business/signin_manager.py +++ b/apps/authentication/backend/business/signin_manager.py @@ -368,7 +368,7 @@ class SignInManager: ) return {"succeeded": True} - async def update_magicleaps_user_password(self, user_id: str, password: str) -> dict[str, any]: + async def update_user_password_no_depot(self, user_id: str, password: str) -> dict[str, any]: error_message = """ Password does not pass complexity requirements: - At least one lowercase character @@ -380,7 +380,7 @@ class SignInManager: raise InvalidDataError(error_message) user_flid = await self.user_auth_service.get_user_flid(user_id) - await self.user_auth_service.save_magicleaps_password_auth_method( + await self.user_auth_service.save_password_auth_method_no_depot( user_id, user_flid, password ) return {"succeeded": True} diff --git a/apps/authentication/backend/infra/auth/user_auth_handler.py b/apps/authentication/backend/infra/auth/user_auth_handler.py index 37afd5f..1d5bdf1 100644 --- a/apps/authentication/backend/infra/auth/user_auth_handler.py +++ b/apps/authentication/backend/infra/auth/user_auth_handler.py @@ -235,8 +235,8 @@ class UserAuthHandler: if not result: raise Exception("Failed to update user password in code depot") - async def save_magicleaps_password_auth_method(self, user_id: str, user_flid, password: str): - """save password auth method to user_password doc for MagicLeaps users (skips depot service) + async def save_password_auth_method_no_depot(self, user_id: str, user_flid, password: str): + """save password auth method to user_password doc without updating depot service Args: user_id (str): user id @@ -257,8 +257,7 @@ class UserAuthHandler: user_password.password = password_hashed await user_password.save() - # Skip depot service call for MagicLeaps users - # MagicLeaps users don't exist in Gitea, so we don't update depot password + # Skip depot service call - users don't exist in Gitea, so we don't update depot password async def reset_password(self, user_id: str): """clean password auth method from user_password doc diff --git a/apps/authentication/backend/services/auth/user_auth_service.py b/apps/authentication/backend/services/auth/user_auth_service.py index cd529a3..38cde61 100644 --- a/apps/authentication/backend/services/auth/user_auth_service.py +++ b/apps/authentication/backend/services/auth/user_auth_service.py @@ -52,9 +52,9 @@ class UserAuthService: user_id, user_flid, password ) - async def save_magicleaps_password_auth_method( + async def save_password_auth_method_no_depot( self, user_id: str, user_flid: str, password: str ): - return await self.user_auth_handler.save_magicleaps_password_auth_method( + return await self.user_auth_handler.save_password_auth_method_no_depot( user_id, user_flid, password ) diff --git a/apps/authentication/webapi/routes/signin/__init__.py b/apps/authentication/webapi/routes/signin/__init__.py index 9294111..1e6aab1 100644 --- a/apps/authentication/webapi/routes/signin/__init__.py +++ b/apps/authentication/webapi/routes/signin/__init__.py @@ -5,7 +5,7 @@ from .signin_with_email_and_password import router as se_router from .signin_with_email_and_code import router as sw_router from .signin_with_magicleaps_email_and_code import router as swm_router from .update_user_password import router as up_router -from .update_magicleaps_user_password import router as ump_router +from .update_user_password_no_depot import router as upnd_router from .update_new_user_flid import router as uu_router from .reset_password_through_email import router as rp_router from .sign_out import router as so_router @@ -18,7 +18,7 @@ router.include_router(tms_router, prefix="/signin", tags=["signin"]) router.include_router(sw_router, prefix="/signin", tags=["signin"]) router.include_router(swm_router, prefix="/signin", tags=["signin"]) router.include_router(up_router, prefix="/signin", tags=["signin"]) -router.include_router(ump_router, prefix="/signin", tags=["signin"]) +router.include_router(upnd_router, prefix="/signin", tags=["signin"]) router.include_router(se_router, prefix="/signin", tags=["signin"]) router.include_router(so_router, prefix="/signin", tags=["signin"]) router.include_router(rp_router, prefix="/signin", tags=["signin"]) diff --git a/apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py b/apps/authentication/webapi/routes/signin/update_user_password_no_depot.py similarity index 74% rename from apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py rename to apps/authentication/webapi/routes/signin/update_user_password_no_depot.py index 66ec03d..7781984 100644 --- a/apps/authentication/webapi/routes/signin/update_magicleaps_user_password.py +++ b/apps/authentication/webapi/routes/signin/update_user_password_no_depot.py @@ -12,7 +12,7 @@ from common.config.app_settings import app_settings router = APIRouter() token_manager = TokenManager() # Web API -# update_magicleaps_user_password +# update_user_password_no_depot # @@ -22,14 +22,14 @@ class RequestIn(BaseModel): @router.post( - "/update-magicleaps-user-password", - operation_id="magicleaps_update_user_password", - summary="update MagicLeaps user's sign-in password", - description="Update the MagicLeaps user's sign-in password without updating code depot. If the password was not set yet, this will enable the user to log in using the password", + "/update-user-password-no-depot", + operation_id="user_update_user_password_no_depot", + summary="update user's sign-in password without depot service", + description="Update the user's sign-in password without updating code depot. If the password was not set yet, this will enable the user to log in using the password", response_description="signin_type:0 meaning simplified(using email) signin, \ 1 meaning standard(using FLID and passward) signin", ) -async def update_magicleaps_user_password( +async def update_user_password_no_depot( item: RequestIn, credentials: HTTPAuthorizationCredentials = Security(HTTPBearer()), ): @@ -51,5 +51,5 @@ async def update_magicleaps_user_password( ) ) else: - result = await SignInHub().update_magicleaps_user_password(user_id, item.password) + result = await SignInHub().update_user_password_no_depot(user_id, item.password) return JSONResponse(content=jsonable_encoder(result))