From 2cf45c72b6dc365a608f33acca4cfdf6c2528d9f Mon Sep 17 00:00:00 2001 From: Davidm4r Date: Thu, 26 Mar 2026 09:41:16 +0100 Subject: [PATCH] fix(api): remove MANAGE_ACCOUNT permission requirement for listing or create a tenant (#10468) --- api/CHANGELOG.md | 1 + api/src/backend/api/tests/test_views.py | 57 +++++++++++++++++++++++++ api/src/backend/api/v1/views.py | 11 +++++ 3 files changed, 69 insertions(+) diff --git a/api/CHANGELOG.md b/api/CHANGELOG.md index 3c18cba56e..1385a9c0f5 100644 --- a/api/CHANGELOG.md +++ b/api/CHANGELOG.md @@ -14,6 +14,7 @@ All notable changes to the **Prowler API** are documented in this file. - Populate compliance data inside `check_metadata` for findings, which was always returned as `null` [(#10449)](https://github.com/prowler-cloud/prowler/pull/10449) - 403 error for admin users listing tenants due to roles query not using the admin database connection [(#10460)](https://github.com/prowler-cloud/prowler/pull/10460) - Filter transient Neo4j defunct connection logs in Sentry `before_send` to suppress false-positive alerts handled by `RetryableSession` retries [(#10452)](https://github.com/prowler-cloud/prowler/pull/10452) +- `MANAGE_ACCOUNT` permission no longer required for listing and creating tenants [(#10468)](https://github.com/prowler-cloud/prowler/pull/10468) ## [1.23.0] (Prowler v5.22.0) diff --git a/api/src/backend/api/tests/test_views.py b/api/src/backend/api/tests/test_views.py index 9b687eeb99..e50a0c84bb 100644 --- a/api/src/backend/api/tests/test_views.py +++ b/api/src/backend/api/tests/test_views.py @@ -807,6 +807,63 @@ class TestTenantViewSet: ) assert response.status_code == status.HTTP_404_NOT_FOUND + def test_tenants_list_no_permissions( + self, authenticated_client_no_permissions_rbac, tenants_fixture + ): + response = authenticated_client_no_permissions_rbac.get(reverse("tenant-list")) + assert response.status_code == status.HTTP_200_OK + + def test_tenants_retrieve_no_permissions( + self, authenticated_client_no_permissions_rbac, tenants_fixture + ): + tenant1, *_ = tenants_fixture + response = authenticated_client_no_permissions_rbac.get( + reverse("tenant-detail", kwargs={"pk": tenant1.id}) + ) + assert response.status_code == status.HTTP_200_OK + + def test_tenants_create_no_permissions( + self, authenticated_client_no_permissions_rbac, valid_tenant_payload + ): + response = authenticated_client_no_permissions_rbac.post( + reverse("tenant-list"), + data=valid_tenant_payload, + format="json", + ) + assert response.status_code == status.HTTP_201_CREATED + + def test_tenants_partial_update_no_permissions( + self, authenticated_client_no_permissions_rbac, tenants_fixture + ): + tenant1, *_ = tenants_fixture + payload = { + "data": { + "type": "tenants", + "id": str(tenant1.id), + "attributes": {"name": "Unauthorized update"}, + }, + } + response = authenticated_client_no_permissions_rbac.patch( + reverse("tenant-detail", kwargs={"pk": tenant1.id}), + data=payload, + content_type=API_JSON_CONTENT_TYPE, + ) + assert response.status_code == status.HTTP_403_FORBIDDEN + + @patch("api.v1.views.delete_tenant_task.apply_async") + def test_tenants_delete_no_permissions( + self, + delete_tenant_mock, + authenticated_client_no_permissions_rbac, + tenants_fixture, + ): + tenant1, *_ = tenants_fixture + response = authenticated_client_no_permissions_rbac.delete( + reverse("tenant-detail", kwargs={"pk": tenant1.id}) + ) + assert response.status_code == status.HTTP_403_FORBIDDEN + delete_tenant_mock.assert_not_called() + @pytest.mark.django_db class TestMembershipViewSet: diff --git a/api/src/backend/api/v1/views.py b/api/src/backend/api/v1/views.py index 5803a2538d..5d2e574630 100644 --- a/api/src/backend/api/v1/views.py +++ b/api/src/backend/api/v1/views.py @@ -1210,6 +1210,17 @@ class TenantViewSet(BaseTenantViewset): # RBAC required permissions required_permissions = [Permissions.MANAGE_ACCOUNT] + def set_required_permissions(self): + """ + Returns the required permissions based on the request method. + """ + if self.action in ("list", "retrieve", "create"): + # No permissions required for listing, retrieving or creating tenants + self.required_permissions = [] + else: + # Require MANAGE_ACCOUNT for update and delete + self.required_permissions = [Permissions.MANAGE_ACCOUNT] + def get_queryset(self): queryset = Tenant.objects.filter(membership__user=self.request.user) return queryset.prefetch_related("memberships")