126 lines
5.6 KiB
Python
126 lines
5.6 KiB
Python
from typing import Optional, List, Tuple
|
|
|
|
from fastapi.exceptions import RequestValidationError
|
|
|
|
from backend.models.permission.models import PermissionDoc, RoleDoc
|
|
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, custom_permission_id: 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.")
|
|
if custom_permission_id:
|
|
try:
|
|
custom_id = PydanticObjectId(custom_permission_id)
|
|
if await PermissionDoc.get(custom_id):
|
|
raise RequestValidationError("Permission with the provided custom_permission_id already exists.")
|
|
except Exception:
|
|
raise RequestValidationError("Invalid custom_permission_id format. Must be a valid ObjectId.")
|
|
|
|
doc = PermissionDoc(
|
|
permission_key=permission_key,
|
|
permission_name=permission_name,
|
|
description=description,
|
|
created_at=datetime.now(),
|
|
updated_at=datetime.now()
|
|
)
|
|
|
|
if custom_permission_id:
|
|
doc.id = PydanticObjectId(custom_permission_id)
|
|
|
|
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, custom_permission_id: 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.")
|
|
#if doc.is_default:
|
|
# raise RequestValidationError("Default permission cannot be updated.")
|
|
# Check for uniqueness (exclude self)
|
|
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.permission_name = permission_name
|
|
doc.description = description
|
|
doc.updated_at = datetime.now()
|
|
|
|
if custom_permission_id:
|
|
# Store the old ID for cleanup
|
|
old_id = doc.id
|
|
doc.id = PydanticObjectId(custom_permission_id)
|
|
await doc.save()
|
|
|
|
# Delete the old document with the original ID
|
|
try:
|
|
old_doc = await PermissionDoc.get(old_id)
|
|
if (str(old_id) != custom_permission_id) and old_doc:
|
|
await old_doc.delete()
|
|
except Exception as e:
|
|
# Log the error but don't fail the operation
|
|
print(f"Warning: Failed to delete old permission document {old_id}: {e}")
|
|
else:
|
|
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
|
|
|
|
async def delete_permission(self, permission_id: PydanticObjectId) -> None:
|
|
"""Delete a permission document after checking if it is referenced by any role and is not default"""
|
|
if not permission_id:
|
|
raise RequestValidationError("permission_id is required.")
|
|
# Check if any role references this permission
|
|
role = await RoleDoc.find_one({"permission_ids": str(permission_id)})
|
|
if role:
|
|
raise RequestValidationError("Permission is referenced by a role and cannot be deleted.")
|
|
doc = await PermissionDoc.get(permission_id)
|
|
if not doc:
|
|
raise RequestValidationError("Permission not found.")
|
|
# Check if the permission is default
|
|
#if doc.is_default:
|
|
# raise RequestValidationError("Default permission cannot be deleted.")
|
|
await doc.delete()
|