diff --git a/app/routes/entity_permissions.py b/app/routes/entity_permissions.py index 38ecfeb..44a6d23 100644 --- a/app/routes/entity_permissions.py +++ b/app/routes/entity_permissions.py @@ -9,6 +9,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy.ext.asyncio import AsyncSession from app.core.db import get_db +from app.core.rate_limit import check_rate_limit from app.deps import get_current_user, get_redis_dep, require_permission from app.schemas.entity_permission import ( EntityPermissionCreate, @@ -18,6 +19,10 @@ from app.services import entity_permission_service router = APIRouter(prefix="/api/v1/permissions", tags=["entity-permissions"]) +# Rate limits for permission changes (prevent abuse/DoS) +_PERM_RATE_LIMIT_MAX = 50 # max changes per minute +_PERM_RATE_LIMIT_WINDOW = 60 # 60 seconds + @router.get("/{entity_type}/{entity_id}") async def list_entity_permissions( @@ -48,6 +53,12 @@ async def create_entity_permission( """Grant or update a permission on any entity for a user, group, role, or guest.""" tenant_id = uuid.UUID(current_user["tenant_id"]) user_id = uuid.UUID(current_user["user_id"]) + # Rate limit: max 50 permission changes per minute per user + await check_rate_limit( + f"perm_change:{user_id}", + _PERM_RATE_LIMIT_MAX, + _PERM_RATE_LIMIT_WINDOW, + ) try: return await entity_permission_service.create_permission( db, @@ -75,6 +86,12 @@ async def update_entity_permission( ): """Update an existing permission entry.""" tenant_id = uuid.UUID(current_user["tenant_id"]) + user_id = uuid.UUID(current_user["user_id"]) + await check_rate_limit( + f"perm_change:{user_id}", + _PERM_RATE_LIMIT_MAX, + _PERM_RATE_LIMIT_WINDOW, + ) try: return await entity_permission_service.update_permission( db, @@ -97,6 +114,12 @@ async def delete_entity_permission( ): """Revoke a permission entry.""" tenant_id = uuid.UUID(current_user["tenant_id"]) + user_id = uuid.UUID(current_user["user_id"]) + await check_rate_limit( + f"perm_change:{user_id}", + _PERM_RATE_LIMIT_MAX, + _PERM_RATE_LIMIT_WINDOW, + ) try: await entity_permission_service.delete_permission(db, tenant_id, permission_id) except ValueError as e: