feat(role_management): add crud for role and permission
This commit is contained in:
parent
40e0fafc2c
commit
2382f0bece
0
apps/authentication/__init__.py
Normal file
0
apps/authentication/__init__.py
Normal file
0
apps/authentication/backend/__init__.py
Normal file
0
apps/authentication/backend/__init__.py
Normal file
0
apps/authentication/backend/annotation/__init__.py
Normal file
0
apps/authentication/backend/annotation/__init__.py
Normal file
@ -0,0 +1,76 @@
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
|
||||
from backend.models.permission.models import PermissionDoc
|
||||
from beanie import PydanticObjectId
|
||||
from datetime import datetime
|
||||
|
||||
class PermissionHandler:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
async def create_permission(self, permission_key: str, permission_name: str,
|
||||
description: Optional[str] = None) -> Optional[PermissionDoc]:
|
||||
"""Create a new permission document"""
|
||||
if not permission_key or not permission_name:
|
||||
raise RequestValidationError("permission_key and permission_name are required.")
|
||||
# if exists.
|
||||
if await PermissionDoc.find_one(
|
||||
{str(PermissionDoc.permission_key): permission_key}) or await PermissionDoc.find_one(
|
||||
{str(PermissionDoc.permission_name): permission_name}):
|
||||
raise RequestValidationError("permission has already been created.")
|
||||
doc = PermissionDoc(
|
||||
permission_key=permission_key,
|
||||
permission_name=permission_name,
|
||||
description=description,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
await doc.insert()
|
||||
return doc
|
||||
|
||||
async def update_permission(self, permission_id: PydanticObjectId, permission_key: Optional[str] = None,
|
||||
permission_name: Optional[str] = None, description: Optional[str] = None) -> Optional[
|
||||
PermissionDoc]:
|
||||
"""Update an existing permission document by id, ensuring permission_key is unique"""
|
||||
if not permission_id or not permission_key or not permission_name:
|
||||
raise RequestValidationError("permission_id, permission_key and permission_name is required.")
|
||||
doc = await PermissionDoc.get(permission_id)
|
||||
if not doc:
|
||||
raise RequestValidationError("Permission not found.")
|
||||
# Check for uniqueness (exclude self)
|
||||
if permission_key and permission_name:
|
||||
conflict = await PermissionDoc.find_one({
|
||||
"$and": [
|
||||
{"_id": {"$ne": permission_id}},
|
||||
{"$or": [
|
||||
{str(PermissionDoc.permission_key): permission_key},
|
||||
{str(PermissionDoc.permission_name): permission_name}
|
||||
]}
|
||||
]
|
||||
})
|
||||
if conflict:
|
||||
raise RequestValidationError("Permission name or permission key already exists.")
|
||||
doc.permission_key = permission_key
|
||||
doc.updated_at = datetime.now()
|
||||
await doc.save()
|
||||
return doc
|
||||
|
||||
async def query_permissions(
|
||||
self,
|
||||
permission_key: Optional[str] = None,
|
||||
permission_name: Optional[str] = None,
|
||||
skip: int = 0,
|
||||
limit: int = 10
|
||||
) -> Tuple[List[PermissionDoc], int]:
|
||||
"""Query permissions with pagination and fuzzy search"""
|
||||
query = {}
|
||||
if permission_key:
|
||||
query[str(PermissionDoc.permission_key)] = {"$regex": permission_key, "$options": "i"}
|
||||
if permission_name:
|
||||
query[str(PermissionDoc.permission_name)] = {"$regex": permission_name, "$options": "i"}
|
||||
cursor = PermissionDoc.find(query)
|
||||
total = await cursor.count()
|
||||
docs = await cursor.skip(skip).limit(limit).to_list()
|
||||
return docs, total
|
||||
73
apps/authentication/backend/infra/permission/role_handler.py
Normal file
73
apps/authentication/backend/infra/permission/role_handler.py
Normal file
@ -0,0 +1,73 @@
|
||||
from typing import Optional, List, Tuple
|
||||
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
|
||||
from backend.models.permission.models import RoleDoc
|
||||
from beanie import PydanticObjectId
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class RoleHandler:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
async def create_role(self, role_key: str, role_name: str, role_description: Optional[str], role_level: int) -> Optional[RoleDoc]:
|
||||
"""Create a new role, ensuring role_key and role_name are unique and not empty"""
|
||||
if not role_key or not role_name:
|
||||
raise RequestValidationError("role_key and role_name are required.")
|
||||
if await RoleDoc.find_one({str(RoleDoc.role_key): role_key}) or await RoleDoc.find_one(
|
||||
{str(RoleDoc.role_name): role_name}):
|
||||
raise RequestValidationError("role_key or role_name has already been created.")
|
||||
doc = RoleDoc(
|
||||
role_key=role_key,
|
||||
role_name=role_name,
|
||||
role_description=role_description,
|
||||
permission_ids=[],
|
||||
role_level=role_level,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
await doc.insert()
|
||||
return doc
|
||||
|
||||
async def update_role(self, role_id: PydanticObjectId, role_key: str, role_name: str,
|
||||
role_description: Optional[str], role_level: int) -> Optional[
|
||||
RoleDoc]:
|
||||
"""Update an existing role, ensuring role_key and role_name are unique and not empty"""
|
||||
if not role_id or not role_key or not role_name:
|
||||
raise RequestValidationError("role_id, role_key and role_name are required.")
|
||||
doc = await RoleDoc.get(role_id)
|
||||
if not doc:
|
||||
raise RequestValidationError("role not found.")
|
||||
# Check for uniqueness (exclude self)
|
||||
conflict = await RoleDoc.find_one({
|
||||
"$and": [
|
||||
{"_id": {"$ne": role_id}},
|
||||
{"$or": [
|
||||
{str(RoleDoc.role_key): role_key},
|
||||
{str(RoleDoc.role_name): role_name}
|
||||
]}
|
||||
]
|
||||
})
|
||||
if conflict:
|
||||
raise RequestValidationError("role_key or role_name already exists.")
|
||||
doc.role_key = role_key
|
||||
doc.role_name = role_name
|
||||
doc.role_description = role_description
|
||||
doc.role_level = role_level
|
||||
doc.updated_at = datetime.now()
|
||||
await doc.save()
|
||||
return doc
|
||||
|
||||
async def query_roles(self, role_key: Optional[str], role_name: Optional[str], skip: int = 0, limit: int = 10) -> \
|
||||
Tuple[List[RoleDoc], int]:
|
||||
"""Query roles with pagination and fuzzy search by role_key and role_name"""
|
||||
query = {}
|
||||
if role_key:
|
||||
query[str(RoleDoc.role_key)] = {"$regex": role_key, "$options": "i"}
|
||||
if role_name:
|
||||
query[str(RoleDoc.role_name)] = {"$regex": role_name, "$options": "i"}
|
||||
cursor = RoleDoc.find(query)
|
||||
total = await cursor.count()
|
||||
docs = await cursor.skip(skip).limit(limit).to_list()
|
||||
return docs, total
|
||||
@ -1,6 +1,8 @@
|
||||
from .user import user_models
|
||||
from .user_profile import profile_models
|
||||
from .permission import permission_models
|
||||
|
||||
backend_models = []
|
||||
backend_models.extend(user_models)
|
||||
backend_models.extend(profile_models)
|
||||
backend_models.extend(permission_models)
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
from .models import PermissionDoc, RoleDoc
|
||||
|
||||
permission_models = [PermissionDoc, RoleDoc]
|
||||
@ -1,4 +1,31 @@
|
||||
from enum import IntEnum
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum, Enum
|
||||
|
||||
|
||||
@dataclass(frozen=True) # frozen=True
|
||||
class DefaultRole:
|
||||
role_name: str
|
||||
role_key: str
|
||||
role_description: str
|
||||
role_level: int
|
||||
|
||||
|
||||
# Default roles, which all tenants will have, cannot be modified.
|
||||
class DefaultRoleEnum(Enum):
|
||||
ADMIN = DefaultRole("Administrator", "admin", "Have all permissions", 0)
|
||||
|
||||
|
||||
@dataclass(frozen=True) # frozen=True
|
||||
class DefaultPermission:
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
permission_description: str
|
||||
|
||||
|
||||
# Default permissions, which all tenants will have, cannot be modified.
|
||||
class DefaultPermissionEnum(Enum):
|
||||
CHANGE_ROLES = DefaultPermission("change:roles", "Change roles", "Add/Update/Delete roles")
|
||||
CHANGE_PERMISSIONS = DefaultPermission("change:permissions", "Change permissions", "Add/Update/Remove permissions")
|
||||
|
||||
|
||||
class AdministrativeRole(IntEnum):
|
||||
|
||||
37
apps/authentication/backend/models/permission/models.py
Normal file
37
apps/authentication/backend/models/permission/models.py
Normal file
@ -0,0 +1,37 @@
|
||||
from beanie import Document
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class PermissionDoc(Document):
|
||||
permission_name: str
|
||||
permission_key: str
|
||||
description: Optional[str] = None # Description of the permission, optional
|
||||
created_at: datetime = datetime.now() # Creation timestamp, auto-generated
|
||||
updated_at: datetime = datetime.now() # Last update timestamp, auto-updated
|
||||
|
||||
class Settings:
|
||||
# Default collections created by Freeleaps for tenant databases use '_' prefix
|
||||
# to prevent naming conflicts with tenant-created collections
|
||||
name = "_permission"
|
||||
indexes = [
|
||||
"permission_key"
|
||||
]
|
||||
|
||||
|
||||
class RoleDoc(Document):
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
permission_ids: list[str]
|
||||
role_level: int
|
||||
created_at: datetime = datetime.now() # Creation timestamp, auto-generated
|
||||
updated_at: datetime = datetime.now() # Last update timestamp, auto-updated
|
||||
|
||||
class Settings:
|
||||
# Default collections created by Freeleaps for tenant databases use '_' prefix
|
||||
# to prevent naming conflicts with tenant-created collections
|
||||
name = "_role"
|
||||
indexes = [
|
||||
"role_level"
|
||||
]
|
||||
@ -1,4 +1,4 @@
|
||||
from typing import Optional
|
||||
from typing import Optional, List
|
||||
|
||||
from beanie import Document
|
||||
|
||||
@ -18,6 +18,7 @@ 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
|
||||
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
|
||||
from backend.infra.permission.permission_handler import PermissionHandler
|
||||
from backend.models.permission.models import PermissionDoc
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
class PermissionService:
|
||||
def __init__(self):
|
||||
self.permission_handler = PermissionHandler()
|
||||
|
||||
async def create_permission(self, permission_key: str, permission_name: str, description: Optional[str] = None) -> PermissionDoc:
|
||||
"""Create a new permission document"""
|
||||
return await self.permission_handler.create_permission(permission_key, permission_name, description)
|
||||
|
||||
async def update_permission(self, permission_id: str, permission_key: Optional[str] = None, permission_name: Optional[str] = None, description: Optional[str] = None) -> PermissionDoc:
|
||||
"""Update an existing permission document by id"""
|
||||
return await self.permission_handler.update_permission(PydanticObjectId(permission_id), permission_key, permission_name, description)
|
||||
|
||||
async def query_permissions(self, permission_key: Optional[str] = None, permission_name: Optional[str] = None, page: int = 1, page_size: int = 10) -> Dict[str, Any]:
|
||||
"""Query permissions with pagination and fuzzy search"""
|
||||
if page < 1 or page_size < 1:
|
||||
raise RequestValidationError("page and page_size must be positive integers.")
|
||||
skip = (page - 1) * page_size
|
||||
docs, total = await self.permission_handler.query_permissions(permission_key, permission_name, skip, page_size)
|
||||
return {
|
||||
"items": [doc.dict() for doc in docs],
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
from typing import Optional, Dict, Any
|
||||
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
|
||||
from backend.infra.permission.role_handler import RoleHandler
|
||||
from backend.models.permission.models import RoleDoc
|
||||
from beanie import PydanticObjectId
|
||||
|
||||
class RoleService:
|
||||
def __init__(self):
|
||||
self.role_handler = RoleHandler()
|
||||
|
||||
async def create_role(self, role_key: str, role_name: str, role_description: Optional[str], role_level: int) -> RoleDoc:
|
||||
"""Create a new role, ensuring role_key and role_name are unique and not empty"""
|
||||
|
||||
doc = await self.role_handler.create_role(role_key, role_name, role_description, role_level)
|
||||
return doc
|
||||
|
||||
async def update_role(self, role_id: str, role_key: str, role_name: str, role_description: Optional[str], role_level: int) -> RoleDoc:
|
||||
"""Update an existing role, ensuring role_key and role_name are unique and not empty"""
|
||||
|
||||
doc = await self.role_handler.update_role(PydanticObjectId(role_id), role_key, role_name, role_description, role_level)
|
||||
return doc
|
||||
|
||||
async def query_roles(self, role_key: Optional[str], role_name: Optional[str], page: int = 1, page_size: int = 10) -> Dict[str, Any]:
|
||||
"""Query roles with pagination and fuzzy search by role_key and role_name"""
|
||||
if page < 1 or page_size < 1:
|
||||
raise RequestValidationError("page and page_size must be positive integers.")
|
||||
skip = (page - 1) * page_size
|
||||
docs, total = await self.role_handler.query_roles(role_key, role_name, skip, page_size)
|
||||
return {
|
||||
"items": [doc.dict() for doc in docs],
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": page_size
|
||||
}
|
||||
0
apps/authentication/common/__init__.py
Normal file
0
apps/authentication/common/__init__.py
Normal file
24
apps/authentication/local.env
Normal file
24
apps/authentication/local.env
Normal file
@ -0,0 +1,24 @@
|
||||
APP_NAME=authentication
|
||||
export SERVICE_API_ACCESS_HOST=0.0.0.0
|
||||
export SERVICE_API_ACCESS_PORT=8004
|
||||
export CONTAINER_APP_ROOT=/app
|
||||
export LOG_BASE_PATH=$CONTAINER_APP_ROOT/log/$APP_NAME
|
||||
export BACKEND_LOG_FILE_NAME=$APP_NAME
|
||||
export APPLICATION_ACTIVITY_LOG=$APP_NAME-activity
|
||||
export MONGODB_NAME=freeleaps2
|
||||
export MONGODB_PORT=27017
|
||||
export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0
|
||||
GIT_REPO_ROOT=/mnt/freeleaps/freeleaps-service-hub
|
||||
CODEBASE_ROOT=/mnt/freeleaps/freeleaps-service-hub/apps/authentication
|
||||
SITE_DEPLOY_FOLDER=/mnt/freeleaps/freeleaps-service-hub/sites/authentication/deploy
|
||||
#!/bin/bash
|
||||
export VENV_DIR=venv_t
|
||||
export VENV_ACTIVATE=venv_t/bin/activate
|
||||
export DOCKER_HOME=/var/lib/docker
|
||||
export DOCKER_APP_HOME=$DOCKER_HOME/app
|
||||
export DOCKER_BACKEND_HOME=$DOCKER_APP_HOME/$APP_NAME
|
||||
export DOCKER_BACKEND_LOG_HOME=$DOCKER_BACKEND_HOME/log
|
||||
export MONGODB_URI=mongodb://localhost:27017/
|
||||
export FREELEAPS_ENV=local
|
||||
export LOG_BASE_PATH=${CODEBASE_ROOT}/log
|
||||
|
||||
@ -11,6 +11,7 @@ from webapi.providers import metrics
|
||||
|
||||
# from webapi.providers import scheduler
|
||||
from webapi.providers import exception_handler
|
||||
from webapi.providers import permission_initialize
|
||||
from .freeleaps_app import FreeleapsApp
|
||||
from common.config.app_settings import app_settings
|
||||
|
||||
@ -23,6 +24,7 @@ def create_app() -> FastAPI:
|
||||
register(app, exception_handler)
|
||||
register(app, database)
|
||||
register(app, router)
|
||||
register(app, permission_initialize)
|
||||
# register(app, scheduler)
|
||||
register(app, common)
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from bson.errors import InvalidId
|
||||
from fastapi import FastAPI, HTTPException
|
||||
from fastapi.exceptions import RequestValidationError
|
||||
from starlette.requests import Request
|
||||
@ -26,6 +27,12 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
|
||||
content={"error": str(exc)},
|
||||
)
|
||||
|
||||
async def validation_error_exception_handler(request: Request, exc: InvalidId):
|
||||
return JSONResponse(
|
||||
status_code=HTTP_400_BAD_REQUEST,
|
||||
content={"error": str(exc)},
|
||||
)
|
||||
|
||||
async def exception_handler(request: Request, exc: Exception):
|
||||
return JSONResponse(
|
||||
status_code=HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
@ -36,4 +43,5 @@ async def exception_handler(request: Request, exc: Exception):
|
||||
def register(app: FastAPI):
|
||||
app.add_exception_handler(HTTPException, custom_http_exception_handler)
|
||||
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||
app.add_exception_handler(InvalidId, exception_handler)
|
||||
app.add_exception_handler(Exception, exception_handler)
|
||||
|
||||
@ -0,0 +1,37 @@
|
||||
import logging
|
||||
|
||||
from backend.models.permission import PermissionDoc, RoleDoc
|
||||
from backend.models.permission.constants import DefaultPermissionEnum, DefaultRoleEnum
|
||||
|
||||
|
||||
def register(app):
|
||||
# Configure logging for pymongo
|
||||
logging.getLogger("init_admin_permission").setLevel(logging.INFO) # Suppress DEBUG logs
|
||||
|
||||
@app.on_event("startup")
|
||||
async def init_admin_permission():
|
||||
# Initialize permissions if not exist
|
||||
default_permission_ids = []
|
||||
for default_permission in [DefaultPermissionEnum.CHANGE_PERMISSIONS, DefaultPermissionEnum.CHANGE_ROLES]:
|
||||
if not await PermissionDoc.find_one(
|
||||
{str(PermissionDoc.permission_key): default_permission.value.permission_key}):
|
||||
doc = await PermissionDoc(
|
||||
permission_key=default_permission.value.permission_key,
|
||||
permission_name=default_permission.value.permission_name,
|
||||
description=default_permission.value.permission_description,
|
||||
).insert()
|
||||
default_permission_ids.append(str(doc.id))
|
||||
logging.info(f"default permissions initialized {default_permission_ids}")
|
||||
# Initialize roles if not exist
|
||||
default_role_ids = []
|
||||
for default_role in [DefaultRoleEnum.ADMIN]:
|
||||
if not await RoleDoc.find_one({str(RoleDoc.role_key): default_role.value.role_key}):
|
||||
doc = await RoleDoc(
|
||||
role_key=default_role.value.role_key,
|
||||
role_name=default_role.value.role_name,
|
||||
role_description=default_role.value.role_description,
|
||||
permission_ids=default_permission_ids,
|
||||
role_level=default_role.value.role_level,
|
||||
).insert()
|
||||
default_role_ids.append(str(doc.id))
|
||||
logging.info(f"default roles initialized {default_role_ids}")
|
||||
@ -2,9 +2,13 @@ from fastapi import APIRouter
|
||||
from .signin import router as signin_router
|
||||
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
|
||||
|
||||
api_router = APIRouter(prefix="/auth")
|
||||
api_router.include_router(signin_router, tags=["user"])
|
||||
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"])
|
||||
websocket_router = APIRouter()
|
||||
|
||||
11
apps/authentication/webapi/routes/permission/__init__.py
Normal file
11
apps/authentication/webapi/routes/permission/__init__.py
Normal file
@ -0,0 +1,11 @@
|
||||
from fastapi import APIRouter
|
||||
from .create_permission import router as cp_router
|
||||
from .query_permission import router as qp_router
|
||||
from .update_permission import router as up_router
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(cp_router, prefix="/permission", tags=["permission"])
|
||||
router.include_router(qp_router, prefix="/permission", tags=["permission"])
|
||||
router.include_router(up_router, prefix="/permission", tags=["permission"])
|
||||
@ -0,0 +1,36 @@
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from backend.services.permission.permission_service import PermissionService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
permission_service = PermissionService()
|
||||
|
||||
class CreatePermissionRequest(BaseModel):
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
class PermissionResponse(BaseModel):
|
||||
id: str
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
description: Optional[str] = None
|
||||
created_at: str
|
||||
updated_at: str
|
||||
|
||||
@router.post(
|
||||
"/create",
|
||||
response_model=PermissionResponse,
|
||||
operation_id="create-permission",
|
||||
summary="Create Permission",
|
||||
description="Create a new permission."
|
||||
)
|
||||
async def create_permission(
|
||||
req: CreatePermissionRequest,
|
||||
) -> PermissionResponse:
|
||||
doc = await permission_service.create_permission(req.permission_key, req.permission_name, req.description)
|
||||
|
||||
return PermissionResponse(**doc.dict())
|
||||
@ -0,0 +1,50 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from backend.services.permission.permission_service import PermissionService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
permission_service = PermissionService()
|
||||
|
||||
class QueryPermissionRequest(BaseModel):
|
||||
permission_key: Optional[str] = None
|
||||
permission_name: Optional[str] = None
|
||||
page: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
class PermissionResponse(BaseModel):
|
||||
id: str
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
description: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class QueryPermissionResponse(BaseModel):
|
||||
items: List[PermissionResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
@router.post(
|
||||
"/query",
|
||||
response_model=QueryPermissionResponse,
|
||||
operation_id="query-permission",
|
||||
summary="Query Permissions (paginated)",
|
||||
description="Query permissions with pagination and fuzzy search. Only Admin role allowed."
|
||||
)
|
||||
async def query_permissions(
|
||||
req: QueryPermissionRequest,
|
||||
) -> QueryPermissionResponse:
|
||||
result = await permission_service.query_permissions(req.permission_key, req.permission_name, req.page, req.page_size)
|
||||
items = [PermissionResponse(**item) for item in result["items"]]
|
||||
return QueryPermissionResponse(
|
||||
items=items,
|
||||
total=result["total"],
|
||||
page=result["page"],
|
||||
page_size=result["page_size"]
|
||||
)
|
||||
@ -0,0 +1,38 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
from backend.services.permission.permission_service import PermissionService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
permission_service = PermissionService()
|
||||
|
||||
class UpdatePermissionRequest(BaseModel):
|
||||
permission_id: str
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
description: Optional[str] = None
|
||||
|
||||
class PermissionResponse(BaseModel):
|
||||
id: str
|
||||
permission_key: str
|
||||
permission_name: str
|
||||
description: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@router.post(
|
||||
"/update",
|
||||
response_model=PermissionResponse,
|
||||
operation_id="update-permission",
|
||||
summary="Update Permission",
|
||||
description="Update an existing permission by id. Only Admin role allowed."
|
||||
)
|
||||
async def update_permission(
|
||||
req: UpdatePermissionRequest,
|
||||
) -> PermissionResponse:
|
||||
doc = await permission_service.update_permission(req.permission_id, req.permission_key, req.permission_name, req.description)
|
||||
return PermissionResponse(**doc.dict())
|
||||
10
apps/authentication/webapi/routes/role/__init__.py
Normal file
10
apps/authentication/webapi/routes/role/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
from .create_role import router as create_role_router
|
||||
from .update_role import router as update_role_router
|
||||
from .query_role import router as query_role_router
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router.include_router(create_role_router, prefix="/role", tags=["role"])
|
||||
router.include_router(update_role_router, prefix="/role", tags=["role"])
|
||||
router.include_router(query_role_router, prefix="/role", tags=["role"])
|
||||
40
apps/authentication/webapi/routes/role/create_role.py
Normal file
40
apps/authentication/webapi/routes/role/create_role.py
Normal file
@ -0,0 +1,40 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from backend.services.permission.role_service import RoleService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
role_service = RoleService()
|
||||
|
||||
class CreateRoleRequest(BaseModel):
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
role_level: int
|
||||
|
||||
class RoleResponse(BaseModel):
|
||||
id: str
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
permission_ids: List[str]
|
||||
role_level: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@router.post(
|
||||
"/create",
|
||||
response_model=RoleResponse,
|
||||
operation_id="create-role",
|
||||
summary="Create Role",
|
||||
description="Create a new role."
|
||||
)
|
||||
async def create_role(
|
||||
req: CreateRoleRequest,
|
||||
) -> RoleResponse:
|
||||
doc = await role_service.create_role(req.role_key, req.role_name, req.role_description, req.role_level)
|
||||
return RoleResponse(**doc.dict())
|
||||
52
apps/authentication/webapi/routes/role/query_role.py
Normal file
52
apps/authentication/webapi/routes/role/query_role.py
Normal file
@ -0,0 +1,52 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from backend.services.permission.role_service import RoleService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
role_service = RoleService()
|
||||
|
||||
class QueryRoleRequest(BaseModel):
|
||||
role_key: Optional[str] = None
|
||||
role_name: Optional[str] = None
|
||||
page: int = 1
|
||||
page_size: int = 10
|
||||
|
||||
class RoleResponse(BaseModel):
|
||||
id: str
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
permission_ids: List[str]
|
||||
role_level: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
class QueryRoleResponse(BaseModel):
|
||||
items: List[RoleResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
|
||||
@router.post(
|
||||
"/query",
|
||||
response_model=QueryRoleResponse,
|
||||
operation_id="query-role",
|
||||
summary="Query Roles (paginated)",
|
||||
description="Query roles with pagination and fuzzy search. Only Admin role allowed."
|
||||
)
|
||||
async def query_roles(
|
||||
req: QueryRoleRequest,
|
||||
) -> QueryRoleResponse:
|
||||
result = await role_service.query_roles(req.role_key, req.role_name, req.page, req.page_size)
|
||||
items = [RoleResponse(**item) for item in result["items"]]
|
||||
return QueryRoleResponse(
|
||||
items=items,
|
||||
total=result["total"],
|
||||
page=result["page"],
|
||||
page_size=result["page_size"]
|
||||
)
|
||||
41
apps/authentication/webapi/routes/role/update_role.py
Normal file
41
apps/authentication/webapi/routes/role/update_role.py
Normal file
@ -0,0 +1,41 @@
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import APIRouter
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional, List
|
||||
from backend.services.permission.role_service import RoleService
|
||||
from common.token.token_manager import TokenManager
|
||||
|
||||
router = APIRouter()
|
||||
token_manager = TokenManager()
|
||||
role_service = RoleService()
|
||||
|
||||
class UpdateRoleRequest(BaseModel):
|
||||
role_id: str
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
role_level: int
|
||||
|
||||
class RoleResponse(BaseModel):
|
||||
id: str
|
||||
role_key: str
|
||||
role_name: str
|
||||
role_description: Optional[str] = None
|
||||
permission_ids: List[str]
|
||||
role_level: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
@router.post(
|
||||
"/update",
|
||||
response_model=RoleResponse,
|
||||
operation_id="update-role",
|
||||
summary="Update Role",
|
||||
description="Update an existing role by id. Only Admin role allowed."
|
||||
)
|
||||
async def update_role(
|
||||
req: UpdateRoleRequest,
|
||||
) -> RoleResponse:
|
||||
doc = await role_service.update_role(req.role_id, req.role_key, req.role_name, req.role_description, req.role_level)
|
||||
return RoleResponse(**doc.dict())
|
||||
0
apps/authentication/webapi/routes/user/__init__.py
Normal file
0
apps/authentication/webapi/routes/user/__init__.py
Normal file
1
apps/notification/.env.local
Normal file
1
apps/notification/.env.local
Normal file
@ -0,0 +1 @@
|
||||
export RABBITMQ_HOST=localhost
|
||||
Loading…
Reference in New Issue
Block a user