From ca0bfb155f01f8a89ef500f82d232945885e87b8 Mon Sep 17 00:00:00 2001 From: icecheng Date: Mon, 21 Jul 2025 10:20:28 +0800 Subject: [PATCH] feat(role_management): add assign roles to user api --- .../infra/__init__.py} | 0 .../infra/permission/__init__.py} | 0 .../infra/permission/user_role_handler.py | 40 +++++++++++++++++++ .../backend/models/permission/__init__.py | 4 +- .../backend/models/permission/models.py | 15 ++++++- .../backend/models/user/models.py | 1 - .../services/user/user_management_service.py | 11 ++++- apps/authentication/webapi/routes/__init__.py | 4 +- .../webapi/routes/user/__init__.py | 6 +++ .../webapi/routes/user/assign_roles.py | 30 ++++++++++++++ 10 files changed, 105 insertions(+), 6 deletions(-) rename apps/authentication/{webapi/routes/user/assign_role.py => backend/infra/__init__.py} (100%) rename apps/authentication/{webapi/routes/user/remove_role.py => backend/infra/permission/__init__.py} (100%) create mode 100644 apps/authentication/backend/infra/permission/user_role_handler.py create mode 100644 apps/authentication/webapi/routes/user/assign_roles.py diff --git a/apps/authentication/webapi/routes/user/assign_role.py b/apps/authentication/backend/infra/__init__.py similarity index 100% rename from apps/authentication/webapi/routes/user/assign_role.py rename to apps/authentication/backend/infra/__init__.py diff --git a/apps/authentication/webapi/routes/user/remove_role.py b/apps/authentication/backend/infra/permission/__init__.py similarity index 100% rename from apps/authentication/webapi/routes/user/remove_role.py rename to apps/authentication/backend/infra/permission/__init__.py diff --git a/apps/authentication/backend/infra/permission/user_role_handler.py b/apps/authentication/backend/infra/permission/user_role_handler.py new file mode 100644 index 0000000..4975147 --- /dev/null +++ b/apps/authentication/backend/infra/permission/user_role_handler.py @@ -0,0 +1,40 @@ +from typing import Optional, List +from fastapi.exceptions import RequestValidationError +from backend.models.permission.models import RoleDoc, UserRoleDoc +from beanie import PydanticObjectId + + +class UserRoleHandler: + def __init__(self): + pass + + async def assign_roles_to_user(self, user_id: str, role_ids: List[str]) -> Optional[UserRoleDoc]: + """Assign roles to a user by updating or creating the UserRoleDoc""" + if not user_id or not role_ids: + raise RequestValidationError("user_id and role_ids are required.") + + # Validate that all role_ids exist in the role collection + for role_id in role_ids: + role_doc = await RoleDoc.get(PydanticObjectId(role_id)) + if not role_doc: + raise RequestValidationError(f"Role with id {role_id} not found.") + + # Remove duplicates from role_ids + unique_role_ids = list(dict.fromkeys(role_ids)) + + # Check if UserRoleDoc already exists for this user + existing_user_role = await UserRoleDoc.find_one(UserRoleDoc.user_id == user_id) + + if existing_user_role: + # Update existing UserRoleDoc + existing_user_role.role_ids = unique_role_ids + await existing_user_role.save() + return existing_user_role + else: + # Create new UserRoleDoc + user_role_doc = UserRoleDoc( + user_id=user_id, + role_ids=unique_role_ids + ) + await user_role_doc.insert() + return user_role_doc diff --git a/apps/authentication/backend/models/permission/__init__.py b/apps/authentication/backend/models/permission/__init__.py index 4a14d68..cf41ebd 100644 --- a/apps/authentication/backend/models/permission/__init__.py +++ b/apps/authentication/backend/models/permission/__init__.py @@ -1,3 +1,3 @@ -from .models import PermissionDoc, RoleDoc +from .models import PermissionDoc, RoleDoc, UserRoleDoc -permission_models = [PermissionDoc, RoleDoc] +permission_models = [PermissionDoc, RoleDoc, UserRoleDoc] diff --git a/apps/authentication/backend/models/permission/models.py b/apps/authentication/backend/models/permission/models.py index 423495d..b87c2f5 100644 --- a/apps/authentication/backend/models/permission/models.py +++ b/apps/authentication/backend/models/permission/models.py @@ -1,6 +1,6 @@ from beanie import Document from datetime import datetime -from typing import Optional +from typing import Optional, List class PermissionDoc(Document): @@ -34,4 +34,17 @@ class RoleDoc(Document): name = "_role" indexes = [ "role_level" + ] + +class UserRoleDoc(Document): + """User role doc""" + user_id: str + role_ids: Optional[List[str]] + + class Settings: + # Default collections created by Freeleaps for tenant databases use '_' prefix + # to prevent naming conflicts with tenant-created collections + name = "_user_role" + indexes = [ + "user_id" ] \ No newline at end of file diff --git a/apps/authentication/backend/models/user/models.py b/apps/authentication/backend/models/user/models.py index 97ad23a..2fe9c21 100644 --- a/apps/authentication/backend/models/user/models.py +++ b/apps/authentication/backend/models/user/models.py @@ -18,7 +18,6 @@ class UserAccountDoc(Document): service_plan_id: Optional[str] properties: UserAccountProperty capabilities: Capability - user_role_ids: List[str] user_role: int = AdministrativeRole.NONE preferred_region: UserRegion = UserRegion.ZH_CN diff --git a/apps/authentication/backend/services/user/user_management_service.py b/apps/authentication/backend/services/user/user_management_service.py index bccdacd..98d2fb6 100644 --- a/apps/authentication/backend/services/user/user_management_service.py +++ b/apps/authentication/backend/services/user/user_management_service.py @@ -1,5 +1,6 @@ +from backend.models.permission.models import UserRoleDoc from common.log.module_logger import ModuleLogger -from typing import Optional +from typing import Optional, List from backend.models.user.constants import ( NewUserMethod, @@ -16,6 +17,9 @@ from backend.infra.auth.user_auth_handler import ( from backend.infra.user_profile.user_profile_handler import ( UserProfileHandler, ) +from backend.infra.permission.user_role_handler import ( + UserRoleHandler, +) from common.log.log_utils import log_entry_exit_async from common.constants.region import UserRegion @@ -24,6 +28,7 @@ class UserManagementService: def __init__(self) -> None: self.user_auth_handler = UserAuthHandler() self.user_profile_handler = UserProfileHandler() + self.user_role_handler = UserRoleHandler() self.module_logger = ModuleLogger(sender_id=UserManagementService) @log_entry_exit_async @@ -97,3 +102,7 @@ class UserManagementService: async def get_account_by_id(self, user_id: str) -> UserAccountDoc: return await self.user_profile_handler.get_account_by_id(user_id) + + async def assign_roles_to_user(self, user_id: str, role_ids: List[str]) -> UserRoleDoc: + """Assign roles to a user by updating or creating the UserRoleDoc""" + return await self.user_role_handler.assign_roles_to_user(user_id, role_ids) diff --git a/apps/authentication/webapi/routes/__init__.py b/apps/authentication/webapi/routes/__init__.py index 19a5ec5..2c8a9ee 100644 --- a/apps/authentication/webapi/routes/__init__.py +++ b/apps/authentication/webapi/routes/__init__.py @@ -4,11 +4,13 @@ from .tokens import router as token_router from .auth import router as auth_router from .permission import router as permission_router from .role import router as role_router +from .user import router as user_router api_router = APIRouter(prefix="/auth") -api_router.include_router(signin_router, tags=["user"]) +api_router.include_router(signin_router, tags=["signin"]) api_router.include_router(token_router, tags=["token"]) api_router.include_router(auth_router, tags=["auth"]) api_router.include_router(permission_router, tags=["permission"]) api_router.include_router(role_router, tags=["role"]) +api_router.include_router(user_router, tags=["user"]) websocket_router = APIRouter() diff --git a/apps/authentication/webapi/routes/user/__init__.py b/apps/authentication/webapi/routes/user/__init__.py index e69de29..a114692 100644 --- a/apps/authentication/webapi/routes/user/__init__.py +++ b/apps/authentication/webapi/routes/user/__init__.py @@ -0,0 +1,6 @@ +from fastapi import APIRouter +from .assign_roles import router as assign_role_router + +router = APIRouter() + +router.include_router(assign_role_router, prefix="/user", tags=["user"]) diff --git a/apps/authentication/webapi/routes/user/assign_roles.py b/apps/authentication/webapi/routes/user/assign_roles.py new file mode 100644 index 0000000..0df9d5a --- /dev/null +++ b/apps/authentication/webapi/routes/user/assign_roles.py @@ -0,0 +1,30 @@ +from fastapi import APIRouter +from pydantic import BaseModel +from typing import List, Optional +from backend.services.user.user_management_service import UserManagementService +from common.token.token_manager import TokenManager + +router = APIRouter() +token_manager = TokenManager() +user_management_service = UserManagementService() + +class AssignRolesRequest(BaseModel): + user_id: str + role_ids: List[str] + +class UserRoleResponse(BaseModel): + user_id: str + role_ids: Optional[List[str]] + +@router.post( + "/assign-roles", + response_model=UserRoleResponse, + operation_id="assign-roles-to-user", + summary="Assign Roles to User", + description="Assign roles to a user by updating or creating the UserRoleDoc." +) +async def assign_roles_to_user( + req: AssignRolesRequest, +) -> UserRoleResponse: + doc = await user_management_service.assign_roles_to_user(req.user_id, req.role_ids) + return UserRoleResponse(**doc.dict())