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 import user_models
|
||||||
from .user_profile import profile_models
|
from .user_profile import profile_models
|
||||||
|
from .permission import permission_models
|
||||||
|
|
||||||
backend_models = []
|
backend_models = []
|
||||||
backend_models.extend(user_models)
|
backend_models.extend(user_models)
|
||||||
backend_models.extend(profile_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):
|
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
|
from beanie import Document
|
||||||
|
|
||||||
@ -18,6 +18,7 @@ class UserAccountDoc(Document):
|
|||||||
service_plan_id: Optional[str]
|
service_plan_id: Optional[str]
|
||||||
properties: UserAccountProperty
|
properties: UserAccountProperty
|
||||||
capabilities: Capability
|
capabilities: Capability
|
||||||
|
user_role_ids: List[str]
|
||||||
user_role: int = AdministrativeRole.NONE
|
user_role: int = AdministrativeRole.NONE
|
||||||
preferred_region: UserRegion = UserRegion.ZH_CN
|
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 scheduler
|
||||||
from webapi.providers import exception_handler
|
from webapi.providers import exception_handler
|
||||||
|
from webapi.providers import permission_initialize
|
||||||
from .freeleaps_app import FreeleapsApp
|
from .freeleaps_app import FreeleapsApp
|
||||||
from common.config.app_settings import app_settings
|
from common.config.app_settings import app_settings
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ def create_app() -> FastAPI:
|
|||||||
register(app, exception_handler)
|
register(app, exception_handler)
|
||||||
register(app, database)
|
register(app, database)
|
||||||
register(app, router)
|
register(app, router)
|
||||||
|
register(app, permission_initialize)
|
||||||
# register(app, scheduler)
|
# register(app, scheduler)
|
||||||
register(app, common)
|
register(app, common)
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from bson.errors import InvalidId
|
||||||
from fastapi import FastAPI, HTTPException
|
from fastapi import FastAPI, HTTPException
|
||||||
from fastapi.exceptions import RequestValidationError
|
from fastapi.exceptions import RequestValidationError
|
||||||
from starlette.requests import Request
|
from starlette.requests import Request
|
||||||
@ -26,6 +27,12 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
|
|||||||
content={"error": str(exc)},
|
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):
|
async def exception_handler(request: Request, exc: Exception):
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
status_code=HTTP_500_INTERNAL_SERVER_ERROR,
|
status_code=HTTP_500_INTERNAL_SERVER_ERROR,
|
||||||
@ -36,4 +43,5 @@ async def exception_handler(request: Request, exc: Exception):
|
|||||||
def register(app: FastAPI):
|
def register(app: FastAPI):
|
||||||
app.add_exception_handler(HTTPException, custom_http_exception_handler)
|
app.add_exception_handler(HTTPException, custom_http_exception_handler)
|
||||||
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
app.add_exception_handler(RequestValidationError, validation_exception_handler)
|
||||||
|
app.add_exception_handler(InvalidId, exception_handler)
|
||||||
app.add_exception_handler(Exception, 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 .signin import router as signin_router
|
||||||
from .tokens import router as token_router
|
from .tokens import router as token_router
|
||||||
from .auth import router as auth_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 = APIRouter(prefix="/auth")
|
||||||
api_router.include_router(signin_router, tags=["user"])
|
api_router.include_router(signin_router, tags=["user"])
|
||||||
api_router.include_router(token_router, tags=["token"])
|
api_router.include_router(token_router, tags=["token"])
|
||||||
api_router.include_router(auth_router, tags=["auth"])
|
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()
|
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