From 6fcbb8e57ba11f072f59c1668b75715309fa5036 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Tue, 26 May 2026 12:54:48 +0800 Subject: [PATCH] feat: prevent deletion of resource pools used by ACE configurations Add safety check to prevent deletion of resource pools that are being used by ACE configurations. If an attempt is made to delete a resource pool that has ACE rules referencing it, the API returns a 400 error with detailed information showing which users/groups are using the pool and their roles. The error message only shows the resource pool name for a clean, user-friendly experience without exposing internal path details. --- gns3server/api/routes/controller/pools.py | 31 ++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/gns3server/api/routes/controller/pools.py b/gns3server/api/routes/controller/pools.py index 6c8e865fa..4c4f53c21 100644 --- a/gns3server/api/routes/controller/pools.py +++ b/gns3server/api/routes/controller/pools.py @@ -147,10 +147,39 @@ async def delete_resource_pool( if not resource_pool: raise ControllerNotFoundError(f"Resource pool '{resource_pool_id}' not found") + # Check if there are any ACE configurations using this resource pool path + pool_path = f"/pools/{resource_pool_id}" + aces = await rbac_repo.get_aces() + using_aces = [ace for ace in aces if ace.path == pool_path or ace.path.startswith(pool_path + "/")] + + if using_aces: + # Build detailed error message with ACE information + ace_details = [] + for ace in using_aces: + identifier = "" + if ace.ace_type == "user" and ace.user: + identifier = f"User '{ace.user.username}'" + elif ace.ace_type == "group" and ace.group: + identifier = f"Group '{ace.group.name}'" + else: + identifier = f"{ace.ace_type.capitalize()} '{ace.user_id or ace.group_id}'" + + if ace.role: + identifier += f" with role '{ace.role.name}'" + + ace_details.append(f"- {identifier}") + + error_message = ( + f"Resource pool '{resource_pool.name}' cannot be deleted because it is being used by {len(using_aces)} ACE configuration(s):\n" + + "\n".join(ace_details) + + f"\n\nPlease delete the ACE configuration(s) for resource pool '{resource_pool.name}' first." + ) + raise ControllerBadRequestError(error_message) + success = await pools_repo.delete_resource_pool(resource_pool_id) if not success: raise ControllerError(f"Resource pool '{resource_pool_id}' could not be deleted") - await rbac_repo.delete_all_ace_starting_with_path(f"/pools/{resource_pool_id}") + await rbac_repo.delete_all_ace_starting_with_path(pool_path) @router.get(